We will be talking about the ISP (Interface Segregation Principle) which deals with the disadvantages of “fat” or “polluted” interfaces, classes, etc. We will see how to avoid them and how they produce.
“Fat” is bad
Classes that have “fat” interfaces are not cohesive interfaces. The interfaces of the class can be broken up into member function groups. The ISP states that there are objects that require non-cohesive interfaces, but clients should not know about them as a single class, instead they should know about abstract classes with cohesive interfaces. These abstract classes can be referred as “interfaces”, “protocols” or “signatures”.
This happens when you force a class to inherit a class that is not required, but since one of the subclasses needs then you require it. With this you are polluting the class, and this problem increases as them goes on because if that subclass needs another class you will have to inherit it again in the base class. One practice that solves this is to add this classes as nil virtual functions and that way the subclasses will not need to implement these new classes, but this practices violates the Liskov Substitution Principle (LSP), because it leads to maintenance and reusability problems.
Separare Clients mean Separate Interfaces
This rule helps us to avoid recompiling or affecting other classes that have nothing to do with the change that it is being done. Sometimes when you require unnecessary classes to a class and you need to change one of the subclasses, the whole base class changes and sometimes breaks, in the best case scenario you will only need to recompile, but still this is not good. Recompiling can be very expensive, because of time, new object module, etc. When recompiling and adding changes to several files will increase the problem and also difficult to manage it if it exists.
Clients should not be forced to depend upon interfaces that they do not use
This means that the interfaces are coupled and when one client needs a change then all clients will be affected, we need to avoid this when possible and separate the interfaces.
Class Interface and Object Interface
Sometimes you will have an object with 2 different interfaces that ares used by 2 different clients. How can we separate these interfaces when they manipulate the same object? The clients can access the object through delegation, or through a base class of the object.
Separation Through Delegation
To perform this, we will use an “Adapter”, this adapter will be in the middle and delegate all the functionality to the corresponding classes, so that when something needs to change it will not affect the base classes because the adapter is doing all the work. The adapter translates the interfaces between each other and avoids coupling. This solution is not perfect because the adapter requires runtime and memory, and in some applications this is crucial and sometimes they can’t afford this change in memory.
Separation Through Multiple Inheritance
The way this works and actually a more solid one than Delegation, is by allowing both interfaces to access the same object, this way none of the clients depend on the object itself because they hace their separate interfaces.
Polyad vs Monad
When using a function that will receive the same object type but from different interfaces there are 2 forms of writing this function, there’s the polyadic form: function(Aobject, Bobject). And theres the monadic form: function(object). The polyadic form is preferable because the monadic form forces the function to depend upon every interface included in the object. With this all clients will need to recompile.