In SOLID, what is the distinction between SRP and ISP? (Single Responsibility Principle and Interface Segregation Principle)

OopDesign PatternsSolid PrinciplesSingle Responsibility-PrincipleInterface Segregation-Principle

Oop Problem Overview


How does the SOLID "Interface Segregation Principle" differ from "Single Responsibility Principle"?

The Wikipedia entry for SOLID says that

>ISP splits interfaces which are very large into smaller and more specific ones so that clients will only have to know about the methods that are of interest to them

However, to me that sounds like just applying the SRP to interfaces as well as classes. After all, if an interface is only responsible for just one conceptual thing, than you wouldn't be able to break it down further.

Am I missing something, or is ISP sort of redundant with SRP? If not, then what does ISP imply that SRP does not?

Oop Solutions


Solution 1 - Oop

SRP tells us that you should only have a single responsibility in a module.

ISP tells us that you should not be forced to be confronted with more than you actually need. If you want to use a print() method from interface I, you shouldn't have to instantiate a SwimmingPool or a DriveThru class for that.

More concretely, and going straight to the point, they are different views on the same idea -- SRP is more focused on the designer-side point-of-view, while ISP is more focused on the client-side point-of-view. So you're basically right.

It all came from

> The ISP was first used and formulated by Robert C. Martin when doing > some consulting for Xerox. Xerox had created a new printer system that > could perform a variety of tasks like stapling a set of printed papers > and faxing. The software for this system was created from the ground > up and performed its tasks successfully. As the software grew, making > modification became more and more difficult so that even the smallest > change would take a redeployment cycle to an hour. This was making it > near impossible to continue development. The design problem was that > one main Job class was used by almost all of the tasks. Anytime a > print job or a stapling job had to be done, a call was made to some > method in the Job class. This resulted in a huge or 'fat' class with > multitudes of methods specific to a variety of different clients. > Because of this design, a staple job would know about all the methods > of the print job, even though there was no use for them.

so

> The solution suggested by Martin is what is called the Interface > Segregation Principle today. Applied to the Xerox software, a layer of > interfaces between the Job class and all of its clients was added > using the Dependency Inversion Principle. Instead of having one large > Job class, a Staple Job interface or a Print Job interface was created > that would be used by the Staple or Print classes, respectively, > calling methods of the Job class. Therefore, one interface was created > for each job, which were all implemented by the Job class.

@ http://en.wikipedia.org/wiki/Interface_segregation_principle#Origin

Solution 2 - Oop

SRP is concerned with what a module does, and how it is done, disallowing any mix of abstraction levels. Basically, as long as a component can be extensively defined with a single sentence, it will not break SRP.

On the other hand ISP is concerned with how a module should be consumed, whether it makes sense to consume just part of the module, while ignoring some aspect.

As an example of a code that keeps the spirit or SRP, but can break ISP is the Facade pattern. It has a single responsibility, "providing simplified access to a larger subsystem", but if the underlying subsystem needs to expose wildly different thinks, it does break ISP.

That said, usually when a piece of code breaks a SOLID principle, it often breaks the whole lot. Concrete examples that break a specific principle, while preserving the rest are rare in the wild.

Solution 3 - Oop

Robert Martin tweeted the following on May 16, 2018.

> ISP can be seen as similar to SRP for interfaces; but it is more than that. ISP generalizes into: “Don’t depend on more than you need.” SRP generalizes to “Gather together things that change for the same reasons and at the same times.” > Imagine a stack class with both push and pop. Imagine a client that only pushes. If that client depends upon the stack interface, it depends upon pop, which it does not need. SRP would not separate push from pop; ISP would.

Solution 4 - Oop

SRP and ISP ultimately boils down to the same things. Implementing, either of them, needs a split of classes or interfaces.

However there are differences on other fronts.

  1. Violation of SRP can have a far reaching effects on the entire design structure, giving rise to poor maintainability, reuse and of course low cohesion and coupling.
  2. SRP has an impact on both the behavioral and structural components of an object structure.
  3. Re designing on SRP violation needs a much deeper analysis, require looking at the different components of design in a holistic way.

Violation of ISP is mostly about poor readability ( and to some degree, low cohesion ). But the impact on maintenance and code re-use is far less sinister than SRP.

Moreover, refactoring code to ISP conformation, seems to be just a structural change.

See also my blog for SRP and ISP

Solution 5 - Oop

From the point of my understanding, both principles are complementary, i.e. they need to be combined.

The ultimate consequence of violating ISP is becoming fragile, "shotgun surgury" or a "butterfly effect". A lot of code can break or require code updates because they depend onto some interface or objects which provide more than they needed. Changes become excessive.

The consequence of violating SRP is mainly decreased readability and maintentance. The lack of clear code structure may require people to search across the code base (a single responsibility is too distributed) or within a single large unit (multiple responsibilities scrammed together) to make a coherent change. In General, it is increased overhead to fully understand the concern (purpose) of some code snippet. Changes are prevented.

In that way, both principles act like a lower and upper bound for sane change management.

Examples for satisfying RSP without ISP – as provided by the other answers – express that there can be code which truly would belong together (like the stack example quote from Robert C. Martin). But it may do too much, is overengineered, etc. Maybe in very small examples, the effect is not visible, but if it grows large, it may be more comfortable to have a depending class still compile correctly after some unrelated part in the (indirect) dependency was changed. Rather than not compile anymore because unrelated things were changed.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSledView Question on Stackoverflow
Solution 1 - Oopdevoured elysiumView Answer on Stackoverflow
Solution 2 - OopSWekoView Answer on Stackoverflow
Solution 3 - Oopjaco0646View Answer on Stackoverflow
Solution 4 - OopaknonView Answer on Stackoverflow
Solution 5 - OopChrisoLosophView Answer on Stackoverflow