DEV Community

Cover image for What Is The Dependency Inversion Principle?
Elle Hallal 👩🏽‍💻
Elle Hallal 👩🏽‍💻

Posted on • Originally published at ellehallal.dev on

What Is The Dependency Inversion Principle?

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.

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
Enter fullscreen mode Exit fullscreen mode

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 classes Board 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
Enter fullscreen mode Exit fullscreen mode

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.

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.


Resources

Top comments (1)

Collapse
 
pomfrit123 profile image
*** • Edited

I think the code could do with better formatting :)