Solid Principles: Liskov Substitution Principle
Learn about the SOLID principles for programming (or refresh your memory) with this in-depth look at the Liskov Substitution Principle.
Join the DZone community and get the full member experience.
Join For FreePreviously we took a dive into solid principles including the single responsibility and the open/closed principle. The Liskov substitution principle (LSP) is a particular definition of a subtyping relation, called (strong) behavioral subtyping,
Supposing object S is a subtype of object T, then objects of type T may be replaced with objects of type S without altering any of the desirable properties of T.
Suppose we have the Employee class.
package com.gkatzioura.solid.liskov;
public class Employee {
public String getTitle() {
return "The employee's title";
}
public void work() {
System.out.println("Employee is working");
}
}
Also, we have another class which inherits the Employee class.
package com.gkatzioura.solid.liskov;
public class EmployeeOnVacation extends Employee {
@Override
public void work() {
throw new IllegalArgumentException("Employees on vacation should not work");
}
}
Supposing that we have a project.
package com.gkatzioura.solid.liskov;
import java.util.List;
public class Project {
public void start(List<Employee> employees) {
for(Employee employee:employees) {
employee.work();
}
}
}
And we assign our employees to start working on it
List<Employee> employees = new ArrayList<>();
employees.add(new EmployeeOnVacation());
employees.add(new Employee());
Project project = new Project();
project.start(employees);
The outcome would be an exception due to the employee who is on vacation and thus the project will not be completed.
The employee on vacation is an employee, however, he will not work. Even if the method didn't throw an exception the method work would do nothing and this would affect delivering our project since our actual team's velocity is not the one we originally thought it was.
In order to avoid violating the principle, we shall use a different approach and change the class hierarchy.
We will change the original employee class.
package com.gkatzioura.solid.liskov;
public class Employee {
public String getTitle() {
return "The employee's title";
}
}
And we will make two different employee interfaces, the WorkingEmployee interface:
package com.gkatzioura.solid.liskov;
public interface WorkingEmployee {
public void work();
}
And the non-working employee interface:
package com.gkatzioura.solid.liskov;
public interface NonWorkingEmployee {
void relax();
}
Then the project will use only employees who are implementations of the WorkingEmployee interface and extend the employee class.
package com.gkatzioura.solid.liskov;
public class WorkingEmployeeImpl extends Employee implements WorkingEmployee {
@Override
public void work() {
}
}
package com.gkatzioura.solid.liskov;
import java.util.List;
public class Project {
public void start(List<WorkingEmployee> workingEmployees) {
for(WorkingEmployee workingEmployee:workingEmployees) {
workingEmployee.work();
}
}
}
List<WorkingEmployee> employees = new ArrayList<>();
employees.add(new WorkingEmployeeImpl());
Project project = new Project();
project.start(employees);
You can find the source code on GitHub. The next principle is the interface segregation principle.
Published at DZone with permission of Emmanouil Gkatziouras, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments