Example of Dependency Injection with only base cla

2019-09-05 14:46发布

问题:

Is it possible to do DI without any third party tools? I've read about people doing it with an abstract class and interface before they discovered some DI framework. How is ID done in that very basic form?

回答1:

Just pass the dependencies to the constructor of the class when you instantiate it. No DI frameworks are needed when the project is small (below a couple of thousand lines of code) - you can write a factory and wire up all the dependencies manually.



回答2:

Of course it's possible without third-party tools. Simple sample:

interface ILogger
{
    void Log(string text);
}

class SomeClass
{
    private ILogger _logger;
    public SomeClass(ILogger logger)
    {
        _logger = logger;
    }

    public void DoSomeWork()
    {
        Log("Starting");
        // do work

        Log("Done");
    }

    private void Log(string text)
    {
        if (_logger != null)
        {
            _logger.Log(text);
        }
    }
}

SomeClass takes an ILogger as input in the constructor. It uses it for logging some output. Let's say we want it in the console:

class ConsoleLogger : ILogger
{
    public void Log(string text)
    {
        Console.WriteLine(text);
    }
}

In some code:

SomeClass instance = new SomeClass(new ConsoleLogger());
instance.DoSomeWork();

..but then we want the log in a file instead:

class FileLogger : ILogger
{
    private string _fileName;
    public FileLogger(string fileName)
    {
        _fileName = fileName;
    }

    public void Log(string text)
    {
        File.AppendAllText(_fileName, text);
    }
}

So, we inject the file logger instead:

SomeClass instance = new SomeClass(new FileLogger("path to file"));
instance.DoSomeWork();

SomeClass is happily unaware of the ILogger implementation in use, and just uses whichever implementation that is injected. It's usually a good idea to have a factory creating the instances of the interface implementations instead of having the objects constructed all over the code, in order to make it simpler to change the implementation in use.



回答3:

There is a nice description in this tutorial.

Basically what you do is that you let the DependingClass only know about an Interface, and then you implement that interface with your IndependentClass. With constructor overloads, you let for example a unit testing framework send in a mock object.
Some code might make it easier to understand what I'm getting at:

public interface IAnInterface
{
    void MethodOne();
    void MethodTwo();
}

public class IndependentClass : IAnInterface
{
     // Implements all members of IAnInterface
}

public class DependentClass
{
    private IAnInterface _dependency;

    public DependentClass() : this(new IndependentClass()) { }

    public DependentClass(IAnInterface dependency)
    {
        this._dependency = dependency;
    }
}

Now, as you see, we have provided a default class type that will be instantiated if no argument is provided to the constructor. But we have also allowed for injection of a different class that implements the same interface, or a mock object of the same.

EDIT: As pointed out in a comment, it is probably better in a larger app to have a factory that instantiates the DependingClass, and remove the "default" constructor. That way you only have to change in one place if you decide to change the implementation.



回答4:

You can create your components communicating with each other through interfaces and have your hosting program that instantiates the components and link them together.

This would be your solution structure:

  • An dll assembly that defines the contract between components (interfaces + data objects that are part of the interface methods signatures).

  • One ore more dll assemblies that defines your components (that implement interfaces). Any communication between components is done through interfaces.

  • An exe assembly that starts up the hosting process, instantiates the components and link them setting some properties. Whenever you need to substitute one component you only need to change this project.

You can create unit tests for any of your components mocking up the components that are used by the component you are testing.

Also you can get fancy reading the property bindings from the app.confing file in the hosting project.



回答5:

There are three ways you can do it...

  1. Pass the reference to the dependant instance (which is an instance of a class that implements the interface of course) in the constructor.

    public class MyClass
    {
        private readonly ISpillDAL iSpDal;
        public ISpillDAL SpillDal { get { return iSpDal; } }
        public SpillLogic() : this(null) { }
        public SpillLogic(ISpillDAL splDAL)
        {
           iSpDal = splDAL ?? new SpillDAL();  // this allows a default 
        }
    }
    
  2. Create the new object and then pass the reference to the dependant object via a property setter

    public class MyClass 
    { 
       private readonly ISpillDAL iSpDal; 
       public ISpillDAL SpillDal    
       { 
          set { iSpDal = value; } 
          get { return iSpDal; } 
       }
       public SpillLogic() { }        
    }
    
  3. Use a function within the object that accepts the reference and assigns it to the internal provbate variable you have created for this

    public class MyClass 
    { 
       private readonly ISpillDAL iSpDal; 
       public ISpillDAL SpillDal    
       { 
          set { iSpDal = value; } 
          get { return iSpDal; } 
       }
       public SpillLogic() { }    
       public void InjectSpillDAL(  ISpillDAL splDAL )
       {  iSpDal = splDAL; } 
    }