Is interface segregation principle only a substitue for single responsibility principle ?
I think that if my class fulfill SRP there is no need to extract more than one
No. Take the example of a class whose responsibility is persisting data on e.g. the harddrive. Splitting the class into a read- and a write part would not make practical sense. But some clients should only use the class to read data, some clients only to write data, and some to do both. Applying ISP here with three different interfaces would be a nice solution.
No.
A class can implement multiple interfaces but it should implement the methods only applicable to it.
Assume that you have 10+ different capabilities like Climb, Think, Learn, Apply
. Class Dog
can have 2 capabilities and class Cat
can have 2 capabilities and class Man
can have 6 capabilities. It makes sense to implement only applicable capabilities in respective classes.
Have a look at this code.
public class ISRDemo{
public static void main(String args[]){
Dog dog = new Dog("Jack",16);
System.out.println(dog);
Learn dl = dog;
dl.learn();
ProtectOwner p = dog;
p.protectOwner();
Cat cat = new Cat("Joe",20);
System.out.println(cat);
Climb c = cat;
c.climb();
Remember r = cat;
cat.doRemember();
Man man = new Man("Ravindra",40);
System.out.println(man);
Think t = man;
t.think();
Learn l = man;
l.learn();
Apply a = man;
a.apply();
PlaySports pm = man;
pm.playSports();
Remember rm = man;
rm.doRemember();
}
}
class Dog implements Learn,ProtectOwner{
private String name;
private int age;
public Dog(String name,int age){
this.name = name;
this.age = age;
}
public void learn(){
System.out.println(this.getClass().getSimpleName()+ " can learn");
}
public void protectOwner(){
System.out.println(this.getClass().getSimpleName()+ " can protect owner");
}
public String toString(){
return "Dog :"+name+":Age:"+age;
}
}
class Cat implements Climb,Remember {
private String name;
private int age;
public Cat(String name,int age){
this.name = name;
this.age = age;
}
public void climb(){
System.out.println(this.getClass().getSimpleName()+ " can climb");
}
public void doRemember(){
System.out.println(this.getClass().getSimpleName()+ " can remember");
}
public String toString(){
return "Cat :"+name+":Age:"+age;
}
}
interface ProtectOwner {
public void protectOwner();
}
interface Remember{
public void doRemember();
}
interface Climb{
public void climb();
}
interface Think {
public void think();
}
interface Learn {
public void learn();
}
interface Apply{
public void apply();
}
interface PlaySports{
public void playSports();
}
class Man implements Think,Learn,Apply,PlaySports,Remember{
String name;
int age;
public Man(String name,int age){
this.name = name;
this.age = age;
}
public void think(){
System.out.println(this.getClass().getSimpleName() + " can think");
}
public void learn(){
System.out.println(this.getClass().getSimpleName() + " can learn");
}
public void apply(){
System.out.println(this.getClass().getSimpleName() + " can apply");
}
public void playSports(){
System.out.println(this.getClass().getSimpleName() + " can play sports");
}
public void doRemember(){
System.out.println(this.getClass().getSimpleName() + " can remember");
}
public String toString(){
return "Man :"+name+":Age:"+age;
}
}
output:
java ISRDemo
Dog :Jack:Age:16
Dog can learn
Dog can protect owner
Cat :Joe:Age:20
Cat can climb
Cat can remember
Man :Ravindra:Age:40
Man can think
Man can learn
Man can apply
Man can play sports
Man can remember
In above example, interface segregation recommends to define 10 capabilities in 10 interfaces in stead of declaring all of them in fat interface. But it does not mean that you need different classes to meet single responsibility criteria.
Have a look at implementation of Dog, Cat and Man
classes in same example.
I think that if my class fulfill SRP there is no need to extract more than one interface.
Single Responsibility Principle is that a class (or a method) shouldn't have more than one reason to change (i.e. each responsible for just one feature). To honour this, you'll find yourself creating new classes as your system develops.
E.g. if you start with a Car
class & find that you need functionality to change the gears, you'll extract this into a Gearbox
class. This means that if you change the mechanism behind gear changes, the parent Car
class doesn't need to change. If you add power-steering to your car, you'll again extract this into it's own class. The radio would be another class.
This cascade of abstraction will happen throughout your Car
class. As you move from the Car
itself downwards, you'll find that the detail increases in each class — e.g. while the Car
class may have a changeGear()
method to allow the user to select a gear to engage, the Gearbox
class will look after the nitty-gritty of making this happen (e.g. depress the clutch, disengage the current gear, select the new gear, etc.)
With an OO design however, we don't want to expose the details of our Gearbox
to the end user — we want them to interact with our system at a high level of abstraction, without needing to know how the internals work. We also want to ring-fence these internals, so that we can change them in the future without needing the users to refactor their code (which is why we'd flag them as private
or protected
).
Because of this, we let users interact with our car only through the Car
class itself. This is where the Interface Segregation Principle comes in. SRP ensures that the Car
class delegates its sub-components to different classes, but all of our public
methods will still be called through the Car
class itself. ISP ensures that rather than lumping all these together in one interface, we instead create logical distinctions & expose multiple interfaces for related functionality.