Wzorzec projektowy Fabryka

Wprowadzenie


Wzorzec projektowy Fabryka należy do grupy wzorców kreacyjnych i służy do tworzenia obiektów bez konieczności bezpośredniego określania ich konkretnych klas.


Innymi słowy, Fabryka pozwala „zlecić” tworzenie obiektów specjalnym klasom, które wiedzą, jak i jaki konkretny obiekt należy utworzyć.

W praktyce oznacza to, że kod korzystający z obiektów nie musi wiedzieć, jak są one tworzone – wystarczy, że poprosi Fabrykę o produkt.

Idea wzorca Fabryka


Główna idea wzorca Fabryka polega na delegowaniu procesu tworzenia obiektów do specjalnych metod lub klas.
Zamiast tworzyć obiekty za pomocą operatora new, korzystamy z metody fabrykującej, która zwraca odpowiedni typ produktu.

Dzięki temu możemy łatwo rozszerzać system o nowe typy obiektów bez modyfikowania istniejącego kodu – wystarczy dodać nową klasę produktu i jej fabrykę.

Kluczowe elementy wzorca:

ConcreteCreator (Konkretny Twórca) – implementacja metody fabrykującej, tworząca określony produkt.

Product (Produkt) – interfejs lub klasa bazowa dla obiektów tworzonych przez fabrykę,

ConcreteProduct (Konkretny Produkt) – konkretna implementacja produktu,

Creator (Twórca) – klasa deklarująca metodę fabrykującą,

Designed by Freepik

Przykład z prefabrykacji żelbetowej


Wyobraźmy sobie zakład prefabrykacji, który produkuje różne typy elementów żelbetowych:

  • Słupy,
  • Belki,
  • Płyty stropowe,
  • Ściany prefabrykowane.

Każdy z tych elementów ma swoje parametry, ale ich tworzenie odbywa się w ramach wspólnego procesu – zbrojenie, formowanie, wylewanie betonu, pielęgnacja, kontrola jakości itd.

W tym przypadku Fabryka może być odpowiedzialna za tworzenie odpowiedniego typu elementu w zależności od zapotrzebowania. Przykładowo, klient może poprosić o „belkę”, a fabryka zwróci gotowy obiekt klasy BelkaPrefabrykowana.

Poniżej przykład zastosowania wzorca Fabryka w kontekście prefabrykacji żelbetowej:

// Produkt – wspólny interfejs prefabrykatów
interface PrefabElement {
    void produkcja();
}

// Konkretne produkty
class Slup implements PrefabElement {
    @Override
    public void produkcja() {
        System.out.println("Produkcja słupa żelbetowego: zbrojenie, szalowanie, betonowanie.");
    }
}

class Belka implements PrefabElement {
    @Override
    public void produkcja() {
        System.out.println("Produkcja belki prefabrykowanej: zbrojenie, forma, pielęgnacja betonu.");
    }
}

class Plyta implements PrefabElement {
    @Override
    public void produkcja() {
        System.out.println("Produkcja płyty stropowej: zbrojenie siatką, betonowanie, wygładzanie powierzchni.");
    }
}

// Twórca (fabryka)
abstract class PrefabFactory {
    public abstract PrefabElement utworzPrefabElement();

    // Szablon metody produkcji
    public void procesProdukcji() {
        PrefabElement element = utworzPrefabElement();
        element.produkcja();
        System.out.println("Kontrola jakości i przygotowanie do transportu.\n");
    }
}

// Konkretne fabryki
class SlupFactory extends PrefabFactory {
    @Override
    public PrefabElement utworzPrefabElement() {
        return new Slup();
    }
}

class BelkaFactory extends PrefabFactory {
    @Override
    public PrefabElement utworzPrefabElement() {
        return new Belka();
    }
}

class PlytaFactory extends PrefabFactory {
    @Override
    public PrefabElement utworzPrefabElement() {
        return new Plyta();
    }
}

// Klasa testowa
public class Main {
    public static void main(String[] args) {
        PrefabFactory slupFabryka = new SlupFactory();
        PrefabFactory belkaFabryka = new BelkaFactory();
        PrefabFactory plytaFabryka = new PlytaFactory();

        slupFabryka.procesProdukcji();
        belkaFabryka.procesProdukcji();
        plytaFabryka.procesProdukcji();
    }
}

Zalety tego podejścia


Designed by Freepik

Elastyczność – łatwe dodawanie nowych typów prefabrykatów (np. „ściana” czy „fundament”) bez zmiany istniejącego kodu,

Hermetyzacja logiki tworzenia – szczegóły budowy elementów są ukryte w fabrykach,

Zgodność z zasadą otwarte-zamknięte (OCP) – można rozszerzać funkcjonalność bez modyfikowania istniejących klas,

Łatwiejsze testowanie – poszczególne fabryki można testować niezależnie.

Wady


Designed by Freepik

Wzrost liczby klas – każda nowa kategoria produktu wymaga stworzenia nowej fabryki,

Dodatkowa warstwa pośrednia – dla prostych systemów może być nadmiarowa,

Potrzeba dobrej struktury dziedziczenia – niepoprawne zaprojektowanie hierarchii produktów i fabryk może prowadzić do trudnej w utrzymaniu architektury