Click here to Skip to main content
11,417,467 members (52,796 online)
Click here to Skip to main content

SOLID Principles: The Open Closed Principle -> What, Why and How.

, 29 Jun 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
SOLID principles: The Open Closed Principle, a simple example in C#

Introduction

This article will give an explanation of the Open Closed Principle (OCP) and will show a simple example in C#.

Background

What
In 1988 the open closed principle (OCP) is already mentioned by Bertrand Meyer :
Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.
"Object Oriented Software Construction, Bertrand Meyer"
Software that works should when possible not be changed when your application is extended with new functionality.
Instead it should be possible to extend the existing software with new functionality without any modification to the current codebase and without adding duplicate code or duplicate functionality.

Why
Tell me how many times you create a green field application from scratch versus the number of times you are working on an existing codebase just adding new functionality?
Chances are good that you spend far more time adding new features to an existing codebase right ?
Then ask yourself the question: is it easier to write all new code or to make changes to existing code ?
The OCP handles the way you want to structure your code in order to yield one of the greatest benefits claimed for object oriented technology: reusability and maintainability.

How
The best way to implement the open closed principle is to first start with implementing the Single Responsibility Principle: a class should have one, and only one, reason to change.
This will separate different concerns in your code.
The next step is represent these separate concerns by abstractions and let consumers of these concerns talk to these abstractions.

To state the open closes principle very straightforward way you can say :
- You should design modules that never change.
- When requirements change, you extend the behavior of such modules by adding new code, not by changing old code that already works.

Abstraction is the way to realize this principle.
Derivatives from an abstraction are closed for modification because the abstraction is fixed but behaviour can be extended by creating new derivatives of the abstraction.

Using the code

The following example shows a class that calculates the sum of area's of an array of shapes.
When a new shape is introduced two things must be implemented :
- The shape class itself .
- The AreaCalculator must be changed to calculate the area of this new shape.

The AreaCalculator violates the open closed principle because, a new requirement for this code will always consist of a change in calculation of a certain existing shape or a new shape is introduced.
In both cases the AreaCalculator has to be changed and therefore not closed for modification.
It is also not open for extension because we can not extend the functionality of the AreaCalulator without modifying it.

Problem

/***************** Calculates the sum of all shape area's *****************/

public class AreaCalculator
{
 
 public double Area(object[] shapes)
 {
  double area = 0;

  foreach (var shape in shapes)
  {
  
   if (shape is Square)
   {
     Square square = (Square)shape;
     area += Math.Sqrt(square.Height);
   }

   if (shape is Triangle)
   {
     Triangle triangle = (Triangle)shape;
     double TotalHalf = (triangle.FirstSide + triangle.SecondSide + triangle.ThirdSide) / 2;
     area += Math.Sqrt(TotalHalf * (TotalHalf - triangle.FirstSide) * 
     (TotalHalf - triangle.SecondSide) * (TotalHalf - triangle.ThirdSide));
   }

   if (shape is Circle)
   {
     Circle circle = (Circle)shape;
     area += circle.Radius * circle.Radius * Math.PI;
   }

  }
  return area;
 }
}
public class Square
{
  public double Height { get; set; }
}
public class Circle
{
  public double Radius { get; set; }
}
public class Triangle
{
  public double FirstSide { get; set; }
  public double SecondSide { get; set; }
  public double ThirdSide { get; set; }
}


Following the solution.
Step 1 : separate responsibilities by moving the area calculations to the concerned shape classes.
Step 2 : Introduce abstractions of the concerned functionality and let consumers obey these abstraction.

Now when a new shape is introduced or e.g. the area calculation of a certain shape changes,
the AreaCalculator does not have to change and it can easy be extended with new shapes without need for change.
A footnote is that real closure many times only can be realized in theory because there is always a changes possible that will violate the closure. If for example the sequence of the area calculation must be changed then we have to change the AreaCalculator. That’s why the closure must be chosen wisely based on feeling and experience.

Solution

Open Closed AreaCalculator

/***************** Calculates the sum of all shape area's *****************/

public class AreaCalculator
{
  public double Area(Shape[] shapes)
  {
    double area = 0;

    foreach (var shape in shapes)
    {
     area += shape.Area();
    }

     return area;
   }
}

Introduce the abstraction

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

Square class implements the abstraction and its area calculation

public class Square : Shape
{
 public double Height { get { return _height; } }
 private double _height; 

 public Square(double Height)
 {
   _height = Height;
 }

 public override double Area()
 {
   return Math.Sqrt(_height);
 }
}

Circle class implements the abstraction and its area calculation

public class Circle : Shape
{
  public double Radius{get{return _radius;}}
 
  private double _radius;

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

  public override double Area()
  {
   return _radius * _radius * Math.PI;
  }
}

Triangle class implements the abstraction and its area calculation

public class Triangle : Shape
{ 
 public double FirstSide {get {return _firstSide;}}
 public double SecondSide { get { return _secondSide; } }
 public double ThirdSide { get { return _thirdSide; } } 
 
 private double _firstSide;
 private double _secondSide;
 private double _thirdSide; 
 
 public Triangle(double FirstSide, double SecondSide, double ThirdSide)
 {
   _firstSide = FirstSide;
   _secondSide = SecondSide;
   _thirdSide = ThirdSide;
 }

 public override double Area()
 {
   double TotalHalf = (_firstSide + _secondSide + _thirdSide) / 2;
   return Math.Sqrt(TotalHalf * (TotalHalf - _firstSide) * (TotalHalf - _secondSide) * (TotalHalf - _thirdSide));
 }
}
Have fun !

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Christian Vos
Founder Rood Mitek
Netherlands Netherlands
Christian Vos .MCSD has been an independent Microsoft developer since 1999 and is specialized in distributed Microsoft .Net platform based Web applications. He is founder of the company Rood Mitek which has developed applications for companies like Philips Electronics, ASML, Lucent Technologies, Amoco, AT&T etc.

Comments and Discussions

 
BugArea of square Pin
Member 102483715-Jan-14 13:58
memberMember 102483715-Jan-14 13:58 
QuestionMy vote of 5 Pin
Peace ON16-Jul-13 10:04
memberPeace ON16-Jul-13 10:04 
GeneralMy vote of 5 Pin
PCoffey29-Jun-13 20:51
memberPCoffey29-Jun-13 20:51 
GeneralRe: My vote of 5 Pin
Christian Vos30-Jun-13 12:40
memberChristian Vos30-Jun-13 12:40 
QuestionThis is a quite better example Pin
Klaus Luedenscheidt28-Jun-13 19:17
memberKlaus Luedenscheidt28-Jun-13 19:17 
AnswerRe: This is a quite better example Pin
Christian Vos29-Jun-13 0:21
memberChristian Vos29-Jun-13 0:21 
GeneralRe: This is a quite better example Pin
Klaus Luedenscheidt29-Jun-13 18:38
memberKlaus Luedenscheidt29-Jun-13 18:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150427.4 | Last Updated 29 Jun 2013
Article Copyright 2013 by Christian Vos
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid