SOLID - Zasada otwarte/zamknięte

Dariusz Ciesielski • 17/12/2017

Zasada mówi o tym, że klasa powinna być otwarta na rozszerzenia i zamknięta na modyfikację. Zmieniamy zachowanie klasy tak, aby nie ruszać jej oryginalnego kodu, ale aby mogła być rozszerzana przez inne klasy.

Załóżmy, że mamy klasę, w której wybieramy sposób płatności. Pierwsze, co przyszłoby nam na myśl, to stworzenie metody z typem na podstawie którego sprawdzalibyśmy typ płatności.

<?php
class Checkout
{
    public function payment($type)
    {
        if ($type == 'cash') {
            $payment = 'cash';//new Chash();
        } elseif ($type == 'credit-card') {
            $payment = 'creadit card';//new CreditCard();
        } elseif ($type == 'bitcoin') {
            $payment = 'bitcoin';//new Bitcoin();
        }
        return $payment;
    }
}
$checkout = new Checkout();
echo $checkout->payment('bitcoin');

Kod

Lecz ten sposób ma pewien minus, mianowicie jeżeli chcemy dołożyć kolejny typ płatności musimy ingerować w samą klasę dokładając kolejny typ. Zapewne nie jeden programista na tym etapie bałby się, że jeżeli dołoży kolejny fragment kodu, to zepsuje poprzedni kod.

Więc jak temu zapobiec?

Potrzebujemy rozłożyć klasę na mniejsze podklasy (w tym przypadku tworzymy klasy Cash, CreditCard, Bitcoin ) i w zależności od potrzeb wstrzykiwać odpowiednią klasę (wykorzystując Dependecy Injection).

Jeżeli chcesz dowiedzieć się więcej na temat Dependency Injection sprawdź mój wideo tutorial

We wszystkich tych klasach musimy zaimplementować metodę o tej samej nazwie (w tym przypadku metodę payment() ). Dzięki wykorzystaniu interfejsu PaymentContract dajemy sobie 100% pewność, że te (i tworzone w przyszłości) klasy będą implementować tą metodę.

<?php
interface PaymentContract
{
    public function payment();
}
class Cash implements PaymentContract
{
    public function payment()
    {
        return 'cash';
    }
}
class CreditCard implements PaymentContract
{
    public function payment()
    {
        return 'creadit card';
    }
}
class Bitcoin implements PaymentContract
{
    public function payment()
    {
        return 'bitcoin';
    }
}
class Checkout
{
    public function begin(PaymentContract $checkout)
    {
        return $checkout->payment();
    }
}
$checkout = new Checkout();
echo $checkout->begin(new CreditCard());

Kod

Na samym końcu tworzymy klasę Checkout, która to w swoim parametrze przyjmuję wszystkie klasy z interfejsem PaymentContract, a następnie wywołuje metodę payment().

Dzięki takiemu podejściu nie musimy już ingerować w naszej głównej klasie, tylko dokładać kolejne klasy w momencie, gdy pojawią się kolejne typy płatności.

Mamy więc klasę zamkniętą na modyfikację i otwartą na rozszerzenia.

Całość kodu znajdziesz tutaj