What Is The Dependency Inversion Principle?
This is a quick blog on my understanding of dependency inversion. There are still elements I am unsure about, so please feel free to leave feedback.
What is the Dependency Inversion Principle?
High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details. Details should depend on abstractions.
— Robert C. Martin
That’s great, but what does it mean? 🤷🏽♀️ I’ll demonstrate my understanding by using a class in my Tic Tac Toe application as an example.
Dependency Inversion in Tic Tac Toe
In the application, there’s a class called
GameFactory. The purpose of
GameFactory is to create an instance of the
Game class with the specified players and a board.
Here’s a condensed version of the class:
1class GameFactory2 def initialize(player_factory)3 @player_factory = player_factory4 end56 def new_game(squares)7 board = Board.new(squares)8 player1 = @player_factory.create_player('x', 1)9 player2 = @player_factory.create_player('o', 2)10 Game.new(board, player1, player2)11 end12end
new_game method, new instances of the
Game classes are created within it. However, this violates the Dependency Inversion Principle.
What’s wrong with it?
The high-level class
GameFactory is dependent on the low level classes
Game. As a result, they are tightly coupled. A change in a low-level class will affect the high-level class.
If the name of the
Game class was changed, the
new_game method within
GameFactory wouldn’t work. As a result, it would need to be amended to accommodate the renamed classes.
If sub classes of
Game were to be used to create a new game, (for example,
new_game method would need to be changed again to accommodate this.
A method to resolve the above issues is to pass the classes into
1class GameFactory2 def initialize(player_factory, board, game)3 @player_factory = player_factory4 @board = board5 @game = game6 end78 def new_game(squares)9 board = @board.new(squares)10 player1 = @player_factory.create_player('x', 1)11 player2 = @player_factory.create_player('o', 2)12 @game.new(board, player1, player2)13 end14end
Whatever is passed in as board and game during initialisation, becomes
If the names of the
Game classes were to change, initialising
GameFactory with the renamed classes would not affect
If subclasses of
Game (for example,
FunGame) were used to initialise an instance of
GameFactory, this would not affect how
In conclusion, my understanding is initialising a specific class, or classes within another results in tight coupling. Being able to inject the classes via the constructor, helps to make the classes loosely coupled.