Originally posted at ellehallal.dev👩🏽💻
This is a quick blog on my understanding of the dependency inversion principle. There are still elements I am unsure about, so please feel free to leave feedback.
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.
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:
class GameFactory def initialize(player_factory) @player_factory = player_factory end def new_game(squares) board = Board.new(squares) player1 = @player_factory.create_player('x', 1) player2 = @player_factory.create_player('o', 2) Game.new(board, player1, player2) end end
new_game method, new instances of the
Game classes are created within it. However, this violates the Dependency Inversion Principle.
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
class GameFactory def initialize(player_factory, board, game) @player_factory = player_factory @board = board @game = game end def new_game(squares) board = @board.new(squares) player1 = @player_factory.create_player('x', 1) player2 = @player_factory.create_player('o', 2) @game.new(board, player1, player2) end end
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.