17

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

Tulains Córdova
  • 39,201
  • 12
  • 97
  • 154
EdvRusj
  • 623
  • 6
  • 12
  • 4
    Why can't there be multiple definitions that are both served by the same principle? – Bobson Jun 21 '13 at 17:01
  • 8
    These definitions do not contradict one another. – Mike Partridge Jun 21 '13 at 17:04
  • 1
    No of course the client's need don't take precedence over the cohesiveness of an interface. You can take this "rule" way to far and end up with single method interfaces all over the place that make absolutely no sense whatsoever. Stop following rules and start thinking of the goals for which these rules were created. With "classes not following SRP" I wasn't talking about any specific classes in your example or that they were not already following SRP. Read again. The first definition only leads to splitting an interface if the interface isn't following ISP and the class is following SRP. – Marjan Venema Jun 22 '13 at 09:36
  • 1
    Btw: it is not considered "playing nice" to extend your question in this way. I understand in this case because you have fairly long text, but these should really have been comments to my answer. – Marjan Venema Jun 22 '13 at 09:39
  • @Marjan Venema: "I wasn't talking about any specific classes in your example or that they were not already following SRP. Read again." Sorry, I've misread it. "The first definition only leads to splitting an interface if the interface isn't following ISP and the class is following SRP." So does the second definition, I presume, or is there a catch? – EdvRusj Jun 22 '13 at 12:00
  • 3
    The second definition doesn't care about the implementers. It defines interfaces from the perspective of the callers and doesn't make any assumptions about whether implementers already exist or not. It probably assumes that when you follow ISP and come to implement those interfaces, you would, of course, follow SRP when creating them. – Marjan Venema Jun 22 '13 at 12:03
  • 2
    How do you know beforehand what clients will exist and what methods will they need ? You can't. What you can know before hand is how cohesive your interface is. – Tulains Córdova Jun 23 '13 at 18:00
  • Isn't it both? In your second example, MySmall doesn't need to implement Small_1. – user253751 Aug 25 '20 at 16:48
  • @Mike Partridge : But what if not every user of a given class is going to use every method thereof, but on the other hand, it logically makes sense for those methods to go together on a single class? For example, a "BankAccount" object may be a single coherent unit of responsibility, but not every user is _necessarily_ going to use _both_ the Deposit and Withdraw methods thereupon. – The_Sympathizer Jan 14 '21 at 21:58
  • So it seems to me the "segregate by user" ("client" = "user") could very well end up with an extreme amount of single method interfaces, and this contradicts the other definition ("client" = "implementer"), which favors a bit more lumping. – The_Sympathizer Jan 14 '21 at 22:01

4 Answers4

18

You confuse the word "client" as used in the Gang of Four documents with a "client" as in consumer of a service.

A "client", as intended by Gang of Four definitions, is a class that implements an interface. If class A implements interface B, then they say A is a client of B. Otherwise the phrase "clients should not be forced to implement interfaces they don't use" wouldn't make sense since "clients" ( as in consumers ) don't implement anything. The phrase only makes sense when you see "client" as "implementor".

If "client" meant a class that "consumes" (calls) the methods of another class that implements the big interface, then by calling the two methods you care about and ignoring the rest, would be enough to keep you decoupled from the rest of the methods you don't use.

The spirit of the principle is avoid the "client" ( the class implementing the interface ) having to implement dummy methods in order to comply with the whole interface when it only cares about a set of methods that are related.

Also it aims to having the less amount of coupling as possible so that changes made in one place cause the less impact. By segregating the interfaces you reduce the coupling.

That problems appears when the interface does too much and have methods that should be divided into several interfaces instead of just one.

Both of your code examples are OK. It's only that in the second one you assume "client" means "a class that consumes/calls the services/methods offered by another class".

I find no contradictions in the concepts explained in the three links you gave.

Just keep clear that "client" is implementor, in SOLID talk.

