Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dependency inversion principle and composition

I am reading about SOLID principles and I stopped here on "Dependency inversion principle" which means the objects should passed already instantiated to anther object, which means composition cannot be applied with Dependency inversion principle am right? or there is something I miss? UPDATE ************************************************** suppose you have a class and this class has an attribute which is reference to anther object, we have 2 solution(for me):

  1. Create the object outside the class and pass it to the class.(Dependency)
  2. Create the object inside the class it self(composition).

thank you.

like image 926
justsomedev Avatar asked Dec 05 '22 15:12

justsomedev


2 Answers

Your confusion comes from your understanding of composition. The object that is owned by another is dependent of the lifetime of the owning object. That doesn't mean that you have to create the owned object inside the owning class.

If you create objects in a class, this class is tightly coupled to the created class. You can't exchange the implementation without changing the class which creates the other.

Example: enter image description here

In the picture above you have the class Client, which uses the class Server. Lets say this is a composition and the Client has an attribute of the type Server.

If you create an instance of the class server inside the client class, it could look like this:

public class Client {
    private Server server;

    public Client(){
        this.server = new Server();
    }
}

Now lets say you want to exchange the implementation of the Server. You need to change the implementation of the Client class, because the only way to exchange it, is to create an instance of another class (maybe called AnotherServer).

public class Client {
    private AnotherServer anotherServer;

    public Client(){
        this.anotherServer = new AnotherServer();
    }
}

This shows you, that the Client class is highly dependent of the class Server.

To easily change the used implementation of the Server and thus modify the Client's behaviour it would be better to compose the Client out of abstractions (abstract classes or interfaces). Doing so means you can't create the needed object in the owning class, because you can only create concrete classes. Creating classes means calling the constructor and being dependent of that class which was created.

A better way to achieve composition (– the Client is composed out of a Server –) is by injecting it through a setter method or the constructor. Like this you can hide implementation classes behind an interface.

Example: enter image description here

In the second picture we protect the Client from the knowledge about the Server's concrete implementation. It only depends on the server interface. This dependency is not that dramatic, because the Client defines the interface. He decides about needed function of the Server interface. To indicate, that the interface for the servers belongs to the Client it is called "ClientServer".

To compose your Client you have to create the concrete classes for the ClientServer interface outside the class and inject it through the constructor or a setter method.

...
FirstServer first = new FirstServer();
Client client = new Client(first);

client.setServer(new SecondServer());
...

Like this you can easily exchange the used Server implementation in the Client, even at runtime.

This mechanism is called the dependency inversion principle (DIP). But Why? The Client class is still dependent of the server interface. If the interface changes, the Client has to change too. Yes, this is correct. But the Client decides which functions he needs in that interface. So normally the interface changes, when the Client says that it needs to be changed. The interface changes because the Client changes.

Because the concrete servers "FirstServer" and "SecondServer" implement the interface ClientServer they are dependent of that interface too. And because inheritance is a stronger dependency than the composition, the concrete server classes are more dependent of the interface than the Client class.

That's why the dependencies are inverted. The concrete server classes now depend on the "Client-ClientServer"-conglomerate.

So the answer to your question is: You can't reach DIP when you create your class inside another class. But you can reach DIP with composition by defining an interface an injecting the concrete classes which inherit this interface.

like image 172
Janis Avatar answered Dec 29 '22 01:12

Janis


Taken from Wikipedia:

A. High-level modules should not depend on low-level modules. Both should depend on abstractions.

B. Abstractions should not depend on details. Details should depend on abstractions.

And you said:

which means the objects should passed already instantiated to another object

Dependency Inversion principle has nothing against a programming implementation detail like class constructors, which are meant to initialize the object being constructed.

Unless you define constructor parameters typed as implementations rather than abstractions, and/or the dependencies being injected are of a higher layer than the target dependency, you're not violating the whole principle.

like image 37
Matías Fidemraizer Avatar answered Dec 29 '22 01:12

Matías Fidemraizer