18 stycznia 2020

PHP: SOLID [O] Otwarty na rozbudowę, zamknięty na modyfikacje

To drugi wpis odnośnie zasad SOLID, zajmiemy się w nim zasadą otwarty/zamknięty. Oryginalna nazwa to: „Open-Closed Principle„.

W tytule wpisu użyłem stwierdzenia „Otwarty na rozbudowę, zamknięty na modyfikacje„, ponieważ tłumaczenie „Open-Closed Principle” może być niezrozumiałe dla młodych programistów.

Wstęp

Założeniem zasady jest, aby w przypadku dodania nowych funkcji ograniczyć ryzyko wystąpienia błędów spowodowanych modyfikacjami w kodzie. Można to uzyskać zamykając modyfikacje pewnych części kodu, dając jednocześnie możliwość rozbudowy tworząc przykładowo nowe klasy dziedziczące.

Praktyka

class Product
{
    private $name;
    private $type;
    private $price;
    private $size = null;
    private $expiration = null;

    public function __construct($name, $type, $price, $size, $expiration)
    {
        $this->name = $name;
        $this->type = $type;
        $this->price = $price;

        if ($type == 'cloth') {
            $this->size = $size;
        }
        
        if ($type == 'food') {
            $this->expiration = $expiration;
        }
        
    }

    public function getSize() : string
    {
        if ($this->type == 'cloth') {
            return $this->size;
        }
    }

    public function getExpirationDate() : string
    {
        if ($this->type == 'food') {
            return $expiration;
        }
    }

    public function getName() : string
    {
        return $this->name;
    }

    public function getType() : string
    {
        return $this->type;
    }

    public function getPrice() : string
    {
        return $this->price;
    }
}

Powyżej mamy klasę produktu, bardzo prosta klasa która zwraca informacje o produkcie, typ, cena itd.

Jest pewien problem, bowiem sklep posiada dwa rodzaje produktów, ubrania i produkty spożywcze.

Ubrania charakteryzują się tym, że posiadają rozmiar, zaś produkty spożywcze posiadają datę przydatności do spożycia.

Programista uznał, że należałoby wykonać warunki if, aby kod działał prawidłowo, no i miał rację, kod będzie działać prawidłowo. Nie zmienia to natomiast faktu, ze nie jest to dobry pomysł.

Dlaczego zatem taki kod łamie drugą zasadę SOLID ?
Jeśli zechcemy wprowadzić kilka nowych elementów tylko dla produktów danego typu, będziemy musieli powyższą klasę modyfikować dodając kolejne warunki if.
Jeśli biznes uzna, że musimy dodać kolejne typy produktów, programista zamiast dodać nowe klasy, będzie zmuszony modyfikować istniejącą klasę.
Łamiąc drugą zasadę SOLID, narażamy się na błędy w już istniejącym kodzie.

Przykładowy, poprawny kod powinien wyglądać następująco:

abstract class Product
{
    private $name;
    private $type;
    private $price;

    public function __construct(string $name, string $type, float $price)
    {
        $this->name = $name;
        $this->type = $type;
        $this->price = $price;
    }

    public function getName() : string
    {
        return $this->name;
    }

    public function getType() : string
    {
        return $this->type;
    }

    public function getPrice() : string
    {
        return $this->price;
    }
}
class ClothProduct extends Product
{
    private $size;

    public function __construct(string $name, string $type, float $price, string $size)
    {
        parent::__construct($name, $type, $price);

        $this->size = $size;
    }

    public function getSize() : string
    {
        return $this->size;
    }
}

class FoodProduct extends Product
{
    private $expiration;

    public function __construct(string $name, string $type, float $price, string $expiration)
    {
        parent::__construct($name, $type, $price);

        $this->expiration = $expiration;
    }

    public function getExpiration() : string
    {
        return $this->expiration;
    }
}

Dzięki dziedziczeniu, możemy bez problemowo dodać nowe typy produktów nie dotykając istniejącego kodu. Możemy dodać również kolejne wartości do przykładowo produktu typu „Food” nie modyfikując przy tym produktów typu „Cloth„.

Podsumowanie

Bez dwóch zdań, warto zapoznać się z zasadami SOLID i stosować się do nich gdy tylko jest to możliwe. Niebawem napiszę kolejne wpisy dotyczące wspomnianych zasad.