Press "Enter" to skip to content

Wzorce projektowe – Fabryka abstrakcyjna

Dzisiaj zajmiemy się omówieniem Fabryki Abstrakcyjnej. Fabryka abstrakcyjna pozwala na tworzenie grupy podobnych sobie elementów. Ukrywa ich implementację oraz pozwala w prosty sposób tworzyć podobne do siebie produkty, należące do jednej „rodziny”. Do przykładu fabryki abstrakcyjnej, jako baza, posłuży mi implementacja wzorca metoda fabrykująca.

Fabryka abstrakcyjna dostarcza interfejs do tworzenia całych rodzin spokrewnionych lub zależnych od siebie obiektów bez konieczności określania ich klas rzeczywistych.

Definicja zaczerpnięta z książki Rusz głową! Wzorce projektowe

Diagram fabryki abstrakcyjnej

Przykład rodziny produktów

Przykładem do zrozumienia tego wzorca posłuży nam dodatkowa możliwość wzbogacania wyposażenia naszego zamawianego samochodu. Domyślnie będą możliwe 2 opcje do każdego samochodu:

  • Normalna konfiguracja samochodu
  • Pełna, dodatkowa konfiguracja samochodu

Do zamawianego przez nas samochodu, możemy od dzisiaj zmienić podstawowe wyposażenie na:

  • Lepsze audio
  • Większe koła
  • Ulepszone lampy przednie

Przypomnienie poprzedniego przykadładu

W poprzednim tygodniu opisywałem wzorzec metoda fabrykująca. Przykładem była nasza firma produkująca samochody, która początkowo była umiejscowiona w Polsce i produkowała jeden typ samochodu. Z biegiem czasu się rozrosła i produkowała dwie marki samochodu w dwóch różnych krajach (Polska i Japonia). Każda fabryka posiadała ogólny sposób tworzenia samochodów, jednak każda fabryka wiedziała dokładnie jak tworzyć jedną konkretną markę samochodów – w Polsce było produkowane Audi w Japonii zaś Honda.

Implementacja nowych możliwości

Do implementacji nowego rozwiązania posłużyłem się klasą abstrakcyjną zamiast interfejsem. Jak już wspomniałem we wcześniejszych wpisach (jeśli nie, to wspominam teraz), wzorce projektowe tylko pokazują pewien sposób rozwiązania typowego problemu i nie musimy kurczowo idei opisanej we wzorcu, możemy adaptować wszystko według własnych potrzeb, według potrzeb projektu. Klasa ta definiuje metody, które będą implementowane w konkretnych odmianach fabryk rzeczywistych. Oto diagram zastosowanego rozwiązania:
Diagram implementacji fabryki abstrakcyjnej
Na diagramie przedstawiony został tylko przykład z zastosowaniem nowych lamp aby nie zaśmiecać widoku 🙂 Podobnie do przykładu z lampami implementowane są pozostałe dodatkowe elementy – koła oraz audio.

Powiązanie z metodą fabrykującą

Aby przykład wzorca fabryki abstrakcyjnej można było powiązać z przykładem wzorca metody fabrykującej, trzeba było zmienić trochę implementację istniejących fabryk z zeszłego tygodnia – spokojnie, nie były to wielkie zmiany 😉
Zmiany polegały na utworzeniu zmiennej przechowującej referencję do naszej nowej fabryki z elementami wyposażenia samochodu, która jest przekazywana jako dodatkowy parametr w konstruktorze fabryk. Z racji iż w poprzednim przykładzie była zastosowana klasa abstrakcyjna, w jej podklasach wystarczyło przerobić delikatnie konstruktor aby był w stanie przyjmować dodatkowy parametr oraz przekazywać go do klasy nadrzędnej.
Kolejną zmianą było użycie metod z nowej fabryki podczas zamawiania samochodu oraz dodanie zmiennych przechowujących wybrane elementy w klasie abstrakcyjnej Car.

To by były wszystkie zmiany, które zostały wykonane w implementacji poprzedniego rozwiązania 🙂

Podsumowanie

Fabryka abstrakcyjna przydaje się nam wszędzie tam, gdzie potrzebujemy tworzyć grupy powiązanych ze sobą obiektów. W zamieszczonym przykładzie, grupą taką są elementy wyposażenia samochodu. Dzięki takiemu rozwiązaniu możemy utworzyć samochody ze standardowym wyposażeniem oraz z wyposażeniem dodatkowym, uzależnionym od marki samochodu. Oczywiście nic nie stoi na przeszkodzie, abyśmy w samochodach marki Honda zastosowali światła opracowane przez Audi, jednak nie taki był zamysł naszych fabryk 😉
Jak zawsze wszystko znajduje się na githubie.

Uruchomienie programu:

public class Startup {
    public static void main(String[] args) {
        OurCompany company = new AudiFactory("Poland", "Audi factory", new ExtraPartsAudiFactory());
        System.out.println(company);
        Car car = company.orderCar("A8");
        System.out.println(car);
        company = new AudiFactory("Poland", "Audi factory", new NormalCarPartsFactory());
        car = company.orderCar("A4");
        System.out.println(car);

        company = new HondaFactory("Japan", "Honda factory", new ExtraPartsHondaFactory());
        System.out.println(company);
        car = company.orderCar("ACCORD");
        System.out.println(car);
    }
}

Wyjście programu:

OurCompany{place='Poland', name='Audi factory'}
Car{vin='AUDI', make='Audi', model='A8', year=2016, wheels=[Wheel17In, Wheel17In, Wheel17In, Wheel17In], lamps=[MatrixLamp, MatrixLamp], audio=BoseAudio}
Car{vin='AUDI', make='Audi', model='A4', year=2016, wheels=[Wheel15In, Wheel15In, Wheel15In, Wheel15In], lamps=[NormalLamp, NormalLamp], audio=NormalAudio}
OurCompany{place='Japan', name='Honda factory'}
Car{vin='HONDA', make='Honda', model='Accord', year=2016, wheels=[Wheel16In, Wheel16In, Wheel16In, Wheel16In], lamps=[ADBLamp, ADBLamp], audio=BoseAudio}