hkucuk

SOLID - Open Closed Principle

26 Ağustos 2021 • ☕️ 3 dk okuma • 🏷 bilgisayar, yazılım

Yazar tarafından şu dillere çevrildi: English


Open-closed prensibi, bir sınıfın işlevselliğini genişletmek için değiştirilmemesi, ancak yeni işlevsellik eklemek için yeni sınıflar oluşturulması gerektiğini öngören bir programlama prensibidir. Bu prensip, programlarımızın daha esnek, bütünleşik ve bakımı daha kolay olmasını sağlar.

Örnek olarak, bir sınıfın bir alt sınıfı olabilmesi için, alt sınıfın yüksek sınıfın yapabildiklerini yapması gerekir. Ayrıca, alt sınıfın yüksek sınıfın yapabildiklerini yapmasının yanı sıra, kendi ek işlevselliğini de içermesi gerekir. Bu sayede, alt sınıf yüksek sınıfın yerine kullanılabilir ve aynı zamanda yüksek sınıfın yapabildiklerini aşarak kendi işlevselliğini de sunabilir.

PHP’de, Open-closed prensibini uygulamak için aşağıdaki gibi bir örnek verilebilir:

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;
  }
}

Bu kod örneğinde, Shape arayüzü bir şeklin alanını hesaplayabilecek bir metodu tanımlar. Circle ve Rectangle sınıfları bu arayüzü uygular ve calculateArea() metodunu kendi içlerinde tanımlar. Bu sayede, daha sonra farklı şekiller eklenmesi gerektiğinde bu sınıfları değiştirmeye gerek kalmaz, sadece Shape arayüzünü uygulayan yeni bir sınıf oluşturulur. AreaCalculator sınıfı da Shape nesnelerinin alanlarını toplamak için kullanılabilir.

Aşağıdaki PHP kodu ise Open-closed prensibini ihlal eden bir sınıf örneğidir.

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;
  }
}

Bu kod örneğinde, Circle ve Rectangle sınıfları calculateArea() metodunu kendi içlerinde tanımlar, ancak bu metodlar sadece belirli bir şeklin alanını hesaplar. Eğer farklı bir şekil yukarıdaki gibi eklenirse Open-closed prensibi ihlal edilmiş olur. Triangle sınıfı Shape interface’ini implement etmemiş, kendi calculateArea() metodunu tanımlamamıştır. Bu durumda da AreaCalculator sınıfının sum() methoduna ekleme yapmak gerekmiştir. Bu da OCP’yi ihlal eder çünkü AreaCalculator sınıfı değiştirilmiştir.


GoLang dilinde, Open-closed prensibini uygulamak için aşağıdaki gibi bir örnek verilebilir:

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
}

Bu kod örneğinde, Shape tipi bir şeklin alanını hesaplayabilecek bir metodu tanımlar. Circle ve Rectangle tipleri bu tipi uygular ve CalculateArea() metodunu kendi içlerinde tanımlarlar. Bu sayede, daha sonra farklı şekiller eklenmesi gerektiğinde bu tipleri değiştirmeye gerek kalmaz, sadece Shape tipini uygulayan yeni bir tip oluşturulur. AreaCalculator tipi de Shape nesnelerinin alanlarını toplamak için kullanılabilir.

PHP örneğine benzer şekilde, aşağıdaki GoLang kodu da Open-closed prensibini ihlal eden bir örnektir.

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
}

Kaynaklar