The problem with the following coupling
public class MyClass
{
private IMyInterface _instance = new MyImplementation();
...
Means that any time MyClass
is created (whether directly, or by an IoC container) is that it will always immediately create a concrete MyImplementation
and bind its dependency _instance
to this concrete implementation. In turn, it is likely that MyImplementation
has other dependencies, which are also coupled this way.
Benefits of decoupling of classes such that MyClass
is only dependent on interfaces to its dependencies, and not concrete implementations of the dependencies (i.e. the D of SOLID principles) include:
for Unit Testing - As you've mentioned, in order to test MyClass
in isolation, with new'ed
dependencies, you would need to resort to nasty things like Moles / Fakes
in order to mock out the the hard wired MyImplementation
dependency.
for Substitution - by coupling only to an interface, you can now swap out different concrete implementations of IMyInterface
(e.g. via configuring your IoC bootstrapping) without changing any code in MyClass
.
for making dependencies explicit and obvious in your system, as the IMyInterface
dependency may have further dependencies, which need to be resolved (and may need configuration considerations as well). If MyClass
hides the IMyInterface
dependency internally, it is not visible to the caller as to what the dependencies of MyClass
are. Although in classic 1990's OO this was commonplace (i.e. encapsulation + composition), this can obscure the implementation as deployment of all dependencies still needs to be done. However, with coupling done on interface level (i.e. consumers of MyClass will do so only via IMyClass
), the coupling-visible interface is IMyClass
which will again hide the dependency on IMyInterface
, since constructors are not visible on the interface).
for configurable dependency lifespan control. By injecting IMyInterface
, instead of newing MyImplementation
, you are allowing additional configuration options with respect to the lifespan management of the MyImplementation
object. When the original hardwired creation of MyImplementation
was done on MyClass
, it was effectively taking ownership of MyImplementation
's lifespan with a 1:1 relationship between the two class instances. By leaving this to the IoC container, you can now play with other options of MyImplementation
's lifespan, which might be more efficient, e.g. if MyImplementation
instances are thread-safe, you may elect to share an instance across multiple instances of MyClass
, for instance.
In summary, here's how I believe the refactoring should look suitable for IoC constructor dependency injection:
public class MyClass
{
// Coupled onto the the interface. Dependency can be mocked, and substituted
private readonly IMyInterface _instance;
public MyClass(IMyInterface instance)
{
_instance = instance;
}
...
The IoC container bootstrapping will define WHICH implementation of IMyInterface
needs to be bound, and will also define the lifespan of the dependency, e.g. in Ninject:
Bind<IMyInterface>()
.To<SomeConcreteDependency>() // Which implements IMyInterface
.InSingletonScope();