Principles

Last Updated: 10/7/2024

Open Closed Principle

  • Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.
  • This means the design of a software entity should be such that you can introduce new functionality or behavior without modifying the existing code since changing the existing code might introduce bugs.

Example 1

Problem

internal interface IShape
{
    void Render();
}
internal class Circle : IShape
{
    public double Radius { get; init; }

    public Circle(double radius)
    {
        Radius = radius;
    }

    public void Render()
    {
        Console.WriteLine("Rendering circle");
    }
}
internal class Rectangle : IShape
{
    public double Width { get; init; }

    public double Height { get; init; }

    public Rectangle(double width, double height)
    {
        Width = width;
        Height = height;
    }

    public void Render()
    {
        Console.WriteLine("Rendering rectangle");
    }
}
internal class ShapeCalculator
{
    public void CalculateArea(IShape shape)
    {
        if(shape is Circle circle)
        {
            Console.WriteLine(3.14 * circle.Radius * circle.Radius);
        }
        else if(shape is Rectangle rect)
        {
            Console.WriteLine(rect.Width * rect.Height);
        }
    }
}

Solution

internal interface IShape
{
    void Render();
    void CalculateArea();
}
internal class Circle : IShape
{
    public double Radius { get; init; }

    public Circle(double radius)
    {
        Radius = radius;
    }

    public void Render()
    {
        Console.WriteLine("Rendering circle");
    }

    public void CalculateArea()
    {
        Console.WriteLine(3.14 * this.Radius * this.Radius);
    }
}
internal class Rectangle : IShape
{
    public double Width { get; init; }

    public double Height { get; init; }

    public Rectangle(double width, double height)
    {
        Width = width;
        Height = height;
    }

    public void Render()
    {
        Console.WriteLine("Rendering rectangle");
    }

    public void CalculateArea()
    {
        Console.WriteLine(this.Width * this.Height);
    }
}
internal class ShapeCalculator
{
    public void CalculateArea(IShape shape)
    {
        shape.CalculateArea();
    }
}
internal class Program
{
    static void Main(string[] args)
    {
        //OCP
        var circle = new Circle(10);
        var shapeCalculator = new ShapeCalculator();
        shapeCalculator.CalculateArea(circle);
    }
}

Example

Example 2

Problem

public class Logger
{
    public void Log(string message)
    {
        Console.WriteLine(message);
    }

    public void Info(string message)
    {
        Console.WriteLine($"Info: {message}");
    }

    public void Debug(string message)
    {
        Console.WriteLine($"Debug: {message}");
    }
}
  • Some developers want to change debug messages to suit their needs. To satisfy their need, you need to edit the code of the Logger class and either create a new method for them or modify the existing Debug method.
  • If you change the existing Debug method then the other developers who don't want this change will also be affected.

Solution

public class Logger
{
    public virtual void Log(string message)
    {
        Console.WriteLine(message);
    }

    public virtual void Info(string message)
    {
        Console.WriteLine($"Info: {message}");
    }

    public virtual void Debug(string message)
    {
        Console.WriteLine($"Debug: {message}");
    }
}

public class NewLogger : Logger
{
    public override void Debug(string message)
    {
        Console.WriteLine($"Dev Debug -> {message}");
    }
}