Let's see an example of a violation to Interface Segregation Principle. Since Car examples are becoming popular, we will continue with a vehicle example.
The class Car needs to have reference to its passengers: depending on the particular vehicle, it will transport 4 or more people and it must check during construction that it is not overloaded in weight and number of passengers since it would be not secure to drive over cartain limits. We have a class People ready who acts as a collection of Person objects; so, to load a car with People, the following method is used:
public function load(People $p);
We do not want to tie Car to People, which has in turn other dependencies, so we start with extracting an interface:
public function load(IPeople $p);
Hungarian notation is a smell that something. IPeople has many methods, the same of the People concrete class: getWeight(), remove($i), add(Person $p), and a bunch of other functions which Car will never call. What does Car need to know? This is the question that needs an answer: Car needs only a count() method to avoid being overloaded, and a getWeight() method to calculate acceleration and other physical variables. We put these two methods in an interface which will be implemented by People, deleting the awful IPeople component.
public function load(Passengers $p);
In this design, Car depends on the smallest possible interface, Passengers. An interface with a name that does not derive from its implementations is a sign that we are on a good path. Even if People has to be decoupled from Passengers interface, I strongly suggest to write an adapter implementing Passengers, which will wrap a People instance.
The important part is that Car is subjected only to variations to the method which really use, that is actually the minimum coupling introduced in the application. If such a small interface does not exist, it has to be created via extraction from the previous one.
Please note that the same issues are present in abstract base classes: although they cannot be broken down in tiny pieces since multiple inheritance is forbidden in most languages, providing every possible method in a base class can quickly transform it in a God object, and that's exaclt what we must avoid. Helpers and delegations can be used instead when not every subclass will actually need the parent's method.
Small interfaces respect the SRP, and can be combined in many ways. They can also be implemented by the same object, like many classes does in php with Countable and Iterator. Fortunately these two interfaces are separated and allow an Iterator who does not know its length to work.
The advantage of less polluted interfaces is also in simplicity of implementation: less methods result in less tests and less interaction which can cause bugs; also, the classes derived will be much more cohesive as they take only one responsibility to manage from the chosen interface.
The testing point of view results in a easy win for small interfaces. How do you know what to mock when a 20+ methods interface or base class is passed in the constructor of the system under test? When the interface has two or three methods, there is little choice in what can be called by the SUT and you can produce mocks without having to know the internals of it (which of the 20+ methods will be called at what time). Probably in such a case you will end up using a concrete class instead of a mock, transforming your unit tests in integration ones.
You will be satisfied of keeping methods to a minimum while producing a self-shunting also for testing purposes.
I think you are now at a good point in our journey in the principles of object-oriented development. Interfaces are a great decoupling tool and should be used at their full potential. Stay tuned for the next part, on Dependency Inversion (and not Injection).
The image at the top is a Swiss Army Knife. How would you define an interface for one and someone will implement it? Do you prefer a real toolbox?