SOLID Principles – Open-closed principle
In object-oriented programming, the open–closed principle states “software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification” that is, such an entity can allow its behavior to be extended without modifying its source code.
Wiki – Open closed principle
The open-closed principle (OCP) is one of the SOLID principles which states the entities(classes or methods) should be open for extension but closed for modification. The “O” in “SOLID” represents the open-closed principle. Many developers descript the OCP of the software, then can find out the developer’s description has two versions. If you like to read more books about software or the research paper on computer science, you will know these versions which are Meyer’s open-closed principle and Polymorphic open-closed principle.
The open-closed principle idea started from Object-Oriented Software Construction by Meyer, Bertrand(1988). Meyer describes the open-closed principle below:
- A module will be said to be open if it is still available for extension. For example, adding fields to the data structures or new elements to the set of functions it performs should be possible.
- A module will be said to be closed if it is available for use by other modules. This assumes that the module has been given a well-defined, stable description (the interface in the sense of information hiding).
In Meyer’s book, adding fields or functions to a library inevitably required change to any programs depending on that library Meyer provides the solution to this dilemma relied on the notion of object-oriented inheritance (specifically implementation inheritance).
- A class is closed, since it may be compiled, stored in a library, baselined, and used by client classes. But it is also open, since any new class may use it as a parent, adding new features. When a descendant class is defined, there is no need to change the original or to disturb its clients.
It is not many developers can accept it. Around ten years, a part of computer science researcher proposes another method to descript the OCP and continuously explain the OCP. During the 1990s, the open-closed principle became popularly redefined to refer to the use of abstracted interfaces, where the implementations can be changed and multiple implementations could be created and polymorphically substituted for each other. It is called the “Polymorphic open-closed principle”.
This definition advocates inheritance from abstract base classes which contrasts with Meyer’s usages. The interface object specifications can be reused through inheritance, but the implementation doesn’t need to be. The existing interface object is closed to modifications and new implementations must, at a minimum, implement that interface.
In the modern age, many developers or interview questions answer will prepare to accept the polymorphic open-closed principle; then there think it will be complete answers. May a part of senior developers only know Meyer’s open-closed principle and don’t know the polymorphic open-closed principle or on contrary.
How to implement the open-closed principle (OCP) in the program?
Basic part:
internal class DeveloperReport
{
public int Id { get; set; } = 0;
public string Name { get; set; } = string.Empty;
public string Level { get; set; } = string.Empty;
public int WorkingHours { get; set; } = 0;
public double HourlyRate { get; set; } = 0;
}
public abstract class BaseSalarycalculator
{
protected DeveloperReport DeveloperReport { get; private set; }
public BaseSalarycalculator(DeveloperReport developerReport)
{
DeveloperReport = developerReport;
}
public abstract double CalculateSalary();
}
public class SeniorDevSalaryCalculator : BaseSalarycalculator
{
public SeniorDevSalaryCalculator(DeveloperReport report) : base(report)
{ }
public override double CalculateSalary()
=> DeveloperReport.HourlyRate * DeveloperReport.WorkingHours * 1.2;
}
public class JuniorDevSalaryCalculator : BaseSalarycalculator
{
public JuniorDevSalaryCalculator(DeveloperReport report) : base(report)
{ }
public override double CalculateSalary()
=> DeveloperReport.HourlyRate * DeveloperReport.WorkingHours;
}
public class SalaryCalculatorPro
{
private readonly IEnumerable<BaseSalarycalculator> _developerCalculation;
public SalaryCalculatorPro(IEnumerable<BaseSalarycalculator> developerCalculation)
{
_developerCalculation = developerCalculation;
}
public double CalculateTotalSalaries()
{
double totalSalaries = 0D;
foreach (var devCalc in _developerCalculation)
{
totalSalaries += devCalc.CalculateSalary();
}
return totalSalaries;
}
}
Advanced part:
internal class DeveloperReport
{
public int Id { get; set; } = 0;
public string Name { get; set; } = string.Empty;
public string Level { get; set; } = string.Empty;
public int WorkingHours { get; set; } = 0;
public double HourlyRate { get; set; } = 0;
}
public interface IOCP : IDisposable
{
abstract double CalculateSalary();
}
public abstract class BaseSalarycalculator : IOCP
{
protected DeveloperReport DeveloperReport { get; private set; }
public BaseSalarycalculator(DeveloperReport developerReport)
{
DeveloperReport = developerReport;
}
public abstract double CalculateSalary();
public void Dispose() => GC.SuppressFinalize(this);
}
public class SeniorDevSalaryCalculator : BaseSalarycalculator
{
public SeniorDevSalaryCalculator(DeveloperReport report) : base(report)
{ }
public override double CalculateSalary()
=> DeveloperReport.HourlyRate * DeveloperReport.WorkingHours * 1.2;
}
public class JuniorDevSalaryCalculator : BaseSalarycalculator
{
public JuniorDevSalaryCalculator(DeveloperReport report) : base(report)
{ }
public override double CalculateSalary()
=> DeveloperReport.HourlyRate * DeveloperReport.WorkingHours;
}
public class SalaryCalculatorPro
{
private readonly IEnumerable<BaseSalarycalculator> _developerCalculation;
public SalaryCalculatorPro(IEnumerable<BaseSalarycalculator> developerCalculation)
{
_developerCalculation = developerCalculation;
}
public double CalculateTotalSalaries()
{
double totalSalaries = 0D;
foreach (var devCalc in _developerCalculation)
{
totalSalaries += devCalc.CalculateSalary();
}
return totalSalaries;
}
}
Reference
- SOLID Principles in C# – Open Closed Principle
- Principles Wiki – Open-Closed Principle (OCP)
- Meyer, Bertrand (1988). Object-Oriented Software Construction. Prentice Hall.
- Open Closed Principle