This is one of a number of things that's been bugging me for a while and for which quibbling over the correct interpretation of this has been leading me in a number of attempted coding projects to more fussing around with design, than it has with making steady forward progress, because I want to make sure I've gotten it "right".
In this case, I have a question about the "interface segregation principle" (ISP) of the five "SOLID" principles of basic object oriented design. Its "canonical" statement is this:
Clients should not be forced to depend on methods they do not use.
However, the question here is, is what is the "client" - because this leads to very different interpretations, and also leads to a seeming dilemma when applied in practice, which is what I'd like to understand if first actually is one, and second, how to best solve it.
One interpretation of this is that it is a "single responsibility principle" for interfaces instead of just classes: i.e. if you have an interface IMessageReceiver, it better not include a Send method. Another possible interpretation is that it means that the implementer of an interface should not be required to implement empty methods, while a third interpretation is that the caller should not be able to see any more methods than it actually needs. And the thing is, I can see merit in all three of these interpretations, yet when you apply them all, it seems that in any suitably large project, highly counterintuitive and seemingly problematic things result. In particular, it's that third one that seems to be the rub.
For one, if something gets used in enough places, it is particularly that last one - the "caller" one - which generally tends to "bite" in that it results naturally in your interfaces being atomized down to single methods only. For example, consider a simple interface to a backend storage or database, which may have a load, save, and update method. Some callers of that, though, may not want to save anything. They may just want to peek at the data. Hence to avoid violating the caller interpretation, we must split off the load method. Add a few more use cases and now it's atomized into a IDataLoader, IDataSaver, and IDataUpdater which all have 1 method each.
And I can't help but feel this almost seems like an anti-pattern, particularly if it happens with enough objects owing to them being used in a suitably wide variety of places. Is it? Or am I misinterpreting something here? After all, nobody makes a generic container class (like the "list", "map", etc. things in a lot of languages) with single-method interfaces to be piecemealed out to whatever it gets passed to.
When somebody wants to develop a feature, then he/she tries to map real world object to unit where code can be put. It is done to imitate behavior. This unit can be called class. And this class has to have just one feature. It is place where we use SRP.
E.g., if programmer maps real world object person to class
Person. Then this classPersondoes not have to have methods/behavior of other objects. E.g. thePersonclass does not have to have methods of Car class. This is what SRP about.We've created a
Personclass so far. Now we want to log all speech of person. So we need to log it. How? We can use interfaceILoggingwith one methodLog. Why does this interface have just one method? Because clients will only have to know about the methods that are of interest to them. HerePersonclass is client of interfaceILogging.SRP means that a class is responsible just for one feature. E.g. if you want to edit logging in Person class, then it is violation of SRP.
How can we avoid violation of SRP? We should create an abstraction of logging:
and use it:
By extracting logic of logging in separate class, we moved logic of logging in special, single class or place where we can edit only logic of logging on one place.
What is about ISP?
yeah, you are right. Interface should have only necessary methods which client class needs.
An example with HDD that uses ISP:
By creating one interface for
Read()andWrite()methods, it would obligate class to implement both methods in class. But some classes only want to read data, others want to write data, and some to do both. So in this case it is better to create separate interfaces.So let's look another example with CardReader. CardReader just reads data, it does not write data. So, if we inherit one interface with
Read()andWrite()methods, then we would violate ISP principle. An example of violation of ISP:So by applying ISP, you only puts methods in interface that are necessary for the client class. If your class/client just wants to read data, then you need to use
IReadableinterface, notIReadableWriteable.