Tulains Córdova
  • 39,201
  • 12
  • 97
  • 154
  • But according to @pdr, while code examples in all the links do adhere to ISP, the ISP definition is more about "isolating the client ( a class that call methods of another class ) from knowing more about the service" than it is about "prevention from clients ( implementors ) being forced to implement interfaces they don't use." – EdvRusj Jun 21 '13 at 18:09
  • 1
    @EdvRusj My answer is based on the documents in Object Mentor ( Bob Martin enterprise ) website, writen by Martin itself when he was in the famous Gang of Four. As you know the Gnag of Four was a group of software engineers, including Martin, that coined the S.O.L.I.D acronym, identified and documented the principles. https://docs.google.com/a/cleancoder.com/file/d/0BwhCYaYDn8EgOTViYjJhYzMtMzYxMC00MzFjLWJjMzYtOGJiMDc5N2JkYmJi/edit?hl=en&pli=1 – Tulains Córdova Jun 21 '13 at 18:37
  • So you disagree with @pdr and thus you find the first definition of ISP ( see my original post ) more agreeable? – EdvRusj Jun 21 '13 at 18:49
  • @EdvRusj I think both are right. But the second one adds unnecessary confusion by using the client/server metaphor. If I have to choose one, I'd go with the official Gang of Four one, which is the first. But what is important it to reduce coupling and unnecessary dependencies which is the spirit of the SOLID principles after all. It doesn't matter which one is right. The important things is you should segregate interfaces acording to bahaviors. That's all. But when in doubt, just go to the original source. – Tulains Córdova Jun 21 '13 at 18:53
  • 3
    I so disagree with your assertion that "client" is implementor in SOLID talk. For one it is linguistic nonsense to call a provider (implementer) a client of what it is providing (implementing). I also have not seen any article on SOLID that tries to convey this, but I may simply have missed that. Most importantly though it sets up the implementer of an interface as the one deciding what should be in the interface. And that makes no sense to me. The callers/users of an interface define what they need out of an interface and the implementers (plural) of that interface are bound to provide it. – Marjan Venema Jun 21 '13 at 19:47
  • Bob Martin was not in the [Gang of Four](http://c2.com/cgi/wiki?GangOfFour) and the Gang of Four did not identify or document the SOLID principles. – jaco0646 Sep 21 '15 at 17:48
9

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:

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(), B() and C():

But that is turning the world upside down.

  • A class implementing an interface does not dictate what it needs in the interface it is implementing.
  • The interfaces dictate what methods an implementing class should provide.
  • The callers of an interface really are the ones that dictate what functionality they need the interface to provide for them and thus what an implementer should provide.

So when you are going to split IFat into smaller interface, which methods end up in which ISmall interface should be decided based on how cohesive the members are.

Consider this interface:

interface IEverythingButTheKitchenSink
{
     void DoDishes();
     void CleanSink();
     void CutGreens();
     void GrillMeat();
}

Which methods would you put in ICook and why? Would you put CleanSink together with GrillMeat 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 IClean
{
     void DoDishes();
     void CleanSink();
}

interface ICook
{
     void CutGreens();
     void GrillMeat();
}

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.

Marjan Venema
  • 8,151
  • 3
  • 32
  • 35
  • 1
    Can you see the update I made? – EdvRusj Jun 21 '13 at 20:39
  • *" the caller gets an immediate dependency on the implementer*" ... only if you violate DIP (dependency inversion principle), if caller internal variables, parameters, return values, etc are of type `ICook` instead of type `SomeCookImplementor`, as DIP mandates, then it doesn't have to depend on `SomeCookImplementor`. – Tulains Córdova Jun 21 '13 at 20:57
  • @user61852: If interface declaration and implementer are in the same unit, I do immediately get a dependency on that implementer. Not necessarily at run time, but most certainly on the project level, simply by the fact that it is there. The project can no longer compile without it or whatever it uses. Also, Dependency Injection is not the same as the Dependency Inversion Principle. You might be interested in [DIP in the wild](http://martinfowler.com/articles/dipInTheWild.html) – Marjan Venema Jun 22 '13 at 09:24
  • I re-used your code examples in this question http://programmers.stackexchange.com/a/271142/61852, improving it after it was already accepted. I gave you due credit for the examples. – Tulains Córdova Jan 26 '15 at 15:06
  • Cool @user61852 :) (and thanks for the credit) – Marjan Venema Jan 26 '15 at 18:56
  • What if someone later comes along who only wants to do dishes, _or_ only clean the sink? Is a bunch of 1-method interfaces an anti pattern? – The_Sympathizer Mar 18 '22 at 11:05
5

ISP is all about isolating the client from knowing more about the service than it needs to know (protecting it against unrelated changes, for example). Your second definition is correct. To my reading, only one of those three articles suggests otherwise (the first one) and it is just plain wrong. (Edit: No, not wrong, just misleading.)

The first definition is much more tightly linked to LSP.

pdr
  • 53,387
  • 14
  • 137
  • 224
  • So code examples for the first definition ( see links ) a) don't adhere to ISP, since the whole point of ISP is that we need to make it easier for client to call methods on class MyClass, but in those examples client trying to call A() or B() will need to actually replace a call to MyClass with a call to OtherClass ( OtherClass implements Small_1 ) or b) code examples do adhere to ISP and thus only the reasoning ( ie "make the life of classes implementing IFat interface easier") is wrong? btw - third article also states that clients shouldn't be forced to implement interfaces they don't use – EdvRusj Jun 21 '13 at 17:09
  • 3
    In ISP, clients shouldn't be forced to CONSUME interface components that they don't use. In LSP, SERVICES shouldn't be forced to implement method D because the calling code requires method A. They are not contradictory, they are complementary. – pdr Jun 21 '13 at 17:16
  • 2
    @EdvRusj, the object that implements InterfaceA that ClientA calls may in fact be the exact same object that implements InterfaceB needed by Client B. In the rare cases where the same client needs to see the same object as different Classes, the code won't usually "touch". You'll be looking at it as an A for one purpose and a B for the other purpose. – Amy Blankenship Jun 21 '13 at 17:20
  • I thought adhering to ISP also meant that even after breaking MyService methods into separate interfaces, these interfaces should always be implemented by MyService, since even after the refactoring, MyClass should still make calls to the same object ( ie MyService ). But instead ISP also allows for these interfaces to be implemented by classes other than MyService ( in which case MyClass will need to replace the calls to MyService with a call to some other class? – EdvRusj Jun 21 '13 at 17:49
  • @Amy Blankenship: Please elaborate what you mean by "the code won't usually touch"? – EdvRusj Jun 21 '13 at 17:50
  • 1
    @EdvRusj: It might help if you rethink your definition of interface here. It's not always an interface in C#/Java terms. You could have a complex service with a number of simple classes wrapped around it, such that client A uses wrapper class AX to "interface" with service X. Thus, when you change X in a way that affects A and AX, you are not forced to affect BX and B. – pdr Jun 21 '13 at 17:56
  • Ok, this I understand but am I also correct that ISP allows to replace service x with service y ( which implements AX and as such client A uses AX to call into y) and with service z ( which implements BX and as such client B uses BX to call into z )? – EdvRusj Jun 21 '13 at 18:01
  • 1
    @EdvRusj: It would be more accurate to say that A and B don't care if they're both calling X or one is calling Y and the other calling Z. THAT is the fundamental point of ISP. So you can choose which implementation you go for, and easily change your mind later. ISP doesn't favour one route or the other, but LSP and SRP might. – pdr Jun 21 '13 at 18:08
  • 1
    @EdvRusj No, client A would be able to replace Service X with Service y, both of which would implement interface AX. X and/or Y may implement other Interfaces, but when the Client calls them as AX, it doesn't care about those other Interfaces. – Amy Blankenship Jun 21 '13 at 18:26
  • @pdr, the way you're defining Interface above is more like Decorator. And you could well use Decorator if your Service doesn't implement the correct interface and for some reason it's good for it to stay that way. – Amy Blankenship Jun 21 '13 at 18:28
  • @AmyBlankenship: Nah, it's more of a Facade than a Decorator. A Decorator requires the same method signature, so that you can wrap multiple Decorators around an implementation without the calling code knowing anything about it. A Facade is a simpler interface into a complex service. But patterns are often used to fulfil SOLID principles, so its being a pattern is not a contradiction to its following ISP. – pdr Jun 22 '13 at 08:43
  • It's not a contradiction because it's a pattern. It's a contradiction because the OP's definition of Interface is more like what Interface Segregation means than what you said in your comment. – Amy Blankenship Jun 22 '13 at 15:37
  • @Amy Blankenship : Could you please point to a comment ( made by pdr ) that you find problematic? – EdvRusj Jun 22 '13 at 18:01
  • 1
    @EdvRus "It might help if you rethink your definition of interface here..." Interface segregation definitely is referring to the API of the Class, not a layer on top of the Class that provides access Though you could provide a layer that provides the correct Interface and passes through to a different Class, the fact that the outer Class is mediating access to an inner Class is completely unrelated and invisible to the fact that it is implementing whatever Interface it is providing. – Amy Blankenship Jun 22 '13 at 19:38
  • @AmyBlankenship: Uncle Bob's original article on the ISP has quite explicit sections on "separation through multiple inheritance" (it was written around C++, but this is equivalent to using interfaces, in Java and C#) and "separation through delegation" (using Adapters/Facades). They are both interfaces in the sense that the word interface is used in the name Interface Segregation Principle. http://www.objectmentor.com/resources/articles/isp.pdf – pdr Jun 23 '13 at 10:03
  • But the interface that is talked about is "above the line" of the implementation details that are provided as _examples_ of how you might implement this principle. But the whole point of these principles is that they are in place to make the implementation details irrelevant to other code that touches the code under discussion. – Amy Blankenship Jun 23 '13 at 16:22
  • @AmyBlankenship: Sorry, in that case, I don't understand what you're arguing with. My point was exactly that. I was simply explaining that it might be harder to understand if you stick to the rigid definition of interface as per the keyword in C#/Java -- a concept that barely existed when ISP was defined. The interface in "interface segregation principle" is related to but does not directly equate to the interface in "Class A implements interface I". – pdr Jun 23 '13 at 17:08
0

The question refers to articles that misstate the Interface Segregation Principle. The principle is not

clients should not be forced to implement interfaces they don't use.

It is, as originally defined

CLIENTS SHOULD NOT BE FORCED TO DEPEND UPON INTERFACES THAT THEY DO NOT USE

Apparently dozens upon dozens of articles have copied their definition of the principle from one another, not from the source.

The client of an interface is not a class that implements an interface. It is a class that depends on an interface.

To say that a class should not implement functionality it does not need doesn't make sense. (If it did, wouldn't we just delete that dead code?) A class doesn't exist because it needs its own functionality. It exists because other classes - its clients - depend on its functionality.

