Open/Closed Principle

1 min read
On trails:CSSOLIDstep 3/6

Software entities (classes, modules, functions and so on) should be open for extension but closed for modification. That means you should be able to add new functionality or behavior to a system without altering existing, tested code.

In C#

An AreaCalculator class that needs modification, for example adding if-else blocks, to support new shapes like Circle in addition to Rectangle violates OCP. You achieve OCP by creating an abstract Shape base class (or interface) with an Area() method. Rectangle and Circle then inherit from Shape and implement their own Area() logic. The AreaCalculator can then work with a collection of Shape objects, without needing changes when new shapes are introduced.

First, the violation. Every new shape means another if in the calculator.

C#
// Violation of OCPpublic class AreaCalculator_Bad{    public double CalculateTotalArea(object[] shapes)    {        double totalArea = 0;        foreach (var shape in shapes)        {            if (shape is Rectangle r) totalArea += r.Width * r.Height;            if (shape is Circle c) totalArea += c.Radius * c.Radius * Math.PI; // Added later        }        return totalArea;    }}

Then the version that follows OCP. The abstract Shape comes first, and the calculator never changes for a new shape.

C#
// Adherence to OCPpublic abstract class Shape { public abstract double Area(); }public class Rectangle : Shape{    public double Height { get; set; }    public double Width { get; set; }    public override double Area() { return Height * Width; }}public class Circle : Shape{    public double Radius { get; set; }    public override double Area() { return Radius * Radius * Math.PI; }}public class AreaCalculator_Good{    public double TotalArea(Shape[] arrShapes)    {        double area = 0;        foreach (var objShape in arrShapes)        {            area += objShape.Area(); // No modification needed for new shapes        }        return area;    }}

Where it matters

OCP improves stability and maintainability by minimizing the risk of introducing bugs into existing, working code. It encourages the use of abstractions (interfaces, abstract classes) and polymorphismPolymorphismOne call, many behaviors, decided by the actual type at runtime.Opens in a new tab. See interfaces versus abstract classesInterfaces versus abstract classesTwo ways to define what a type must offer, and when each one fits.Opens in a new tab.


Sources