Photo by Roman Kraft on Unsplash

SOLID — The Dependency Inversion Principle

Elle Hallal
Apr 30 · 3 min read

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:

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

In the new_game method, new instances of the Board and 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 classesBoard and 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 Board or 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 Board and Game were to be used to create a new game, (for example, BestBoard and FunGame) the 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 GameFactory's constructor:

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 @board and @game within GameFactory.

If the names of the Board and Game classes were to change, initialising GameFactory with the renamed classes would not affect GameFactory.

If subclasses of Board and Game (
for example, BestBoard and FunGame) were used to initialise an instance of GameFactory, this would not affect how new_game functions.


Elle Hallal

Written by

Apprentice at 8th Light. Github: https://github.com/itsellej