I recommend reading the original paper. One of its points is that if an interface exposes some members used by certain clients and certain members used by other clients, those clients become in a sense coupled together. We may make changes to the interface and its implementation to satisfy the needs of one client, and in doing so we change code on which other clients depend.

A perfect example of this is the giant ISomethingService/SomethingService interface and class we see in many projects. Everything depends on it. Because everything depends on it we need to change it often, and the potential blast radius is larger because everything depends on it. We wouldn't have that problem if the interface and its implementation were segregated into smaller interfaces and classes that served the needs of different clients.

I can't count how many Java and .NET articles on the subject describe classes which implement an interface partially, throwing exceptions for methods which are not implemented. Of course we shouldn't do that. But that's not the Interface Segregation Principle. It's not even close.

Implementing an interface and calling its members as a client are two entirely different things. If we replace the ISP as defined with the modified version (don't implement unneeded methods) we still end up with some decent guidance, but it's redundant. It was already covered by other principles. Meanwhile the benefits of the ISP as originally defined are obscured or lost.

Scott Hannen
  • 989
  • 1
  • 6
  • 13
  • Yes, and then I suppose that means having a lot of one/two-method mini-service interfaces makes sense, right, so you can piecemeal out all that functionality as need be, and you can implement them with as many or as few objects in the back end as it logically makes sense to (e.g. via SRP and the like)? – The_Sympathizer Mar 18 '22 at 11:15