- No clients should be forced to depend on methods they do not use.
- This often becomes a problem when
clients
have dependencies on what we call those fat classes (classes that have too many responsibilities) - Having an interface to separate behavior is a good idea, and it prevents tight coupling between between the objects.
-
In this logic, what if the
Substitude Teacher
is not allowed to teach (maybe monitoring students are their responsibility) . Teaching in this case is the responsibility of the main teacher. -
In this case, a violation of the
Interface Segregation Principle
would be to have a substitute teacher or inherit from the teacher class because as as as for our definition, which means theSubstituteTeacher
also have to implement the teach() method (like the diagram below). However in our logic, Substitute teachers don't actually teach. So this does not fall into this particular hierarchy because it is not really a teacher as per our definition.
We can however handle this probem by throwing a customer CannotTeachException
. So we're actually throwing this cannot teach exception in the body of the teach method like so.
public class CannotTeachException extends Exception {
}
public class SubstitudeTeacher extends Teacher {
@Override
public void teach() {
throw new CannotTeachException();
}
}
This is BAD because we have to add the exception to the teacher class like so.
public abstract class Teacher {
public abstract void teach() throw CannotTeachException; // <- Forcing changes on parent class by having to add an exception here
private void makeAnnoucements() {
...
}
...
public void performOtherResponsibilites() {
makeAnnoucements();
takeAttendence();
...
}
}
- Now notice how the subclass is forcing change in its parent. Now in the definition of this abstract method we would actually have to throw the new exception. So this is unacceptable we should never force upon a parent class changes because things don't work out in the subclasses
- Throwing an exception like this is an
Anti-Pattern
.
- The solution to this whole mess would be to pull out a substitute teacher from this inheritance relationship because it simply is not a teacher as per our definition of what a teacher what a real teacher whose behavior should be.
- We broken out the responsibility of teaching to a particular interface called CourseInstructor and the general responsibilities that all high school staff members should have is encapsulated in the
SchoolStaff
abstract class.
In this school staff abstract class:
public class SchoolStaff extends Employ {
private void makeAnnouncements() {
System.out.println("made announcements..");
}
private void takeAttendence() {
System.out.println("took attendence..");
}
private void collectPaperWork() {
System.out.println("collected paperwork..");
}
private void conductHallwayDuties() {
System.out.println("conducted hallway duties..");
}
public void performOtherResponsibilities() {
makeAnnouncements();
takeAttendence();
collectPaperWork();
conductHallwayDuties();;
}
}
public interface CourseInstructor {
public void teach();
}
public class EnglishTeacher extends SchoolStaff implements CourseInstructor {
@Override
public void teach() {
System.out.println("Taught English");
}
}
public class MathTeacher extends SchoolStaff implements CourseInstructor {
@Override
public void teach() {
System.out.println("Taught Math");
}
}
public class ScienceTeacher extends SchoolStaff implements CourseInstructor {
@Override
public void teach() {
System.out.println("taught science");
}
}
public class SubstituteTeacher extends SchoolStaff {
}
Interface Segregation Principle
is all about segregating interfaces and make them smaller. Whenever you have an Interface, you need everything that implements that Interface to use every portion of the Interface.
You know when you are violating ISP
, when a method is not meant to be called on an Object.
In our example a SubstituteTeacher
was not really a teacher so it didn't make sense for us to be calling the teach()
method on that object.
Eg.
- It doensn't make sense for a
Turret
Object to be able to access methods such as move even though move could be left unimplemented in theturret
Object. Hence, theTurret
Object should only inherit theGameObject
interface, and not inherit theMoveable
interface.