Dependency Injection

Steven D'Aprano steve+comp.lang.python at pearwood.info
Sat Apr 11 06:33:36 EDT 2015


On Sat, 11 Apr 2015 04:50 pm, Palpandi wrote:

> Hi all,
> 
> Can anyone explain about the dependency injection concept in python?
> 
> I have a class A. A is used in all other classes(B, C, D, ..). Is it good
> to use dependency injection concept in this situation or else any other
> suggestions?

The best description of Dependency Injection is this one from James Shore:

    "Dependency Injection" is a 25-dollar term for a 5-cent 
    concept. ... Dependency injection means giving an object 
    its instance variables.

http://www.jamesshore.com/Blog/Dependency-Injection-Demystified.html


Suppose we have a class like this:

class Car:
    def __init__(self):
        self.engine = Engine('4L', 'automatic')
    def accelerate(self):
        ...
    def brake(self):
        ...


Our Car class depends on the Engine class. Now, suppose we want a car with
an electric engine. We could subclass:

class ElectricCar(Car):
    def __init__(self):
        self.engine = ElectricMotor()


and for some problems that's fine. But *dependency injection* is another
solution. We change the Car class to take the engine as an argument:


# With dependency injection.
class Car:
    def __init__(self, engine):
        self.engine = engine


Now preparing the engine is the responsibility of the caller, not the Car
class. But that gives us lots more flexibility that we otherwise didn't
have:

grandmothers_car = Car(Engine('6L', 'manual')
dads_car = Car(ElectricMotor())
my_car = Car(LawnmowerEngine())
funnycar = Car(NitroBurningSupercharger())


This can make (for example) testing easier and less fragile:

# Without dependency injection, we have to monkey-patch on the fly.
save_engine = Engine  # Save.
Engine = MockEngine  # Replace.
test = Car()
Engine = save_engine  # Restore.


# With dependency injection, things are easy.
test = Car(MockEngine())



but the cost is that the caller now is responsible for the Car's internals.
We can have the best of both worlds by using a default value:

class Car:
    def __init__(self, engine=None):
        if engine is None:
            engine = Engine('4L', 'automatic')
        self.engine = engine

# I don't care what sort of engine I get
car = Car()

# Now I want to test.
test = Car(MockEngine())


Another related strategy is to pass the engine type, not the engine itself:

class Car:
    def __init__(self, engine_type):
        self.engine = engine_type('4L', 'automatic')



-- 
Steven




More information about the Python-list mailing list