Sunday 4 September 2016

Flipping Dependencies



Sometimes we can be guilty of following a principle or a pattern without fully understanding or appreciating why this is a good thing.

Some could argue what difference does it make if your writing good code? But without understanding what makes the code good you invite the risk of unintentionally degrading it.

One of these patterns is dependency injection, sometimes developers will implement this pattern without appreciating that the reason for doing it is to apply the principle of Inversion of Control (IoC).

Static vs Dynamic

A class that does not apply IoC is statically bound to its dependencies, they are defined when the class is compiled and will never change without the code being changed.

When a class is written to apply the IoC principle it is dynamically bound to its dependencies they are not necessarily defined at compile time, instead they are loosely coupled by a shared contract in the form of an interface between the dependency and the dependent.

The major point here is that we have inverted who is in control of defining which implementation will fulfil the dependency for the class we have written. Rather than the class itself controlling this the framework in which the class operates is in control.

So why is this inversion a good thing?

Hang Loose

The primary benefit of IoC is to create loose coupling between classes.

The benefits of this to the dependent class should be clear, it makes the class eminently testable and increases the opportunities for re-use.

It is almost by definition not possible to test a class that is statically bound to its dependencies.

A unit test should only have one reason to fail, namely that the class being tested is not supplying its required functionality. It should not be the case that the failure is caused by one of its dependencies, this would mark this test as an integration test, this doesn't mean the test has no value but it does make it harder to interpret the results.

If its impossible to break a class away from its dependencies then its impossible to write a unit test.

Quite often despite the fact that a class has the control to define the implementation of its dependency it actually has no reason to have this control, it has not got a dependency on the implementation only on the functionality.

By applying IoC we properly model this situation and allow the class to function with any implementation willing to provide the desired functionality, we have made the class more flexible, re-usable and ensured we do not have to needlessly write more code or copy code we've already written.

Loose coupling also has a benefit to the class supplying the dependency, by being statically bound to an implementation we don't allow for any change in that implementation without the requirement to transmit that change to the dependent classes.

By dynamically binding to the functionality we allow for change in implementation to have no impact on the dependent class allowing it to carry on working in the way it did before. 

Giving Up Control

Once a class is no longer responsible for creating its dependencies it can also be absolved of any responsibility for implementing logic relating to that creation.

The IoC principle allows our use of constructs such as singletons, object pools or prototypes to be both hidden from the class that needs the functionality and centralised within our implementation of a container to realise the IoC principle.

This is another example of how application of IoC is all about leaving the class that requires dependencies to be written with only concern for the functionality it needs to offer and not also be responsible for playing a role in implementing the framework we need our code to run in.

In this way IoC can be seen as playing a role in ensuring we follow multiple elements of the SOLID principles, not only dependency inversion but also single responsibility and the open close principle. 

Too often we can simple show developers a piece of code and simply tell them this is good code.

But this encouragement to simply repeat what you've been shown without appreciation for why the code is good will inevitability lead to the subtitles of the code being missed.

It is never the case in software engineering that any particular pattern can be endlessly applied, there are always other factors to consider and different situations to recognise.

The way to enable developers to appreciate this is to teach them the principles that are used to define good code, which patterns to use are a natural consequence of understanding these principles. 

No comments:

Post a Comment