4

In Chapter 10 of Clean Architecture, Martin gives an example for the Interface Segregation Principle. I have some trouble understanding that example and his explanations.

In this example we have three separate Users (Classes) that use a Class called OPS. OPS has three methods, op1, op2, and op3. Each of these is only used by one user (op1 only by User1 and so on).

Martin now tells us that any change in OPS would result in a recompilation for the other classes since they all depend on OPS, even if the change was performed in a method that is of no interest to them. (So a change in op2 would require a recompilation of User1.)

He argues that thus there should be three separate interfaces, one for each method. The OPS class then implements all of them. The users only use the interface they use. So you have User1 implementing only Interface1 and so on.

According to Martin, this would stop the otherwise necessary redeployment of, say, User1 if the implementation of ops2 in OPS was changed (since User1 does not use the interface that describes op2).

I had my doubts and did some testing. (Martin explicitly used Java for his example, so I did as well.) Even without any interfaces any change in OPS does not cause any user to be recompiled.

And even if it did (which I thought it would), using three interfaces and then having the same class implement all three of them makes no sense to me either. Wouldn't any change in that class require all of the users to be recompiled, interface or no? Is the compiler smart enough to separate where I did my changes and then only recompile those users that rely on the interface describing the method I changed? I kind of doubt that.

The only way how this principle makes sense to me is if we were to split the OPS class into three different classes, interfaces or no. That I could understand, but that's explicitly not the answer Martin gives.

Any help would be greatly appreciated.

  • are you using dynamic linking to link the User to OPS? – Ewan Jan 1 at 16:42
10

In that particular example the use of Java as compared to C++ does indeed hide the benefit of interfaces. Interfaces (or more generally, late binding) are useful to break direct dependencies.

Direct dependency    Indirect dependency
=================    ===================

+---------+              +-----------+
| Service |              | Interface |
+---------+              +-----------+
     ^                     ^        ^
     |                     |        |
 +--------+          +---------+ +--------+
 | Client |          | Service | | Client |
 +--------+          +---------+ +--------+

However, Java already uses some amount of late-ish binding: the .class files that the Java compiler produces are not linked in a meaningful way. Instead, the kind of linking in the sense of C++ happens later during runtime when the JVM loads the .class file. Therefore, the focus on Java's compilation model is misleading here. Furthermore, a focus on avoiding recompilation would mean that the ISP would be irrelevant for interpreted language implementations, which is nonsensical.

To be clear: breaking direct dependencies through polymorphism (and applying the ISP more carefully) does have significant effect on compile times in ahead-of-time compiled systems (such as nearly all C++ implementations). This is also related to C++ specific idioms such as pImpl.

The much more universal ISP benefit is that by avoiding unneeded dependencies, the system becomes

  • easier to understand;
  • easier to test;
  • easier to change.

One kind of unneeded dependency is the dependency of a client on a specific service implementation, which can be avoided via polymorphism/interfaces (compare the Dependency Inversion Principle). Another unneeded dependency is the dependency of a consumer on extra methods in an interface. Keeping interfaces small and segregated minimizes these extra dependencies.

The main takeaway from the ISP for me is that

  1. Interfaces should be defined by how an object is used, not by how the object is implemented. It is preferable to define an interface at the point of its usage, and to then implement this interface (possibly by implementing an adapter to an existing class).
  2. Small interfaces are basically the Single Responsibility Principle applied to interfaces.
  • 1
    Right, bringing recompilation to the table in an explanation of ISP is just confusing and totally irrelevant. I suspect the author's point was about breaking interfaces, which you do not want to do once they are out there and users of your library depend on them. Then having lots of small interfaces will be less disruptive than having one big one. Say you have op1, op2, op3 and op4 and you have to change op4 for some reason, you will make user4 unhappy whereas with one big broken interface you would have had 4 unhappy users. – Martin Maat Jan 1 at 17:21
  • Thanks @amon, I was thinking about C++ too and was surprised to see that Java didn't recompile the classes that depended on the changed ones - now I even know why :) Those broader reasons of the ISP were something I already suspected, I was wondering why he didn't talk about it. Your explanation makes a lot of sense. Thank you very much. – zalaponia Jan 1 at 17:36
  • This is not completely accurate, late binding does not remove the need for recompilation. Overload resolution is an example of something that happens at compile time, if you added a new overload to a class without recompiling the clients, you will end up calling the wrong method at runtime. – casablanca Jan 1 at 17:59
  • Also it doesn't matter whether a language is interpreted or not, what matters is whether it uses static typing or not. Java and C++ are both statically typed, and ISP is equally useful in both languages. On the other hand, it wouldn't matter in a dynamically typed language such as Ruby or Python because method dispatches are resolved at runtime. – casablanca Jan 1 at 18:03
  • @casablanca while that is true humans still don't react well to interfaces that provide more than they need. It's nice when constructors and method signatures make real needs clear. – candied_orange Jan 1 at 20:41
2

Note that even if you don't "implement" an interface, the public members of your class form an implicit interface.

I had my doubts and did some testing. (Martin explicitly used Java for his example, so I did as well.) Even without any interfaces any change in OPS does not cause any user to be recompiled.

This is true only if you did not change the public interface of OPS, i.e. you only changed implementation details. If you added, removed or changed the signature of any public methods, it could potentially break existing consumers and any good build tool would recompile your user classes.

Wouldn't any change in that class require all of the users to be recompiled, interface or no?

If your clients are only coupled to an interface, they don't need to be recompiled as long as the interface doesn't change. If OPS changes in a way that it no longer correctly implements an interface, it causes a compile error in OPS, not in the user classes.

The only way how this principle makes sense to me is if we were to split the OPS class into three different classes, interfaces or no.

Think of a class as "how something is implemented" and an interface as "what I can do with it". It might make sense for a single class to implement three interfaces if the implementation happens to provide the services of all three interfaces. No point in splitting it out into three classes in that case.

  • "If you added, removed or changed the signature of any public methods, it could potentially break existing consumers and any good build tool would recompile your user classes." I would likely have to rewrite the code in that case anyway since the signature changed. – zalaponia Jan 1 at 19:28
  • 1
    @zalaponia The point is that the compiler doesn't know whether your change is compatible or not, a recompilation of user classes is required to determine that. But if you have an interface, your user classes aren't directly coupled to OPS and will not need to be recompiled unless the interface changes. – casablanca Jan 1 at 20:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.