I've been trying to understand dependency inversion in python. I understand the theory that everybody quotes but I've not yet seen the code example with and without inversion that would clearly demonstrate the benefits. I've found only one highly rated answer here that shows the code I pasted below but if you scroll down I also pasted same code without abstraction and it does the same thing and it is also modular. The only benefit I see is that using abstraction prevents someone making changes to the name of the method...
I am still struggling to understand the need for dependency inversion. Looking at the code below one uses dependency inversion and the other does not. They both seem to accomplish the same purpose and are equally modular... What's going on?
class IFood:
def bake(self): pass
def eat(self): pass
class Bread(IFood):
def bake(self):
print("Bread was baked")
def eat(self):
print("Bread was eaten")
class Pastry(IFood):
def bake(self):
print("Pastry was baked")
def eat(self):
print("Pastry was eaten")
class Production:
def __init__(self, food):
self.food = food
def produce(self):
self.food.bake()
def consume(self):
self.food.eat()
ProduceBread = Production(Bread())
ProducePastry = Production(Pastry())
ProducePastry.consume()
vs.
class Bread():
def bake(self):
print("Bread was baked")
def eat(self):
print("Bread was eaten")
class Pastry():
def bake(self):
print("Pastry was baked")
def eat(self):
print("Pastry was eaten")
class Production:
def __init__(self, food):
self.food = food
def produce(self):
self.food.bake()
def consume(self):
self.food.eat()
ProduceBread = Production(Bread())
ProducePastry = Production(Pastry())
ProducePastry.consume()
I've been playing with the code trying to spot an obvious benefit of dependency inversion in python but with no good results.
Both examples use dependency injection. The only difference is that
IFoodis a quasi-abstract base class the indicates its subclasses should definebakeandeat.The alternative would be a definition of
Productionsimilar toIf another food product were created, you would need to modify the definitions of
Production.produceandProduction.consumeinto ever longerifstatements. With dependency inversion, the only thing you need t do is define your newFoodsubclass;Productionitself doesn't need to change at all.A more idiomatic version of the
IFood-based solution would use theabcmodule. Type hints are added to more explicitly demonstrate the role ofIFoodinProduction.If you wanted to add a third type of food to produce, just define a new subclass of
Food.An example of dependency inversion that you use frequently in Python is the
strtype. You can pass values of many different types tostrand get back a string.stritself doesn't know how to do this, i.e., the definition ofstrdoes not look likeInstead, every value that you want to turn into a
strprovides its own function for doing so: instead of the result givenxdepending onstr, the behavior ofstrdepends onx: the dependency has been inverted.Now, if you want to define a class
Xand havestr(X())output something other than'<X object at 0x28342827>', you just defineX.__str__instead of having to modify your Python interpreter itself to change the built-in definition ofstr.