Open Close Principle
Bertrand Meyer is generally credited for this principle, he introduce it in his 1988 book titled "Object Oriented Software Construction".
"Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification."
This principle is generally oriented to a scenario where there is inheritance. The idea behind this principle is to establish a way to safegard the correct function of a program preventing possible breaking changes in the classes that are used for inheritance (base clases, parent classes).
"Closing" parent classes ensure that wherever those classes are used, it wont alter it's previous behavior. "Opening" them means it's possible to add new fields or methods to them.
But there is more to this than just simple safegards. Enters the Polymorphic open-closed principle.
What the heck?
With the introduction of interfaces and abstract base clases, the principle became a new monster, defining how the Object Oriented Design should be applied to enforce a more structured and expandable solution.
For example, let's see the following case. We need a class to draw different shapes.
Let's see an example of our ShapeManager class
public class ShapeManager {
public enum ShapeType
{
Circle,
Square
}
public class Shape
{
public ShapeType shapeType;
}
public class Circle : Shape
{
double radius;
}
public class Square : Shape
{
double sideDimension;
}
public void DrawCircle(Circle circle)
{
// here you draw the circle
}
public void DrawSquare(Square square)
{
// here you draw the square
}
public void DrawAllShapes(List shapes)
{
foreach (var shape in shapes)
{
switch (shape.shapeType)
{
case ShapeType.Circle:
DrawCircle((Circle)shape);
break;
case ShapeType.Square:
DrawSquare((Square)shape);
break;
default:
break;
}
}
}
}
So what? It works, rigth?
Well yes, it does..but if in the future you need to add a new Shape, you will need to modify the DrawAllShapes method to add support for the new Shape. Meaning that the ShapeManager class will not be really closed
How to do it correctly then?
Here's an example.
public class ShapeDraw {
public enum ShapeType
{
Circle,
Square
}
public interface IShape
{
public void Draw();
}
public class Circle : IShape
{
double radius;
public void Draw()
{
DrawCircle();
}
public void DrawCircle()
{
// here you draw the circle
}
}
public class Square : IShape
{
double sideDimension;
public void Draw()
{
DrawSquare();
}
public void DrawSquare()
{
// here you draw the square
}
}
public void DrawAllShapes(List shapes)
{
foreach (var shape in shapes)
{
shape.Draw();
}
}
}
As you see, using this approach you will not have to modify the ShapeManager class to add new Shapes, therefore we can consider the class "closed". Of course, the class is "Open" because we can easily inherit from it and add new functionalities (draw the Shape in 3D, for example)
Not only that, we can use delegates for the drawing methods and inject them so each Shape object can have it's own drawing mechanism even if share the same type.
Interesting!