Dude what’s that? My gosh, did you write that? It’s hard to understand! Dude! you should change this code.
If you are a developer, working within a team, you have probably spoken or heard one of these expressions, but I believe nobody said that out of spite. Quite the contrary, they are probably trying to help. Therefore, I will share in the next months a series of articles where I try to explore some basic concepts / principles that can help us write better code.
Here are the topics :
-
Programming to a interface, not an implementation.
-
Favor composition over inheritance.
-
Single Responsibility Principle.
-
The Open closed principle.
-
The interface segregation Principle
We start with the first topic.
Programming to a interface, not an Implementation
The sentence in the title was introduced in 1994 by the Gang of Four in one of the most important books ever written about Design Patterns in software development, the “Design Patterns: Elements of Reusable Object-Oriented Software”. This principle is one of the most important to build flexible, reusable and maintainable software products.
I would like to recall that the word interface in the sentence above is not refer to the keyword “interface” that some OOP languages have. The word interface from a dictionary definition “is a point where two systems, subjects, organizations, etc, meet and interact”. In a software development two objects interact using their exposed functions, those functions represent the protocol or vocabulary that the object define as a mean of communication with the external word.
But, what exactly does it mean?
It’s all about coupling, it’s all about the first part of the phrase, “Low in coupling and high in cohesion”.
The point is to exploit polymorphism by programming to a supertype so that the actual runtime object isn’t locked into the code.
I like to think about this principle in the following way: The Abstract dependency is the health one. Because every time we depend in the most abstract type possible for our business logic we are reducing coupling. Decoupling from the implementation allows us to change the implementation without affecting any class or client.
In Java, we can define the vocabulary of an object by creating an abstract class or an interface class. Using one of them we can define a set of algorithms with identical interface.
This set of algorithm if derived from the same abstract/interface class will share its vocabulary, and all sub classes can only add or overrides operations, never hide them. Keeping the vocabulary defined by the abstract class always valid, all sub classes can then respond to the requests for the abstract class type.
This brings two benefits:
-
The external object / client remain unaware of the concrete type of object that implements the algorithm, as long as the objects is in conformance with the interface expected by the client.
-
If your algorithm will be used outside of your context (ex: different application), the client remains unaware of the classes, it only needs to know about the abstract class or the interface class, reducing the coupling between them.
How do we write code in conformance with this principle?
Now lets see some code examples, I will write a simple example using Java.
Imagine a game about superheros, and you are developing the module that creates and displays the superheros. How can we design the set of classes that represent the different superheros?
We should define an abstraction, a contract that each superhero will follow, so that the different modules can depend on that contract and become unaware of the concrete classes.
public abstract class SuperHero { public void run() { //run common behavior } public void walk() { //walk common behavior } public abstract void superPower(); public abstract void render(); }
public class SuperMan extends SuperHero { public void superPower() { // SuperMan super power implementation } public void render() { // render super man } }
public class SpiderMan extends SuperHero { public void superPower() { //spiderman super power implementation } public void render() { // render spider man } }
So, to be in tune with this principle we have always to code using the type far up abstraction that can respond to our needs. That means we should not declare variables to be instances of a concrete class.
Ex: SpiderMan spider = new SpiderMan();
Instead we should declare a variable as the type far up abstraction.
Ex : SuperHero superHero = new SpiderMan();
Or even better, we can assign the concrete implementation at runtime.
Ex: SuperHero superHero = getSuperHero();
That’s it, a really simple and straightforward principle that help us write better code, and that we should have always in mind.