Classlar genişletmeye açık ama değiştirmeye kapalı olmalıdırlar.


# VIOLATION
class DiscountCalculator
{
public function calculate(string $itemType, float $itemPrice, int $unitTotal): float
{
$discount = 0;

if ($itemType == 'bread') {
$discount = $this->getDiscount(0.2, $itemPrice, $unitTotal); # 20%
} elseif ($itemType == 'rice') {
$discount = $this->getDiscount(0.4, $itemPrice, $unitTotal); # 40%
}

return $discount;
}

private function getDiscount(float $discount, float $itemPrice, int $unitTotal): float
{
return ($itemPrice * $unitTotal) * $discount;
}
}

$discountCalculator = new DiscountCalculator();
echo $discountCalculator->calculate('bread', 1.00, 5);
echo $discountCalculator->calculate('rice', 3.00, 4);

Yukarıda gördüğümüz gibi, DiscountCalculator classı pek genişletmeye açıkmış gibi görünmüyor. Yeni tipler eklemeyi hayal edersek, bu class daha fazla değiştirmeye açıkmış gibi görünüyor. Bu durum da prensip ihlaline yol açıyor.


# REFACTORED
interface ItemInterface
{
public function setDiscountValue(float $discountValue);
public function getDiscountValue(): float;
}

class Bread implements ItemInterface
{
private $discountValue;

public function setDiscountValue(float $discountValue)
{
$this->discountValue = $discountValue;
}

public function getDiscountValue(): float
{
return $this->discountValue;
}
}

class Rice implements ItemInterface
{
private $discountValue;

public function setDiscountValue(float $discountValue)
{
$this->discountValue = $discountValue;
}

public function getDiscountValue(): float
{
return $this->discountValue;
}
}

class DiscountCalculator
{
public function calculate(ItemInterface $item, float $itemPrice, int $unitTotal): float
{
return ($itemPrice * $unitTotal) * $item->getDiscountValue();
}
}

$discountCalculator = new DiscountCalculator();

$bread = new Bread();
$bread->setDiscountValue(0.2);

$rice = new Rice();
$rice->setDiscountValue(0.4);

echo $discountCalculator->calculate($bread, 1.00, 5);
echo $discountCalculator->calculate($rice, 3.00, 4);

Yukarıda gördüğümüz gibi, elimizde Bread ve Rice classları var. Ayrıca DiscountCalculator tam bir hesap yapma classı halini almış durumda. Eğer ileride yeni maddelerin hesaplamasını yapmak istersek, tek yapmamız gereken Bread veya Rice classlarına benzeyen bir class yaratıp, onu DiscountCalculator classına enjekte etmek olacaktır. Sonuç olarak hiçbir yerde değişiklik yapmaya gerek olmayacaktır.