There are two properties of code that can give a pretty good indication as to the quality and the adherence to SOLID design principles, coupling and cohesion.
Coupling, at the class level, is an indication of the interdependence of one or more classes. If I make a change in class A how much impact does that have on class B? When I'm coding class B how much do I have to care about the implementation of class A? Coupling between classes should always be loose, the impact of change between classes should be minimal if not zero, classes should never be concerned about the implementation of others.
Cohesion, within a class, is an indication of how much the functionality belongs together, how closely related are the functions the class presents. Cohesion within a class should be high, the class should be focused on doing what it says on the tin.
These two properties usually have an inverse relationship, classes with loose coupling normally have high cohesion and vice-versa, but how do we make sure we achieve this?
Do One Thing and Only One Thing
Classes that do more than one thing have multiple reasons to change, there generally exposed to more areas of the code base both in terms of how many dependencies they have and how many classes are dependent on them. This web of interdependency leads to tight coupling and fragile code.
Almost by definition a class that does more than one thing has low cohesion. A tell tale sign of this is when a classes functions and member variables can easily be segregated, this is telling us there are multiple classes just waiting to be set free from the monster we have created.
Abstraction NOT Implementation
Coupling doesn't get much tighter than when classes are forced to be aware of and concerned with each others implementation.
Implementation is detail, classes should only be aware of the contract its dependencies have agreed to abide by, making it happen is your problem!
By inverting the control of dependencies and using an interface driven design we create loose coupling between dependents, if we also ensure that interfaces are well focused on supplying a single piece of functionality we will promote high cohesion within the implementation.
We Might Have Gone Too Far
In reality it is possible to take loose coupling and high cohesion too far.
Many systems, especially on mobile, have a variant of the event aggregator pattern, that is where classes can communicate by sending events out into the ether and some mediator will ensure that they make it to the appropriate recipient (for example NSNotification on iOS and Intent on Android).
On the face of it this seems as loose as coupling can get and in many situations is the ideal solution, but.....this shouldn't be used as an excuse not to have to think about the architecture of a system and how the bricks should fit together, we don't want a random collection of classes we want an engineered system. This type of pattern can also act as a smelly "get out of jail free card", I've coded myself into a corner but its ok I can just fire of this event and someone will save me!
A class could have perfect cohesion if it only presented a single, atomic operation, but the likely complexity of such classes and the resulting large number of dependencies that would be required to achieve an overall goal would lead to a complex potentially unmanageable design.
Loose coupling and high cohesion are a happy consequence of following SOLID design principles, we can use these principles to build a wall made of cohesive bricks and loose cement that can be adapted over time as we change the shape of our house, remember software development isn't a game of Jenga.
No comments:
Post a Comment