fordeka
fordeka

Reputation: 979

How does dependency injection aid object composition?

So say I have class A, B, and C. Class A has a single responsibility but needs functionality from class B and C so at first I wanted to get A to inherit from B and C then realized by following the "composition over inheritance" principle and making B and C members of A I could reduce the rigidity of the design and make those two classes more reusable.

At first B and C only needed to be instantiated in the constructor of A but eventually they had methods that needed to be called in two or three other places- as I was reusing the classes elsewhere I was forgetting to call some of the methods in the right places creating a lot of unnecessary defects and time wasted... my question is does dependency injection help with this problem, does it help reduce the complexity of using composition, and if so how?

public class A
{
     private mB;
     private mC;
     public A(IB b, IC c)
     {
         mB = b;
         mC = c;
     }
     public MethodX()
     {
         mB.DoWhatever();
     }
     public MethodY()
     {
         mC.DoSomething();
     }
}

From what I understand DI would let me configure what concrete classes for IB and IC get put through the constructor like this and handle its creation- but how else would it help (complexity wise)?

I decided to ask this question based on not understanding this article: http://lostechies.com/chadmyers/2010/02/13/composition-versus-inheritance/

For a real life example, say I have a State class that contains an EventListener class which has to register its events when the State calls its Begin method, unregister when it calls its End method.

Upvotes: 1

Views: 2639

Answers (4)

Anders Johansen
Anders Johansen

Reputation: 10455

Your reasoning is sound, and you are on the right track.

The use of DI and composition via interfaces/abstractions makes it bot easier to tst and construct the code. You can use temporary "fake" implementations of B and C while building A, if that helps your process.

The typical solution is to factor the construction and configuration of A, B and C out into a factory class or a subclass of A.

Upvotes: 1

Steve Py
Steve Py

Reputation: 34793

Dependency injection offsets complexity to a degree when dealing with dependencies by automating and governing the creation of those dependencies. (When implemented with an IOC Container such as Autofac or Unity) In a simple example of A, B, and C it's probably pretty difficult to see any real savings. Where you start to see lowering of complexity is when you implement class D with a dependency on C. An IOC Container facilitating your DI will know how to compose an A, B, and C (using an A & B) so your hand-rolled code doesn't need to worry about creating instances of objects to pass as dependencies. By complexity I'm referring to the amount of code/babysitting needed to govern dependencies.

Where IOC and DI really shine are as Peter points out, in de-coupling dependencies from one another so that code can be tested in isolation. If you want to write unit tests for class C, but C creates instances of A & B, it cannot be tested in isolation from A & B. When A or B is something like a data service, or file service this can make unit testing painful. Utilizing IOC, C accepts references to contract interfaces for those services which can be mocked out. This way C can be tested with predictable behaviour from its dependencies.

Upvotes: 0

Peter Ritchie
Peter Ritchie

Reputation: 35870

Dependency Injection doesn't aid object composition at all. You need a degree of composability before you can even use injection. Generally Dependency Inversion is used to invert conventional dependencies so that dependencies can be modelled by abstractions which themselves can be injected into something else without a direct coupling. Injection is a form of inversion that aids in decoupling, not composition.

Upvotes: 3

Volma
Volma

Reputation: 1345

I think your understanding of DI is correct. By removing instantiation of classes B and C from class A, DI makes them less coupled with each other. Now class A depends on the abstract interfaces IB and IC, and not on their concrete implementations. This makes code of class A more flexible (you can easily inject some other implementations of IB and IC if needed by simply reconfiguring your DI container), and testable (you can inject mock implementations of IB and IC). Also, it arguably makes code of class A more readable, because now its dependency on collaborators IB and IC is explicitly manifested by the signature of the constructor.

Upvotes: 0

Related Questions