Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification
Bertrand Meyer
This statement was introduced by Bertrand Meyer in 1988. And it basically means that:
- A class is treated as open if it is available for extension. I.e. possible to extend by adding extra attributes or methods.
- A class is treated as closed, if it is available for use to others. That means it should have stable and well defined description. Also it can be compiled and stored in library and still available to reuse by client classes.
Shortly saying, this principal is about class inheritance. Later on, after it was introduced, Robert Martin redefined it as Polymorphic Open/Closed Principle. It means that interfaces are involved to enhance this principal. It benefits the way that extra abstraction level appears which enables loose coupling between parent and child classes. It becomes easy to substitute implementations of parent class without impact to child classes.
The most important principle of object-oriented design.
Robert Martin
Example
Current State
Lets use HR sample and implement a requirement to get annual expenses of Sales department for employees salaries. We have only one type of employees – salesman so far… Here is a current state of implementation:
public class Salesman { public int getMonthlySalary(); public int getAnnualSalesAmount(); } public class SalesDepartment { private Salesman[] salesmans; public int getAnnualEmployeeExpenses() { for (Salesman emp : salesmans){ annualExpenses += emp.getMonthlySalary() * 12 + emp.getAnnualSalesAmount() * 0.05; } return annualExpenses; } }
And now get a change request as Sales department just hired an assistant – Administrator. Our task is to include him to annual expenses counter. Lets do it in a wrong way, as we were never heard of Open/Closed design principle:
Wrong
public class Salesman { public int getMonthlySalary(); public int getAnnualSalesAmount(); } class Administrator { public int getMonthlySalary(); public int getBonus(); } public class SalesDepartment { private Object[] employees; public int getAnnualEmployeeExpenses() { for (Object emp : employees){ if (emp.instanceOf("Salesman")) { Salesman salesman = (Salesman) emp; annualExpenses += salesman.getMonthlySalary() * 12 + salesman.getAnnualSalesAmount() * 0.05; } else if (emp.instanceOf("Administrator")) { Administrator administrator = (Administrator) emp; annualExpenses += administrator.getMonthlySalary() * 12 + administrator.getBonus(); } } return annualExpenses; }
What we did here, is just appended expense calculator method with extra logic which identifies which type of employee it is and calculates expenses accordingly. Apparently when adding further more employee types we need to modify SalesDepartment class. And that means it is not closed for modification.
Now lets do it in correct way by taking into account Polymorphic Open/Closed principle:
Correct
public interface Employee { int getAnnualIncome(); } public class Salesman implements Employee { public in getAnnualIncome() { return getMonthlySalary() * 12 + getAnnualSalesAmount() * 0.05; } public int getMonthlySalary(); public int getAnnualSalesAmount(); } public class Administrator implements Employee { public in getAnnualIncome() { return getMonthlySalary() * 12 + getBonus(); } public int getMonthlySalary(); public int getBonus(); } public class SalesDepartment { private Employee[] employees; public int getAnnualEmployeeExpenses() { for (Employee emp : employees){ annualExpenses += emp.getAnnualIncome(); } return annualExpenses; }
Now we have SalesDepartment class fully corresponding to Open/Closed principle as it is not necessary to modify it adding new type of employee. It is easy to extend by just creating new class which implements Employee interface. The latter adds extra abstraction layer.
Please check for other principles of SOLID in this post – SOLID Software Design Principles. Summary.