Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inversion of control and Dependency Injection

Here is one of the very talked about concept: Ioc (Inversion of control). I have been using this concept for a quite some time now. Usually I go for DI (rather than service locator) approach to implement IoC in my code. Below is my understanding of IoC.

If classA is depending on classB if it declares an instance of classB inside it. It's being dependent on classB, is by some reason (I will come to that later), not good and hence we use DI to fix this. So now we have an interface called IClassB that will be passed in classA's constructor (I am taking up constructor injection here, for illustration). Here goes the code:

public class ClassA
{
    IClassB _classBObj = null;

    public ClassA(IClassB classBObj)
    {
         _classBObj = classBObj;
    }

    public void UseClassBMethod()
    {
        this._classBObj.classBMethod();
    }
}

public class ClassB:IClassB
{

    public void classBMethod()
    {
        //Some task carried out by classB.
    }
}

public interface IClassB
{
    void classBMethod();
}

And here goes the code that makes it go running:

class Program
{
    static void Main(string[] args)
    {
        //This code is outside of class A and hence declaring 
        //object of ClassB is not dependency
        ClassA classA=new ClassA(new ClassB);
        classA.UseClassBMethod();

    }
}

I have demonstrated the above example just to ensure if my understanding of IoC and DI is correct. If you find anything wrong please do correct me. Now, while I tried to find out why IoC, I found two important reasons:

  1. Testability: Certainly a correct concern. Lets assume, in the above example for instance, classB method makes use of SMPTP server or some WCF/Webservice, we may not be able to test it. We can however create a test stub class implementing the interface IClassB and go ahead with testing by passing an instance of the test stub class.

  2. Dependency in terms of concrete impementation: This is something I could not get along with. Even if I alter the code of classA with the following code:

public class ClassA
{
   ClassB _classBObj = null;

   public ClassA()
   {
     _classBObj = new ClassB();
   }

   public void UseClassBMethod()
   {
      this._classBObj.classBMethod();
   }
}

How is IoC helping to get rid of any problem that arises because of change in the classB Method of the ClassB object? If there is any change in the method signature we will have to make that change in the interface IClassB's method signature too (this in fact, is increasing the refactoring task.). If the method implementation changes say for instance it takes up an additional task of logging, the change is going to be limited to ClassB code.

I may appear a bit stupid with the question but can somebody present a scenario where (apart from unit testability) we are getting benefited by this approach?

Thanks a lot for reading this.

like image 473
James Avatar asked Feb 21 '23 22:02

James


2 Answers

The core concept here is that you program against an interface.
Your code does not know of any concrete classes.

As a result you can switch concrete implementations without affecting your code.

In your case you could replace ClassA classA=new ClassA(new ClassB); with ClassA classA=new ClassA(new ClassC); which has different behavior e.g. is less or more optimal or requires some password to do something etc.

The idea is that if ClassC adheres to the interface that ClassB also implements then you can change to use ClassC without any change in your code

If the interface change, of course your code is affected. This you can not avoid.

But what you gain, is that you can switch and change the concrete implementations without affecting your code and this changes are more frequently needed and occuring than an interface that has been defined properly according to your needs

like image 80
Cratylus Avatar answered Feb 24 '23 11:02

Cratylus


I think in some cases you don't want to inject an instance into a constructor. I often inject lazy objects or factory delegates. I will try to explain real quick why you would want to do this.

In some real world scenarios you may end up injecting a lot of instances into the constructor and sometimes depending on the app logic only just a few of them are actually used during the object's life cycle. It means that sometimes you initialize a dependency even if it is not used.

In .NET 4 you can use the Lazy class which allows you to instantiate the dependency class only when it is first used. It is cool when instantiating the dependencies take a long time or the instance requires a lot of memory.

public class ClassA
{
    private readonly Lazy<IClassB> _lazyClassBObj;

    public ClassA(Lazy<IClassB> lazyClassBObj)
    {
      _lazyClassBObj = lazyClassBObj;        
    }

    public void UseClassBMethod()
    {
        _lazyClassBObj.Value.classBMethod();
    }
}

class Program
{
    static void Main(string[] args)
    {
        ClassA classA = new ClassA (new Lazy<IClassB>(() => new ClassB));
        ...

    }
}

Another trick is to inject the factory delegate into the constructor instead of the instance. It is very similar to the Lazy solution, but it has a few advantages. Injecting the factory delegate is cool if your class must be able to create any number of new instances of the dependency class (want to create instances in a loop or something similar). In this example ClassA will have a reference to a method which can create an object which implements IClassB. To make things more interesting the ClassB which implements IClassB has a IClassC dependency as well.

public class ClassA
{
    private readonly Lazy<IClassC> _lazyClassBObj;
    private readonly Func<IClassC, IClassB> _classBObjFactory;

    public ClassA(Func<IClassC, IClassB> classBObjFactory, Lazy<IClassC> lazyClassBObj)
    {
      _classBObjFactory = classBObjFactory;
      _lazyClassBObj = lazyClassBObj;        
    }

    public void UseClassBMethod()
    {
        var classC = _lazyClassBObj.Value;

        var classB1 = _classBObjFactory(classC); // instance 1
        var classB2 = _classBObjFactory(classC); // instance 2
        var classB3 = _classBObjFactory(classC); // instance 3
    }
}

The main benefit of this approach is to reduce the memory requirement by not initializing objects you might not use. The benefit of the factory delegate injection is that you can hide init logic of dependencies and you can control which factory params are exposed to the caller. ClassA doesn't need to know how to assemble all its dependencies, it only needs to know the params it knows about and it can control. It allows you to replace the dependencies more easily.

I hope it makes sense :) Just wanted to demo how you can get more from the pattern using the C# functional style.

like image 37
Jeno Laszlo Avatar answered Feb 24 '23 13:02

Jeno Laszlo