Dependency Inversion Principle Explained

Excuse me for cross-posting on Software Engineering, didn't know this was underestimated.

The answer I got was exactly what I was looking for, for those interested: https://softwareengineering.stackexchange.com/a/347143/269571


Original question

I am reading Agile Software Development, Principles, Patterns and Practices by Robert K. Martin.

When he talks about the Dependency Inversion Principle, he gives the following example of a DIP violation:

DIP violation

This seems very clear to me since the higher level Button

object depends on the lower level object Lamp

.

The solution it comes with is:

Decision

It creates the interface so that the path is Button

no longer dependent on the object Lamp

.

The theory seems really clear to me, however, I can't wrap my head around using the principle in real projects.

  • Who is going to determine which classes (which implement SwitchableDevice

    ) should be called?

  • Who tells Button

    what devices he needs to turn on / off?

  • How do you specify an object that uses something abstract, concrete whatever it needs to use? (Please correct me if this question is completely wrong)

If something is unclear about my question, please let me know, I will be happy to clarify everything for you.

+3


source to share


2 answers


The whole point of Dependency Injection (at least as I understood it) is that you Button

don't need to know which specific one SwitchableDevice

it switches.

An abstract interface might look like this:

struct SwitchableDevice {
    virtual void switchOn() = 0;
    virtual void switchOff() = 0;
};

      

And the button can be implemented like this:

struct Button {
    SwitchableDevice& dev;
    bool state = false;
    Button(SwitchableDevice& d) : dev(d) {}
    void buttonPress(){
        if (state) { dev.switchOff(); }
        else       { dev.switchOn();  }
        state = !state;
    }
};

      

For the button, that's all! Nobody has to tell the button what the specific implementation is SwitchableDevice

, in other words: implementation Button

and SwitchableDevice

decoupled.

A possible implementation Lamp

might look like this:



struct Lamp : SwitchableDevice {
    void switchOn(){std::cout << "shine bright" << std::endl;}
    void switchOff(){std::cout << "i am not afraid of the dark" << std::endl;}
};

      

And it can be used like this:

int main(){
    Lamp lamp;
    Button button(lamp);
    button.buttonPress();
    button.buttonPress();
}

      

Hope it helps ...

The favor is that now we can individually change the implementation Button

and Lamp

without changing anything on the other part. For example, it ButtonForManyDevices

might look like this:

struct ButtonForManyDevices {
    std::vector<SwitchableDevice*> devs;
    bool state = false;
    Button(std::vector<SwitchableDevice*> d) : devs(d) {}
    void buttonPress(){
        if (state) for (auto d: devs) { d.switchOff(); }
        else       for (auto d: devs) { d.switchOn();  }
        state = !state;
    }
};

      

Likewise, you can completely change the behavior Lamp

(within limits SwitchableDevice

, of course, without changing anything on the button. The same ButtonForManyDevices

can even be used to toggle Lamp

, a VaccumCleaner

and a MicroWaveOven

.

+3


source


He says that control over buttons should be more generalized than just a lamp. If you have button classes for every type of thing a button can control, then you can complete many button classes.

The first example describes a button on a lamp. Basically, it takes a lamp as a starting point and divides it into components.

In the second example, he divides the parts and looks at the button as a whole.

Who will determine which classes (which implement SwitchableDevice) should be called?

There must be a link between the button and the interface.



Who will tell the button which devices it needs to turn on / off?

The Button class will need to implement a mechanism to tell which device it is connected to.

How do you tell an object that is using something abstract what specific things should it use? (Please correct me if this question is completely wrong).

Because an object derived from an abstract interface must fully implement the interface. The Lamp object must define a TurnOn and TurnOff method somewhere ..

0


source







All Articles