
Spis treści
Open/closed principle
All classes should be open for enlargement and closed for modifications.
How to use OCP in practice?
You should use interfaces and abstraction, not to become dependent on implementation details.
Difficulties in using OCP
Sometimes is hard to decide, what is implementing detail and in which direction the application is going.
Example
To exercises OCP I took a simple example. We have Calculator, which calculates figure area.In first version I am using switch, to recognise a specific figure.
class AreaCalculator { private readonly List<object> figures; public AreaCalculator(List<object> figures) { this.figures = figures; } public double Calculate() { double area = 0; foreach (var figure in figures) { switch (figure) { case Square square: area += square.GetWidth() * square.GetWidth(); break; case Rectangle rectangle: area += rectangle.GetWidth() * rectangle.Getheight(); break; case Circle circle: area += Math.PI * Math.Pow(circle.GetRadius(), 2); break; default: area += area; break; } } return area; } } class Square { private readonly double width; public Square(double width) { this.width = width; } internal double GetWidth() { return width; } } class Rectangle { private readonly double width; private readonly double height; public Rectangle(double width, double height) { this.width = width; this.height = height; } internal double GetWidth() { return width; } internal double Getheight() { return height; } } class Circle { private readonly double radius; public Circle(double radius) { this.radius = radius; } internal double GetRadius() { return radius; } } class Program { static void Main(string[] args) { List<object> figures = new List<object>() { new Square(5), }; AreaCalculator areaCalculator = new AreaCalculator(figures); Debug.Assert(areaCalculator.Calculate() == 25); List<object> figures2 = new List<object>() { new Square(5), new Rectangle(5,3), }; Debug.Assert(new AreaCalculator(figures2).Calculate() == (25 + 15)); List<object> figures3 = new List<object>() { new Square(5), new Rectangle(5,3), new Circle(5), }; Debug.Assert(new AreaCalculator(figures3).Calculate() == (25 + 15 + Math.PI * 25)); } }
After refactoring, I have figure classes, which implement the interface to calculate figure area. The calculator only sums all figure areas.
class AreaCalculator { private readonly IEnumerable<IFigureAreaCalculator> figures; public AreaCalculator(IEnumerable<IFigureAreaCalculator> figures) { this.figures = figures; } public double Calculate() { return figures.Sum(x => x.GetArea()); } } interface IFigureAreaCalculator { double GetArea(); } class Square : IFigureAreaCalculator { private readonly double width; public Square(double width) { this.width = width; } public double GetArea() { return width * width; } internal double GetWidth() { return width; } } class Rectangle : IFigureAreaCalculator { private readonly double width; private readonly double height; public Rectangle(double width, double height) { this.width = width; this.height = height; } internal double GetWidth() { return width; } internal double Getheight() { return height; } public double GetArea() { return width * height; } } class Circle : IFigureAreaCalculator { private readonly double radius; public Circle(double radius) { this.radius = radius; } public double GetArea() { return Math.PI * Math.Pow(radius, 2); } internal double GetRadius() { return radius; } } class Program { static void Main(string[] args) { var figures = new List<IFigureAreaCalculator>() { new Square(5), }; AreaCalculator areaCalculator = new AreaCalculator(figures); Debug.Assert(areaCalculator.Calculate() == 25); var figures2 = new List<IFigureAreaCalculator>() { new Square(5), new Rectangle(5,3), }; Debug.Assert(new AreaCalculator(figures2).Calculate() == (25 + 15)); var figures3 = new List<IFigureAreaCalculator>() { new Square(5), new Rectangle(5,3), new Circle(5), }; Debug.Assert(new AreaCalculator(figures3).Calculate() == (25 + 15 + Math.PI * 25)); } }
Adding a new figure does not require any changes in the AreaCalculator.
All posts from mini project: Learn SOLID and OOP principles:
- SOLID
- SOLID exercises
- S like Single responsibility principle
- SOLID exercises — Kata
- O as Open-closed principle
- L jak Liskov Substitution Principle
- Interface segregation principle
- KISS — Keep it simple, stupid
- DRY — Don’t repeat yourself
- Dependency inversion principle
- SLAP — Single Level of Abstraction Principle
- Composition Over Inheritance
- Encapsulate what changes
- Lod — Law of Demeter
- ES as Exercises of Single responsibility principle
- EO as Exercises of Open/closed principle
- EL as Exercises of Liskov Substitution Principle
- EI as Eexrcises of Interface segregation principle
- ES as Exercises of Dependency Inversion Principle
- Object-oriented programming
- OOP — Object-Oriented Programming — Advice
- OOP — Object Oriented Programming
Sources
Main image
Materials
- Clean Architecture — Robert C. Martin
- https://hackernoon.com/why-the-open-closed-principle-is-the-one-you-need-to-know-but-dont-176f7e4416d
- https://code.tutsplus.com/tutorials/solid-part-2-the-openclosed-principle–net-36600
Sorry, there was a YouTube error.