views:

224

answers:

5

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?

+2  A: 

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.

Tomas Lycken
I'd question whether the default constructor is a good idea. If you want default construction, it'd be better to use a factory class to get a "default" instance. That wayou keep the implementation of `IndependentClass` out of the implementation of `DependentClass`.
Nader Shirazie
True - in a larger application that is a better way to do it. However, for this small example I found it overkill to use a factory just to show how DI can be done without a third party framework.
Tomas Lycken
What good does your invoking the default constructor do in this case? The instance you're creating isn't ever assigned to anything.
4thSpace
Yes it is. When invoking the default constructor (first one in my code), the constructor chain kicks in, and the second constructor is invoked with an argument of `new IndependentClass()`. In the second constructor's method body, the instance of `IndependentClass` is assigned to the field.
Tomas Lycken
+4  A: 

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.

Esko Luontola
+1  A: 

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.

Fredrik Mörk
A: 

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.

Claudiu
Thanks. I guess that is why I don't see the need for DI, since I rarely use interfaces and still achieve loosely coupled classes/components.
4thSpace
A: 

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; } 
    }
    
Charles Bretana
So DI is only useful with interfaces right? And if you have the above scenarios but not interfaces than don't use DI?
4thSpace