01/09/2017 - PHP
Objects should be open for extension but closed for modification.
# 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);
As you can see above, our DiscountCalculator
class is not a good candidate for extension. It looks more like a class which should be modified if we wanted to add more item types to it. That's why this is a violation.
# 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);
As you can see above, we now have a Bread
and a Rice
classes. Also the DiscountCalculator
is a pure calculator class. In future, If we want to calculate discounts for a new item, all we have to do is, create a new dedicated class just like Bread
or Rice
then inject it into DiscountCalculator
class. As a result, no modification would be needed anywhere.