Wzorzec Fabryka Abstrakcyjna: Tworzenie Rodzin Obiektów bez Specyfikowania Ich Konkretnych Klas

W świecie programowania obiektowego, wzorce projektowe pełnią kluczową rolę w tworzeniu 🚀elastycznego, skalowalnego i łatwego do utrzymania kodu. Jednym z takich wzorców, który pozwala na eleganckie zarządzanie tworzeniem rodzin powiązanych lub zależnych obiektów bez określania ich konkretnych klas, jest 🏭 Fabryka Abstrakcyjna (ang. Abstract Factory). Ten wzorzec konstrukcyjny zapewnia interfejs do tworzenia rodzin obiektów, umożliwiając klientom korzystanie z obiektów różnych rodzin bez związania ich z konkretnymi klasami.

Zobacz także wpis o prostej fabryce.

Zasada Działania

Fabryka Abstrakcyjna opiera się na serii powiązanych ze sobą fabryk, z których każda jest odpowiedzialna za tworzenie obiektów należących do określonej rodziny. Wzorzec ten pozwala na abstrakcję procesu instancjonowania klasy, dzięki czemu system może być niezależny od sposobu tworzenia obiektów i ich reprezentacji. W praktyce oznacza to, że dodanie nowej rodziny obiektów do aplikacji nie wymaga modyfikacji kodu klienta, co znacznie ułatwia rozbudowę i utrzymanie systemu.

Przykłady Zastosowań

Wzorzec Fabryka Abstrakcyjna jest szczególnie przydatny w sytuacjach, gdy:

👉 System powinien być konfigurowany z jedną z wielu rodzin obiektów.
👉 Rodzina powiązanych obiektów ma być używana razem.
👉 Biblioteka lub framework ma dostarczać obiekty do aplikacji bez eksponowania ich implementacji.

Zalety

Izolacja konkretnej implementacji: Klient korzysta z interfejsów, nie wiedząc, które konkretne instancje są używane.
Łatwość wymiany rodzin produktów: Możliwość zmiany rodziny produktów bez modyfikacji kodu klienta.
Promowanie spójności między produktami: Ułatwia tworzenie zestawów obiektów, które dobrze ze sobą współpracują.

Przykład w PHP

Zakładając, że chcemy zastosować wzorzec Fabryka Abstrakcyjna do stworzenia systemu zarządzania różnymi rodzajami samochodów, możemy zdefiniować fabrykę, która będzie produkować obiekty reprezentujące samochody różnych marek, każdy z własnymi cechami. Poniżej znajduje się uproszczony przykład, jak to może wyglądać w PHP.

Interfejsy i Klasy Produktów
Definiujemy podstawowe interfejsy dla naszych produktów, czyli różnych typów samochodów, oraz implementacje tych interfejsów dla konkretnych marek samochodów.

interface Car {
    public function getType(): string;
    public function getBrand(): string;
}

class Sedan implements Car {
    private $brand;

    public function __construct(string $brand) {
        $this->brand = $brand;
    }

    public function getType(): string {
        return 'Sedan';
    }

    public function getBrand(): string {
        return $this->brand;
    }
}

class SUV implements Car {
    private $brand;

    public function __construct(string $brand) {
        $this->brand = $brand;
    }

    public function getType(): string {
        return 'SUV';
    }

    public function getBrand(): string {
        return $this->brand;
    }
}

Interfejs Fabryki Abstrakcyjnej i Konkretne Fabryki
Następnie definiujemy interfejs dla fabryki abstrakcyjnej i konkretne implementacje tej fabryki dla różnych marek samochodów.

interface CarFactory {
    public function createSedan(): Car;
    public function createSUV(): Car;
}

class ToyotaFactory implements CarFactory {
    public function createSedan(): Car {
        return new Sedan("Toyota");
    }

    public function createSUV(): Car {
        return new SUV("Toyota");
    }
}

class FordFactory implements CarFactory {
    public function createSedan(): Car {
        return new Sedan("Ford");
    }

    public function createSUV(): Car {
        return new SUV("Ford");
    }
}

Kod Klienta
Kod klienta używa fabryki abstrakcyjnej do tworzenia obiektów, nie wiedząc, z jakimi konkretnymi implementacjami ma do czynienia.

function clientCode(CarFactory $factory) {
    $sedan = $factory->createSedan();
    echo "Stworzono " . $sedan->getType() . " marki " . $sedan->getBrand() . ".\n";

    $suv = $factory->createSUV();
    echo "Stworzono " . $suv->getType() . " marki " . $suv->getBrand() . ".\n";
}

// Tworzenie samochodów marki Toyota
clientCode(new ToyotaFactory());

// Tworzenie samochodów marki Ford
clientCode(new FordFactory());

W tym przykładzie, CarFactory jest interfejsem fabryki abstrakcyjnej, który deklaruje metody do tworzenia różnych typów samochodów (Sedan i SUV). ToyotaFactory i FordFactory są konkretnymi fabrykami, które implementują ten interfejs, tworząc samochody swoich marek. Kod klienta wykorzystuje te fabryki do stworzenia samochodów, nie będąc bezpośrednio zależnym od konkretnych klas samochodów. Dzięki temu możemy łatwo dodać nowe fabryki dla innych marek samochodów bez zmiany kodu klienta.

Moje początki programowania sięgają 2010r. W trakcie wielu projektów zdobywałem doświadczenie, rozwijając nie tylko umiejętności techniczne, ale także kompetencje miękkie. Programuje głównie w PHP i Python.