In object-oriented programming,SOLIDis the acronym of five design principles designed to make software design easier to understand, flexible and maintainable. These principles are a subset of many principles proposed by American software engineer and lecturer Robert Cecil Martin, first proposed in his 2000 paper, Design Principles and Design Patterns.
The SOLID principle includes:
- S:Single-responsibility principle
- O:Open-closed principle
- L:Liskov substitution principle
- I:Interface segregation principle
- D:Dependency inversion principle
In this article, we will introduce the principles of opening and closing.
Opening and closing principle
In the realm of object-oriented programming,Opening and closing principle (open-closed principle, OCP) Regulation“Objects (classes, modules, functions, etc.) in the software should be open to extensions and closed to modifications.”, which means that an entity allows its behavior to be changed without changing its source code. This feature is particularly valuable in productized environments where changing the source code requires code review, unit testing, and such processes to ensure the quality of product usage. Code following the principle of opening and closing does not change when extended, so these processes are not needed.
Specifically for a class, that is, it should be able to extend its behavior without modifying the class's own code.
C# Example
Let's review the previous articleSingle function principleThe AreaCalculator class mentioned in
class AreaCalculator { private List<object> _shapes; public AreaCalculator(List<object> shapes) { _shapes = shapes; } /// <summary> /// Calculate the sum of areas of all shapes /// </summary> /// <returns></returns> public double Sum() { List<double> areas = new List<double>(); foreach (var item in _shapes) { if (item is Square s) { ((, 2)); } else if (item is Circle c) { ( * (, 2)); } } return (); } }
For the above calculation method, consider such a scenario, the user wants to calculate the sum of areas of some other shapes, such as triangles, rectangles, pentagons, etc.... You will have to edit this class repeatedly to add moreif/else
Block, this violates the principle of opening and closing.
improve
A better approach is to remove the logic that calculates the area of each shape from the AreaCalculator class and add it to the class corresponding to each shape. We can define a withCalcArea
Method interface IShape, then let each shape implement this interface.
Interface IShape:
interface IShape { /// <summary> /// Calculate area /// </summary> /// <returns></returns> double CalcArea(); }
Modified Square and Circle classes:
/// <summary> /// square/// </summary> class Square : IShape { public Square(double length) { SideLength = length; } public double SideLength { get; init; } public double CalcArea() { return (SideLength, 2); } } /// <summary> /// Circular/// </summary> class Circle : IShape { public Circle(double radius) { Radius = radius; } public double Radius { get; init; } public double CalcArea() { return * (Radius, 2); } }
The AreaCalculator class also needs to make some corresponding modifications:
class AreaCalculator { private List<IShape> _shapes; public AreaCalculator(List<IShape> shapes) { _shapes = shapes; } /// <summary> /// Calculate the sum of areas /// </summary> /// <returns></returns> public double Sum() { List<double> areas = new List<double>(); foreach (var item in _shapes) { (()); } return (); } }
At this time, if we have a new shape that needs to be calculated, we can directly add a new class that implements the interface IShape without modifying the code of the AreaCalculator class, such as adding a rectangular class:
/// <summary> /// rectangle/// </summary> class Rectangle : IShape { public Rectangle(double width, double height) { Width = width; Height = height; } public double Width { get; init; } public double Height { get; init; } public double CalcArea() { return Width * Height; } }
The SumCalculatorOutputter class that handles the output format also does not need to be modified:
class SumCalculatorOutputter { protected AreaCalculator _calculator; public SumCalculatorOutputter(AreaCalculator calculator) { _calculator = calculator; } public string String() { return $"Sum of the areas of provided shapes: {_calculator.Sum()}"; } public string JSON() { var data = new { Sum = _calculator.Sum() }; return (data); } }
Then, we modifyMain
Let’s test the code in the method:
static void Main(string[] args) { var shapes = new List<IShape> { new Circle(2), new Square(5), new Rectangle(2,3) }; var areaCalculator = new AreaCalculator(shapes); var outputer = new SumCalculatorOutputter(areaCalculator); (()); (()); }
Run it and the output is:
{"Sum":43.56637061435917}
Sum of the areas of provided shapes: 43.56637061435917
Now, these categories designs follow the principle of single function and the principle of opening and closing.
Summarize
In this article, I introduced the SOLID principleOpening and closing principle(open-closed principle), and concisely interprets its meaning and implementation through C# code examples, I hope it will be helpful to you.
Reference documentation:
/community/conceptual_articles/s-o-l-i-d-the-first-five-principles-of-object-oriented-design
This is the end of this article about the examples of the opening and closing principles in C# object-oriented programming. For more related contents of the opening and closing principles of C#, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!