Inversion of control and dependency injection

Here is one of the most talked about concepts: Ioc (inversion of control). I have been using this concept for quite some time. I usually use the DI approach (not the service locator) to implement IoC in my code. Below is my understanding of IoC.

If classA depends on class B, if it declares an instance of class B inside it. It depends on class B, for some reason (I will come to this later), but not good, and therefore we use DI to fix this. So now we have an interface called IClassB, which will be passed in the constructor of classA (for illustration, I am involved in the injection of the constructor). Here is 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 is the code that makes it work:

 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 demonstrated the above example to make sure my understanding of IoC and DI is correct. If you find something wrong, please correct me. Now, when I was trying to find out why IoC, I found two important reasons:

  • Verification: Of course, the correct problem. Suppose that in the example above, for example, the classB method uses an SMPTP server or some WCF / Webservice, we will not be able to test it. However, we can create a test stub class that implements the IClassB interface and continue testing by passing an instance of the test stub class.

  • Dependence in terms of a particular implication: This is something I could not agree with. Even if I changed the class A code with the following code:

 public class ClassA { ClassB _classBObj = null; public ClassA() { _classBObj = new ClassB(); } public void UseClassBMethod() { this._classBObj.classBMethod(); } } 

How does IoC help get rid of any problem that occurs due to a change in the class B method of the ClassB object? If there are any changes in the method signature, we will also need to make this change in the signature of the IClassB interface (this actually increases the refactoring task.). If a change in a method implementation says, for example, that it requires an additional registration task, that change will be limited to the ClassB code.

I may seem a little silly with the question, but can anyone imagine a scenario in which (besides unit testing) this approach helps us?

Thanks so much for this.

+4
source share
2 answers

The main idea here is that you are program against an interface .
Your code does not know any specific classes.

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

In your case, you can replace ClassA classA=new ClassA(new ClassB); on ClassA classA=new ClassA(new ClassC); which has different behavior, for example. is less or more optimal or requires some password to do something, etc.

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

If the interface changes, of course, your code is affected. This cannot be avoided.

But what you get is that you can switch and change specific implementations without affecting your code, and these changes are most often necessary and occur than an interface that has been correctly defined to suit your needs.

+6
source

I think in some cases you do not want to inject an instance into the constructor. I often add lazy objects or factory delegates. I will try to explain why you want to do this.

In some real-world scenarios, you can embed multiple instances into the constructor, and sometimes, depending on the application logic, only some of them are actually used during the life cycle of the object. This means that sometimes you initialize the 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. This is great when dependency instances take a long time or an 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 insert a factory delegate into the constructor instead of the instance. It is very similar to Lazy's solution, but has several advantages. Implementing a factory delegate is cool if your class should be able to create any number of new instances of the dependency class (want to instantiate in a loop or something like that). In this example, ClassA will reference a method that can create an object that implements IClassB. To make things more interesting, ClassB, which implements IClassB, also has an IClassC dependency.

 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 advantage of this approach is to reduce the need for memory without initializing objects that you cannot use. The advantage of introducing a factory delegate is that you can hide the dependency initialization logic, and you can control which factory parameters are exposed to the caller. ClassA does not need to know how to collect all its dependencies, it only needs to know the parameters that it knows about, and it can control. This makes it easier to replace dependencies.

I hope this makes sense :) I just wanted to demonstrate how you can get more from the template using the C # functional style.

+2
source

Source: https://habr.com/ru/post/1390013/


All Articles