Hard Rocking With the Interface Segregation Principle
Segregating interfaces is not about ''small is beautiful.'' It's about consciously choosing what methods should your class depend on.
Join the DZone community and get the full member experience.Join For Free
Our systems seem safe when it comes to different requirement sources and adding new features. Privately, you’re safer than ever, as King Benedictus promised to protect you. Your life is so beautiful and so peaceful. At least until someone asks you about deploying some newly developed feature. Worry not, here comes the Interface Segregation Principle!
Tony Loves Hard Rock(s)
It’s a calm December morning. You come to the office, make yourself a coffee and sit by your desk. You turn on the computer.
“Ouch! What the DLL Hell is this?”
You got hit by a piece of something. Maybe a rock. Someone’s having a crazy day.
“Ouch! Who is that? Ouch!”
You turn around.
“Ouch! Ouch! Tony, stop! What the Haskell is wrong with you?!”
“It’s not me. As you wrote in your last email: 'It’s the monolith.' You’re going to suffer from pieces of mine until you get done with yours. I don’t want to wait for my new reports until the whole system gets released, “because it’s a monolith.” I don’t give a damn, understood?!”
The Need For Dependency Management
There’s a lot of things to consider if we want to achieve a well-structured monolith with independently deployable modules. One of those, which is absolutely essential, is good dependency management. What I mean by this is not just using Maven, Gradle, or some other tool that allows us to specify dependencies. I mean that we consciously decide what each part of the system can depend on.
One of the reasons why it’s so important, which is especially visible in large monolithic systems, is this simple truth:
If module A depends on module B, then, if module B changes, then module A might need to be adjusted, rebuilt, and redeployed.
Imagine that report generation is one of many things that depend on some “general purpose” services, that you might find in a lot of legacy applications:
Now, let’s say that when adding a new report, you have to modify that “general purpose” service to provide data for the report that you want to generate. Consider the diagram above. Do you see what’s happening? By changing the CustomerService, you’re forcing a potential rebuild and redeployment of the remaining five modules! Likewise, change to the service introduced to satisfy one of the other modules will require rebuild and redeployment of report generation!
Interface Segregation Principle
This is the moment when Interface Segregation Principle comes in:
No client should be forced to depend on methods that it does not use.
What it tells us is that we should find a way to NOT depend on things that we’re not interested in, so that those other things can change independently. In our example, we’re interested in stuff necessary for report generation and we do not want to depend on stuff that is necessary for any of the other modules. Likewise, and maybe even more important in this case, we do not want the other modules to depend on stuff necessary for report generation, because we want to extend this independently!
Applying Interface Segregation
There are two ways in which we might apply interface segregation:
- Splitting a class into smaller ones with segregated interfaces.
- Extracting a segregated interface.
The first approach would be desirable in situations, where ISP violation is connected to an SRP violation – in our example, the CustomerService most likely has more than one reason to change. In such case, we’d adhere to both of the rules with the same refactoring of extracting a dedicated class:
But sometimes, it’s not desirable or even possible to split a class. In such cases, we can apply ISP by extracting interfaces that contain only those methods that are used by a given module:
This might look a bit overwhelming, and it certainly would be in the code, but it works. Our modules depend only on things that they really use and the only things that have to be rebuilt and redeployed due to changes are the affected modules and the CustomerService implementation.
I think I don’t have to say that the first approach is simpler and favorable when possible. The second one does the job, but it might cause the CustomerService to grow indefinitely. Also, the amount of interfaces created for the sole purpose of segregation grows substantially.
That’s it! Now, you’re at least one step closer to making your monoliths more manageable. For the time being, I recommend getting a helmet, because Tony is unlikely to give up! I also hope that there are fewer people around thinking that ISP is about changing big interfaces into smaller ones, because “smaller is nicer to read.” Well, maybe it is, but the dependency management argument should be much more compelling. Thanks for reading, take care!
Published at DZone with permission of Grzegorz Ziemoński, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.