My goal is to understand the Interface Segregation Principle and achieve polymorphism at the same time.
My expected result: I can achieve polymorphism with the Interface Segregation Principle.
My actual result: No I can't. I am forced to create boilerplate and use the Liskov Substitution Principle (If there is a Worker, there must be a Worker that can't eat, so create an interface for Worker that can eat that extends Worker). I think I misunderstand the Interface Segregation Principle.
This is the code that violates the Interface Segregation Principle.
public interface IWorker {
void work();
void eat();
}
class Human implements IWorker {
public void work() {
System.out.println("Human is working.");
}
public void eat() {
System.out.println("Human is eating.");
}
}
class Robot implements IWorker {
public void work() {
System.out.println("Robot is working.");
}
public void eat() {
throw new UnsupportedOperationException("Robot cannot eat");
}
}
I was told to separate the interface into 2.
public interface IEatable {
void eat();
}
interface IWorkable {
void work();
}
class Human implements IWorkable, IEatable {
public void work() {
System.out.println("Human is working.");
}
public void eat() {
System.out.println("Human is eating.");
}
}
class Robot implements IWorkable {
public void work() {
System.out.println("Robot is working.");
}
}
The solution is to use the Liskov Substitution Principle.
public interface IWorkable {
void work();
}
interface IEatable {
void eat();
}
interface IWorker extends IWorkable {
}
interface IHumanWorker extends IWorker, IEatable {
}
Your second step looks good, you have split the interface in two more specific interfaces. It does not make sense for a Robot to "eat". (I dont really get the third step)
On the caller side you can now work with your abstractions:
//Polymorphism
List<IWorkable> workers = Arrays.asList(new Robot(), new Human());
//do some work
List<IEatable> eaters = Arrays.asList(new Human(), new Human());
//send for lunch break
If you want to have both behaviours in the same thing, than your abstraction/design seems to be wrong, since Robots cannot eat by definition (indicated by the code smell of not implemented methods).
A Robot is not an IWorker (your first code), because it does not full fill the (full) contract (interface, eat method), no matter how similar it seems to be.