Conform to Interfaces either totally or not at all with interface Segregation Principle
This article, the fourth of the 5-part article on S.O.L.I.D design principles, is about the “I,” Interface Segregation principle made famous by Robert C. Martin (Uncle Bob)in his paper, https://web.archive.org/web/20150924054349/http://www.objectmentor.com/resources/articles/Principles_and_Patterns.pdf.
I — Interface segregation principle
An Interface is a point where two disparate or similar systems meet to exchange information. The simplest example is this article on Medium, which is an interface for you, the reader, to learn and share something with another user or from another user. When it comes to object-oriented programming and objects, an interface is a description of the behavior of a class. It is a way of enforcing or informing the external world about the properties of an object.
Robert C Martin in his paper defines ISP or interface segregation principle as
Many client-specific interfaces are better than one general-purpose interface.
To simplify that a bit further, ISP states that
Clients should not be forced to depend upon interfaces that they don’t use. Make your interfaces lean, mean and super specific to the clients which implement it
Let us consider an abstraction for implementing the earth’s mightiest heroes (Drum Roll!). SuperHeroes have many extraordinary abilities that can be abstracted as under.
Now let us consider if we were implementing my favorite superhero, Superman with this interface.
What is obvious here is that Superman is being forced to confer to methods or interface which he really does not need to i.e growLarge(), shrink(). At first look, it might not seem such a big deal because an empty interface is quite harmless. While this might be true, a clear dependency has been formed between SuperHeroes and SuperMan with respect to an interface that he does not need to implement. Meaning any change to shrink() or growLarge() will cause SuperMan to change. Let’s say the shrink() interface is now modified by the JusticeLeague, to take a parameter size shrinkTo(size). Now SuperMan is forced to recompile and rebuild himself even though he gives two hoots about the shrink() interface. And we know he is busy fighting evil all across the world, so getting him to recompile and rebuild is a very very costly affair
What happened with the above interface SuperHeroes is it was bloated and needless for SuperMan’s convenience. Sure we could argue that we should not have made SuperMan conform to the Superheroes interface in the first place when he cant shrink() and growLarge() and that's quite true. A client should ideally conform to an interface totally and completely. The designing of the interface should be such that clients who conform or implement that, should implement all the methods within that in a meaningful manner i.e, not an empty implementation. Otherwise, our interfaces are bloated or fat and raise unnecessary dependencies
By applying ISP, our redesigned interface would look like this.
All superheroes fight bad guys. So SuperHeroes is now a base interface. Al FlyingSuperHeroes can fly(), all ShrinkingSuperHeroes can shrink() and GiantSuperHeroes can growLarge() in addition to fightVillains(). Different superheroes have different powers and hence those interfaces vary.
With this updated interface design, we can create concrete implementations of SuperHero as below. Now the dependency between the superheroes and the interfaces is very real, limited and required. So if the shrink() contract or growLarge() contract has changed, then its a necessary change for AntMan. If the fly() contract changes, its a required change for SuperMan. But in both cases, it won't affect the SuperHero who does not conform to the unnecessary interface. Concrete implementations of superheroes are no longer bothered by contracts of which they have no use. Also at the same time, SuperMan and AntMan are SuperHeroes and thus making the client safe from any modification required because a new additional superhero is being created
ISP simply states that interfaces should be categorized based on their type. Grouping of behavior based on similar characteristics. All the interfaces which exhibit similar behavior or have a common goal, or provide a similar service should be grouped together. What service does that type provide? Are their behaviors similar etc?