Zasada otwarte-zamknięte. Klasy (encje) powinny być otwarte na rozszerzenia i zamknięte na modyfikacje. 

Zgodnie z tą zasadą, oprogramowanie powinno być otwarte na rozszerzenia, ale zamknięte na modyfikacje. Oznacza to, że jeśli potrzebujemy dodać nową funkcjonalność, powinniśmy móc to zrobić bez konieczności zmiany istniejącego kodu. Nowa funkcjonalność powinna być dodawana przez rozszerzenie lub dziedziczenie, nie zmieniając kodu źródłowego. Zasada otwarte – zamknięte często jest realizowana poprzez zastosowanie dziedziczenia od istniejących klas lub implementację nowych interfejsów.

Przykład: Różne klasy zwierząt komunikujące się ze sobą

Załóżmy, że mamy kilka różnych klas zwierząt np: Dog, Duck, Cat.

Błędny kod – struktura klas niezgodna z zasadą otwarte-zamknięte.
class Dog
{
    public function bark(): string
    {
        return 'woof woof';
    }
}
class Duck
{
    public function quack(): string
    {
        return 'quack quack';
    }
}
class Cat
{
    public function miau(): string
    {
        return 'miau miau';
    }
}

Mamy klasę, która pozwala komunikować się zwierzętom. Jest to klasa Communication.

class Communication
{
    public function communicate($animal): string
    {
        switch (true) {
            case $animal instanceof Dog:
                return $animal->bark();
            case $animal instanceof Duck:
                return $animal->quack();
            case $animal instanceof Cat:
                return $animal->miau();
            default:
                throw new \InvalidArgumentException('Unknown animal');
        }
    }
}

Zastanówmy się czy nasza klasa Communication jest zgodna z zasadą otwarte-zamknięte? Możemy dodać nową klasę zwierząt nie zmieniając istniejącego kodu?

Niestety NIE. Musielibyśmy w klasie Communication, w metodzie communicate dodać kolejny case dla nowego zwierzątka.

Poprawny kod – struktura klas zgodna z zasadą otwarte-zamknięte.

Tworzymy interfejs Communicative, w którym znajduje się metoda speak(). Zaimplementujemy interfejs w klasie Dog, Duck, Cat.

interface ICommunicative
{
    public function speak(): string;
}
class Dog implements ICommunicative
{
    public function speak(): string
    {
        return 'woof woof';
    }
}
class Duck implements ICommunicative
{
    public function speak(): string
    {
        return 'quack quack';
    }
}
class Cat implements ICommunicative
{
    public function speak(): string
    {
        return 'miau miau';
    }
}

W kolejnym kroku zmieniamy metodę communicate(), przekazując w niej obiekt typu ICommunicative.

class Communication
{
    public function communicate(ICommunicative $animal): string
    {
        return $animal->speak();
    }
}

W powyższym kodzie wykorzystujemy interfejs ICommunicative, który definiuje metodę speak(). Każda klasa (np. Dog, Duck, Fox) implementuje ten interfejs i dostarcza własną implementację komunikacji. Klasa Communication teraz przyjmuje obiekt implementujący interfejs ICommunicative, co oznacza, że nie musisz modyfikować klasy Communication, aby dodać nowe zwierzęta.

Podsumowanie

Podsumowując, zasada otwarte/zamknięte jest jedną z pięciu zasad SOLID i ma na celu tworzenie oprogramowania, które jest otwarte na rozszerzenia, ale zamknięte na modyfikacje. Kod źródłowy powinien być elastyczny, z możliwością zaimplementowania nowych funkcji bez konieczności zmiany istniejącego kodu. Funkcjonalności powinny być dodawane poprzez rozszerzenie istniejących klas lub poprzez implementację nowych interfejsów.


0 komentarzy

Dodaj komentarz

Avatar placeholder

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *