When reading articles on ISP, there seem to be two contradicting definitions of ISP:
According to first definition ( see 1, 2, 3), ISP states that classes implementing the interface shouldn't be forced to implement functionalities which they don't need. Thus, fat interface IFat
interface IFat
{
void A();
void B();
void C();
void D();
}
class MyClass: IFat
{ ... }
should be split into smaller interfaces ISmall_1
and ISmall_2
interface ISmall_1
{
void A();
void B();
}
interface ISmall_2
{
void C();
void D();
}
class MyClass:ISmall_2
{ ... }
since this way my MyClass
is able to implement only the methods it needs ( D()
and C()
), without being forced to also provide dummy implementations for A()
and B()
:
But according to the second definition ( see 1 , 2, answer by Nazar Merza), ISP states that MyClient
calling methods on MyService
shouldn't be aware of methods on MyService
that it doesn't need. In other words, if MyClient
only needs the functionality of C()
and D()
, then instead of
class MyService
{
public void A();
public void B();
public void C();
public void D();
}
/*client code*/
MyService service = ...;
service.C();
service.D();
we should segregate MyService's
methods into client-specific interfaces:
public interface ISmall_1
{
void A();
void B();
}
public interface ISmall_2
{
void C();
void D();
}
class MyService:ISmall_1, ISmall_2
{ ... }
/*client code*/
ISmall_2 service = ...;
service.C();
service.D();
Thus with the former definition, the goal of ISP is to "make the life of classes implementing IFat interface easier", while with the latter the goal of ISP is to "make the life of clients calling methods of MyService easier".
Which of the two different definitions of ISP is actually correct?
@MARJAN VENEMA
1.
So when you are going to split IFat into smaller interface, which
methods end up in which ISmallinterface should be decided based on how
cohesive the members are.
While it makes sense to put cohesive methods within the same interface, I thought with ISP pattern the needs of the client take precedence over the "cohesiveness" of an interface. In other words, I thought with ISP we should lump within the same interface those methods needed by particular clients, even if that means leaving out of that interface those methods that should, for the sake of cohesiveness, also be put inside that same interface?
Thus, if there were lots of clients that will only ever needed to call CutGreens
, but not also GrillMeat
, then to adhere to ISP pattern we should only put CutGreens
inside ICook
, but not also GrillMeat
, even though the two methods are highly cohesive?!
2.
I think that your confusion stems from the a hidden assumption in the
first definition: that the implementing classes are already following
the single responsibility principle.
By "implementing classes not following SRP" are you referring to those classes that implement IFat
or to classes that implement ISmall_1
/ ISmall_2
? I assume you're referring to classes that implement IFat
? If so, why do you assume they don't already follow SRP?
thanks
Best Answer
Both are correct
The way I read it, the purpose of ISP (Interface Segregation Principle) is to keep interfaces small and focused: all interface members should have very high cohesion. Both definitions are intended to avoid "jack-of-all-trades-master-of-none" interfaces.
Interface segregation and SRP (Single Responsibility Principle) have the same goal: ensuring small, highly cohesive software components. They complement each other. Interface segregation ensures that interfaces are small, focused and highly cohesive. Following the single responsibility principle ensures that classes are small, focused and highly cohesive.
The first definition you mention focuses on implementers, the second on clients. Which, contrary to @user61852, I take to be the users/callers of the interface, not the implementers.
I think that your confusion stems from the a hidden assumption in the first definition: that the implementing classes are already following the single responsibility principle.
To me the second definition, with the clients as the callers of the interface, is a better way of getting to the intended goal.
Segregating
In your question you state:
But that is turning the world upside down.
So when you are going to split
IFat
into smaller interface, which methods end up in whichISmall
interface should be decided based on how cohesive the members are.Consider this interface:
Which methods would you put in
ICook
and why? Would you putCleanSink
together withGrillMeat
just because you happen to have a class that does just that and a couple of other things but nothing like any of the other methods? Or would you split it into two more cohesive interfaces, such as:Interface declaration note
An interface definition should preferably be on its own in a separate unit, but if it absolutely needs to live with either caller or implementer, it should really be with the caller. Otherwise the caller gets an immediate dependency on the implementer which is defeating the purpose of interfaces altogether. See also: Declaring interface in the same file as the base class, is it a good practice? on Programmers and Why should we place interfaces with classes that use them rather than those that implement them? on StackOverflow.