Example of dependency injection with base class only
In this guide .
Basically what you do is that you DependingClass
only let know about Interface
and then implement that interface with IndependentClass
. With constructor overloads, you allow, for example, unit testing of frames to be dispatched to a mock object.
Some code might make it easier to understand what I am getting:
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 can see, we have provided a default class type that will be generated if no argument is provided to the constructor. But we also allowed the injection of another class that implements the same interface, or a mock object of the same.
EDIT: As pointed out in the comment, it is probably better in a larger application to have a factory that instantiates DependingClass
and removes the default constructor. This way you only need to change in one place if you decide to change the implementation.
a source to share
Of course, this is possible without third-party tools. Simple example:
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 ILogger
as input in the constructor. He uses it to record some result. 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 instead we want the file to be written to the file:
class FileLogger : ILogger
{
private string _fileName;
public FileLogger(string fileName)
{
_fileName = fileName;
}
public void Log(string text)
{
File.AppendAllText(_fileName, text);
}
}
So, instead, we insert the file logger:
SomeClass instance = new SomeClass(new FileLogger("path to file"));
instance.DoSomeWork();
SomeClass
happily not aware of the implementation ILogger
that is in use and just uses the one that was introduced. It's generally a good idea to create a factory creating instances of interface implementations instead of creating objects throughout your code to make it easier to change the implementation you're using.
a source to share
You can create your components that interact with each other through interfaces and have your own hosting program that creates the components and links them together.
This will be your solution structure:
-
A collection of dlls that defines the contract between components (interfaces + data objects that are part of the interface method signatures).
-
One or more dll assemblies that define your components (that implement interfaces). Any communication between components is done through interfaces.
-
A compilation exe that starts the hosting process, creates the components and bundles them by setting some properties. Whenever you need to replace one component, you only need to change that project.
You can create unit tests for any of your components mocking the components that are used by the component under test.
Also you can get a fantastic read on property bindings from the app.confing file in your hosting project.
a source to share
There are three ways to do this ...
-
Pass a reference to the dependent instance (which is an instance of the 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 } }
-
Create a new object and then pass the reference to the dependent object using the property setter
public class MyClass { private readonly ISpillDAL iSpDal; public ISpillDAL SpillDal { set { iSpDal = value; } get { return iSpDal; } } public SpillLogic() { } }
-
Use a function inside an object that takes a reference and assigns it to the provbate internal variable you 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; } }
a source to share