hkucuk

SOLID - Open Closed Principle

August 26, 2021 • ☕️ 4 min read • 🏷 computer, software, solid

Translated by author into: English


The open-closed principle is a programming principle that stipulates that a class should not be modified to extend its functionality, but that new classes should be created to add new functionality. This principle makes our programs more flexible, integrated and easier to maintain.

For example, for a class to be a subclass, the subclass must do what the higher class can do. Also, the subclass must do what the higher class can do, as well as contain its own additional functionality. In this way, the lower class can be used in place of the higher class and at the same time offer its own functionality, surpassing what the higher class can do.

An example of applying the Open-closed principle in PHP is as follows:

interface Shape {
  public function calculateArea();
}

class Circle implements Shape {
  private $radius;

  public function __construct($radius) {
    $this->radius = $radius;
  }

  public function calculateArea() {
    return pi() * pow($this->radius, 2);
  }
}

class Rectangle implements Shape {
  private $width;
  private $height;

  public function __construct($width, $height) {
    $this->width = $width;
    $this->height = $height;
  }

  public function calculateArea() {
    return $this->width * $this->height;
  }
}

class AreaCalculator {
  private $shapes;

  public function __construct($shapes = array()) {
    $this->shapes = $shapes;
  }

  public function sum() {
    $area = 0;

    foreach ($this->shapes as $shape) {
      $area += $shape->calculateArea();
    }

    return $area;
  }
}

In this code example, the Shape interface defines a method that can calculate the area of a shape. The Circle and Rectangle classes implement this interface and define the calculateArea() method internally. This way, when different shapes need to be added later, there is no need to change these classes, just a new class that implements the Shape interface is created. The AreaCalculator class can also be used to collect areas of Shape objects.

The PHP code below is an example of a class that violates the Open-closed principle.

interface Shape {
  public function calculateArea();
}

class Triangle {
  private $base;
  private $height;

  public function __construct($base, $height) {
    $this->base = $base;
    $this->height = $height;
  }
}

class Circle implements Shape {
  private $radius;

  public function __construct($radius) {
    $this->radius = $radius;
  }

  public function calculateArea() {
    return pi() * pow($this->radius, 2);
  }
}

class Rectangle implements Shape {
  private $width;
  private $height;

  public function __construct($width, $height) {
    $this->width = $width;
    $this->height = $height;
  }

  public function calculateArea() {
    return $this->width * $this->height;
  }
}

class AreaCalculator {
  private $shapes;

  public function __construct($shapes = array()) {
    $this->shapes = $shapes;
  }

  public function sum() {
    $area = 0;

    foreach ($this->shapes as $shape) {
      if ($shape instanceof Circle) {
        $area += return 0.5 * $shape->base * $shape->height;
      } else {
        $area += $shape->calculateArea();
      }
    }

    return $area;
  }
}

In this code example, the Circle and Rectangle classes define the calculateArea() method internally, but these methods only calculate the area of a particular shape. If a different shape is added as above, the Open-closed principle is violated. The Triangle class did not implement the Shape interface and did not define its own calculateArea() method. In this case, it was necessary to add the sum() method of the AreaCalculator class. This violates OCP because the AreaCalculator class has been changed.


In GoLang, an example to apply the Open-closed principle is as follows:

type Shape interface {
  CalculateArea() float64
}

type Circle struct {
  radius float64
}

func (c *Circle) CalculateArea() float64 {
  return math.Pi * math.Pow(c.radius, 2)
}

type Rectangle struct {
  width  float64
  height float64
}

func (r *Rectangle) CalculateArea() float64 {
  return r.width * r.height
}

type AreaCalculator struct {
  shapes []Shape
}

func (a *AreaCalculator) Sum() float64 {
  var area float64
  for _, shape := range a.shapes {
    area += shape.CalculateArea()
  }
  return area
}

In this code example, the Shape type defines a method that can calculate the area of a shape. Circle and Rectangle types implement this type and define the CalculateArea() method within themselves. This way, when different shapes need to be added later, there is no need to change these types, just a new type that implements the Shape type is created. The AreaCalculator type can also be used to collect areas of Shape objects.

Similar to the PHP example, the GoLang code below is an example that violates the Open-closed principle.

type Circle struct {
  radius float64
}

func (c *Circle) CalculateArea() float64 {
  return math.Pi * math.Pow(c.radius, 2)
}

type Rectangle struct {
  width  float64
  height float64
}

func (r *Rectangle) CalculateArea() float64 {
  return r.width * r.height
}

type AreaCalculator struct {
  shapes []interface{}
}

func (a *AreaCalculator) Sum() float64 {
  var area float64
  for _, shape := range a.shapes {
    switch v := shape.(type) {
    case Circle:
      area += math.Pi * math.Pow(v.radius, 2)
    case Rectangle:
      area += v.width * v.height
    }
  }
  return area
}

Resources