The dependency inversion principle always sounds more complicated than it is. It says:
High-level classes shouldn’t have to change just because low-level classes change.
High-level classes typically drive your system. You want them to remain as stable as possible.
Why Should You Use Dependency Inversion?
Speaking of driving, let’s take cars as an example. The high-level classes are the items you interact with most: steering wheel, accelerator, and brake pedal. They tell the low-level implementation classes (tires, engine, brakes) what to do.
Let’s see what happens to the high-level classes if you change the engine from gas to electric. Nothing! A driver still steers, accelerates, and brakes using the same functionality. If you had violated this principle, the switch to electric (a low-level class) would force a change to the driver's interface (a high-level class). That sounds obvious with a car, but it’s easy to mess it up in code.
This ensures that you put the high-level classes in charge. They define the interface through which they communicate. In the car, you make the engine go faster by stepping on the accelerator pedal. It’s up to the engine to conform to this standard.
How Do We Apply Dependency Inversion to Our Code?
In our game, the view needs to be driven by the controller. So the controller defines the interface. Any view must conform to it. Otherwise, every time you change the interface, the controller must be modified to conform to the view (that’s backward).
But in the last chapter, we did have to modify the controller substantially to upgrade the view to support broadcasting! So it looks like we’re not yet conforming to the dependency inversion principle.
The cause of this problem was letting the controller know too much about the structure of the view. It doesn’t affect the game’s flow if we have various extra broadcasts happening, so GameController shouldn’t have any code that depends on specific details of the view.
Try it Yourself!
As a final challenge, let’s revert our changes to GameController.run, and find a way to broadcast our game on any number of view components without ever needing to change the code in our controller. Make sure you are following all five of the SOLID design principles and the MVC design pattern in your solution! Use this file: card-game-p3-ch6-d1.py.
Congratulations, you’ve got a working card game that will be easy to maintain whenever you need to add new features! You can change almost any aspect of the game without worrying too much about how it might affect other code parts. 😍
And that’s the dream with writing code in any language: to be confident that your code is SOLID enough that a little change in one place won’t cause a nightmare for anyone else.
Dependency inversion says high-level concepts should communicate through high-level abstractions. In other words: high-level classes shouldn’t have to change because low-level classes change.
Be aware of knowing too much about other classes’ implementation.
In the next chapter, we will look at several bad ideas or easy traps when coding and how to avoid them.