Solid Design Patterns – Open closed principle

Solid Design Patterns – Open Closed Principle

Open Closed Principle
In object-oriented programming, the Open Closed Principle states “software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification”; that is, such an entity can allow its behaviour to be extended without modifying its source code.

Quick recap, SOLID stands for five design principles:
S – Single responsibility principle
O – Open closed principle
L – Liskov substitution principle
I – Interface segregation principle
D – Dependency Inversion principle

We’ve discussed the Single Responsibility principle before.

Now we are going to address the Open Closed Principle.

Open Closed Principle

Yes, in beginning this principle little confusion but it is very easy. The main concept here is that our code, our abstractions such as classes, modules that do one single thing and should always do it well. Most importantly we want to keep them pretty, neat and well behaved always.

But at the same time business needs keep coming with new requirements, changes in existing requirements, sometimes scope may change or a new feature request which requires changes in the existing system. Never the less as a developer we know changes always are there and we need to change the code for adding more functionality or features. Now we want that way or scope in the existing system to keep the new code, system, classes, modules should remain neat and well behaved.

That’s where the particular principle of Open Closed comes into play to help, so we can do changes nicely. Following things need to consider whenever we are changing the existing system, module or code:

– Keep the current functions, Classes, modules as they are or at least close to what they used to be – immutable.
– Extend, in a composable (avoid inheritance) the way the existing Classes, functions or modules so that they expose the new feature or functionality possibly in a different name.

The composition or the Composite design pattern, Decorator pattern, Observer pattern, Factory Method are very helpful to achieving this principle like all new functionality can be added by adding new composite classes or methods, or by reusing existing code through delegation.

An example – calculating area

Let’s say we want to calculate an area of Square, as we for Square all side is equal so we can compute the area of a square if you know the length of its sides.

public class Square
{
public double Side { get; set; }
}
public class AreaCalculator
{
public double Area(Square[] shapes)
{
double area = 0;
foreach (var shape in shapes)
{
area += shape.Side * shape.Side;
}
return area;
}
}

Now new requirement comes up with a calculating area of Rectangle and Cricle. Ok, we change our Area method to accept a collection of objects instead of the more specific Square type.

public class Rectangle
{
public double Width { get; set; }
public double Height { get; set; }
}
public class AreaCalculator
{
public double Area(object[] shapes)
{
double area = 0;
foreach (var shape in shapes)
{
if (shape is Square)
{
area += shape.Side * shape.Side;
}
elseif (shape is Rectangle)
{
Rectangle rectangle = (Rectangle) shape;
area += rectangle.Width*rectangle.Height;
}
else
{
Circle circle = (Circle)shape;
area += circle.Radius * circle.Radius * Math.PI;
}
}
return area;
}
}

Now let’s check solution with Open close principle (SOLID, Software design principles).

Create a base abstract class for shape with Area method. We can further improve the following example by using interface and dependency injection.

public abstract class Shape
{
public abstract double Area();
}

Now all three shape classes Rectangle, Square and Circle with extend this Shape class.

/**
* Rectangle class
*/
public class Rectangle extends Shape
{
public double Width { get; set; }
public double Height { get; set; }
public override double Area()
{
return Width*Height;
}
}

/**
* Cricle class
*/
public class Circle extends Shape
{
public double Radius { get; set; }
public override double Area()
{
return Radius*Radius*Math.PI;
}
}

/**
* Square class
*/
public class Square extends Shape
{
public double Side { get; set; }
public override double Area()
{
area += shape.Side * shape.Side;
}
}

Now our new Area method is much simpler and also have scope for changes with well-defined way.

/**
* AreaCalculator class
*/
public class AreaCalculator
{
public double Area(Shape[] shapes)
{
double area = 0;
foreach (var shape in shapes)
{
area += shape.Area();
}
return area;
}
}

Important point: A clever application design and the code writing part should take care of the frequent changes that are done during the development and the maintaining phase of an application. The more you know is better. Always ask for good and more questions, to know about the future plan or upcoming things which going to change your module. This will help you to make your modules add some extra scope for changes.

Like every principle, OCP (Open Closed Principle) is only a principle. Making a flexible design involves additional time and effort spent on it and it introduces the new level of abstraction increasing the complexity of the code. So this principle should be applied in those areas which are most likely to be changed.