Sunday 6 September 2015

The Fifth Pillar



Traditionally their are four so called pillars to object oriented programming (OOP), these concepts separate OOP from its procedural counterparts and define what the properties of good code should be and some of the tools we have to achieve them.
"Abstraction, Encapsulation, Inheritance and Polymorphism" 
I think their should be a fifth item on this list or maybe even that one item on that list should be replaced, but before we get to that why are these concepts so important?
Abstraction
Abstraction is about the management of complexity and you could argue is the very reason to write code in the first place, to make something complex simpler.
Proper use of abstraction takes a situation that is complex and gradually simplifies by reducing the amount of the system that is in view at any one time.
As an example, I have an application that needs to save some data, do I want every part of my system that needs to do this to be creating SQL statements and interacting with a database?
Instead I create an abstraction of a data store that exposes basic CRUD functionality, now I'm free to put that data anywhere and by any means, no other part of the system cares because they only deal with the abstraction not the implementation.
Interfaces are the currency of this design by contract approach to abstraction, the loose coupling they provide is the single biggest quality a code base can have, its the path down which testability and maintainability are achieved.
Encapsulation
Encapsulation is sometimes confused with abstraction, but while abstraction is concerned with reducing coupling encapsulation is all about cohesion.
Encapsulation is the vale behind which we hide things that you don't need to concern yourself with, we restrict your access because the information we're hiding should have one authoritative source and not be open to change from many different places.
Imagine we were writing code to hold a database of customer records, does it make sense to store all customer addresses in a single array? Or would it be better to have a customer object that has an address property.
The former has low cohesion because the data and the code that operates upon it are separated, the latter moves them together to encapsulate the concept of a customer.
Polymorphism
Polymorphism is the concept that when you look at a class you can see what you want to see, and what you see maybe different to what others see. 
Polymorphism is the natural consequence of the good use of abstraction and encapsulation, once you only care about the interface to an object you start seeing things that might be very different in the same light because they've agreed to do the same thing.
With our data source abstraction we are able to see a T-SQL, MySQL or even a simple array as one and the same thing.
This indifference to detail is what leads to testability and maintainability, my class sees no difference between a real data source and a mock one in a test, you need to start saving data to the cloud, no problem I don't know what your doing with this stuff anyway, just honour the contract. 
Inheritance
Inheritance is the concept that objects can be derived from other objects, most examples revolve around some kind of data model, a Mercedes is a car which is a vehicle etc.  
Inheritance should be considered OOP's nuclear option.
I've seen more code bases in distress because of the bad use of inheritance then any other reason.
Clearly their is a place for inheritance, the majority of application frameworks rely on it, but when you use it in the wrong place it will kill testability and maintainability in a single stoke.
This improper use is usually driven by the so called "is-a" test being confused with "uses-a", for example is a car an engine or does it use an engine? You don't need to inherit from something because you rely on it, it should only ever be because you are and will always be one of those.
The bad use of inheritance is also a very difficult road to reverse out from, once you've built a web of interdependency it has to be torn down not re-arranged.
Composition
The answer, and in my opinion the fifth pillar, is composition.
Composition isn't a grand concept its simple saying you should build big things from using lots of small things, their doesn't need to be a relationship between them just that one is providing functionality that the other can make use of and combine with others to construct something as big as the sum of its parts.
Their is almost nothing that can't be built using composition and the fact that interactions between objects is based solely on functionality means by design you have created testable and maintainable code. 
Preferring composition over inheritance will very very rarely steer you wrong, don't let inheritance become the crutch you rely on because it can very easily become weak and fragile and not hold your weight any more.
Instead use the strength of composition to build systems with strong abstractions, well encapsulated information and use polymorphism to test all the things and adapt at a moments notice.   

No comments:

Post a Comment