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}");
}
}