Obiekt tworzony w fabryce

Factory pattern, simple factory, factory method, abstract factory

Featured image

Implementacje wzorca Factory

Simple Factory - tworzymy klasę Factory, która bezpośrednio tworzy obiekty. Odpowiedzialność za tworzenie obiektów jest po stronie tej klasy. Dodawanie, modyfikowanie, utrzymanie kodu jest dość trudne.

Factory Method - tworzymy interfejs nadrzędny, który pozwala decydować klasom podrzędnym o tworzeniu obiektu. Odpowiedzialność za tworzenie obiektów jest po stronie klas podrzędnych.

Abstract Factory - zwraca wiele różnych powiązanych ze sobą obiektów. Taka fabryka wielu fabryk. W praktyce mamy interfejs, który posiada jeden lub wiele Factory-Methods.

Czym jest i jak nam pomoże koncepcja Factory?

Kojarzysz na pewno grę, w której był statek kosmiczny i asteroidy. Celem było przetrwać jak najdłużej. Wyobraź sobie tworzenie takiej gry. Musisz stworzyć pojazd. Potem kolejne poziomy trudności. Każdy poziom ma więcej asteroid. Liczba ta progresywnie się powiększa. Poziomu trudności rośnie. Asteroidy mogłyby być tworzone w Factory. Poziom trudności mógłby być tworzony dynamicznie na podstawie podanych parametrów. Cała logika jest ukryta (encapsulation) wewnątrz naszej klasy tworzącej poziom.

A nie mogę po prostu new Factory, po co mi dodatkowa złożoność?

Pomyśl w ten sposób. W obiektowym programowaniu mamy całą masę obiektów. Czasami, aby stworzyć jeden potrzebujemy kilku innych. Nie chce nam się polować na każdy kolejny obiekt zamiast tego mamy Factory. Tworzymy strukturę raz, następnie używamy jej ile tylko chcemy. Kolejnym plusem jest to, że można obiekt stworzyć podczas run-time, czyli wtedy kiedy nasza aplikacja już działa. Pozornie wydaje się, że zastępujesz jedną linijkę kodu drugą. Podstawiasz jedno pod drugie. Często initialization obiektu jest bardziej skomplikowana. W większości przypadków będziesz potrzebować wielu innych obiektów do osiągnięcia celu.

Dlaczego warto używać Factory?

ponowne użycie - decyzja o tworzeniu obiektu jest oddelegowana do innej klasy. Nie musimy przejmować się wszystkimi zależnościami. Tworzenie obiektu dzieje się za kulisami.

rozszerzalność - w momencie, gdy potrzebujemy nowej funkcjonalności po prostu tworzymy kolejną konkretną klasę. Dzięki temu nie naruszamy zasady Open/Closed, która mówi o tym, żeby tworzyć modyfikacje bez naruszania już istniejących funkcji. Zwiększa to stabilność systemu oraz zmniejsza niechciane bóle głowy : )

Kiedy używać Factory?

Zalety używania Factory?

Wady używania Factory?

Simple Factory

// SUPER-CLASS - ogólny typ, wybierający podobne cechy, zachowania...
public interface Animal { ... }
// SUB-CLASSES - konkretne implementacje Animal. Każda może być tworzona w inny sposób.
public class Dog implements Animal { ... }
public class Dinosaur implements Animal { ... } 
public class Salmon implements Animal { ... }
--------------------------------------------------------------------------------
public enum AnimalType { LAND, OCEAN }
// SimpleFactory - stworzenie konkretnej implementacji na podstawie parametrów
public class EarthAnimalsFactory
    public Animal createAnimal(AnimalType type) { ... }
-------------------------------------------------------------------------------- 
new EarthAnimalsFactory().createAnimal( AnimalType.OCEAN )

Factory Method

// SUPER-CLASS - ogólny typ
public interface Animal { ... }
// SUB-CLASSES - konkretne implementacje, każda jest tworzona w inny sposób.
public class Dog implements Animal { ... }
public class Dinosaur implements Animal { ... } 
public class Salmon implements Animal { ... }
--------------------------------------------------------------------------------
// SUPER-CLASS - ogólny typ Factory. Podobnie jak w Animal.
public interface AnimalFactory 
    public Animal createAnimal()
// SUB-CLASS - konkretne implementacje - metoda szablonowa (czyli niezmienna część algorytmu jaki tworzymy) 
public class LandAnimalConcreteFactory { public Animal createAnimal() { ... } }
public class OceanAnimalConcreteFactory { public Animal createAnimal() { ... } }
-------------------------------------------------------------------------------- 
new OceanAnimalConcreteFactory().getAnimal()
new LandAnimalConcreteFactory().getAnimal()

Abstract Factory

// SUPER-CLASS - ogólny typ 
public interface Animal { ... }
// SUB-CLASSES - konkretne implementacje, każda jest tworzona w inny sposób
public class Dog implements Animal { ... }
public class Dinosaur implements Animal { ... } 
public class Salmon implements Animal { ... }
--------------------------------------------------------------------------------
//SUPER-CLASS - ogólny typ Factory. Podobnie jak w Animal. 
public interface AnimalsAbstractFactory {  
    public Animal createLandAnimal();
    public Animal createOceanAnimal();
} 
// SUB-CLASS - konkretne implementacje - tworzenie rodziny obiektów na zasadzie kompozycji
public class EarthAnimalsConcreteFactory {  
    public Animal getLandAnimals() { ... }
    public Animal getOceanAnimals() { ... }
}
public class MarsAnimalsConcreteFactory { ... } 
-------------------------------------------------------------------------------- 
new EarthAnimalsConcreteFactory().getLandAnimals();
new EarthAnimalsConcreteFactory().getOceanAnimals();