Fabryka jest to jeden z najpopularniejszych wzorców projektowych stosowanych w programowaniu. Wzorzec ten pozwala na tworzenie obiektów bez konieczności ujawniania implementacji tworzonych obiektów. W ten sposób można ukryć szczegóły implementacji oraz zwiększyć elastyczność i skalowalność kodu.

W tym artykule skupimy się na dwóch najczęściej stosowanych formach wzorca Fabryka: metodzie fabrykującej i fabryce abstrakcyjnej.

Metoda fabrykująca to wzorzec projektowy, który pozwala na tworzenie obiektów poprzez wywołanie metody, zamiast bezpośredniego tworzenia obiektu przez konstruktor. Dzięki temu możemy wydzielić proces tworzenia obiektów do osobnej klasy, która zajmuje się tylko tym procesem. W ten sposób unikamy powtarzania kodu oraz ułatwiamy utrzymanie i testowanie naszego kodu.

public interface Animal {
   void makeSound();
}

public class Dog implements Animal {
   @Override
   public void makeSound() {
      System.out.println("Bark bark!");
   }
}

public class Cat implements Animal {
   @Override
   public void makeSound() {
      System.out.println("Meow!");
   }
}

public class AnimalFactory {
   public static Animal createAnimal(String animalType) {
      switch(animalType) {
         case "dog":
            return new Dog();
         case "cat":
            return new Cat();
         default:
            throw new IllegalArgumentException("Unsupported animal type: " + animalType);
      }
   }
}
Animal dog = AnimalFactory.createAnimal("dog");
dog.makeSound(); // Output: Bark bark!

Animal cat = AnimalFactory.createAnimal("cat");
cat.makeSound(); // Output: Meow

W powyższym kodzie, mamy interfejs Animal, który reprezentuje zwierzę i definiuje metodę makeSound(). Następnie mamy dwie klasy: Dog i Cat, które implementują ten interfejs.

W klasie AnimalFactory mamy metodę fabrykującą createAnimal(), która przyjmuje parametr animalType określający, jaki rodzaj zwierzęcia chcemy utworzyć. W metodzie tej tworzymy nowy obiekt na podstawie przekazanego typu i zwracamy go jako wynik. Jeśli zostanie podany nieobsługiwany typ, zostanie zgłoszony wyjątek IllegalArgumentException.

Dzięki takiemu podejściu, klienci mogą korzystać z metody createAnimal() do tworzenia obiektów zwierząt bez wiedzy jak dokładnie te obiekty są tworzone. W przypadku dodania nowego typu zwierzęcia, wystarczy dodać nową klasę i zaktualizować metodę fabrykującą.

Wzorzec fabryki abstrakcyjnej to wzorzec projektowy, który umożliwia tworzenie rodzin powiązanych ze sobą obiektów bez konieczności określania ich konkretnych klas. Zamiast tego, korzysta się z interfejsów i klas abstrakcyjnych, które definiują ogólne zachowania i właściwości obiektów. Dzięki temu, fabryka abstrakcyjna umożliwia łatwe dodawanie nowych typów obiektów bez modyfikowania istniejącego kodu.

Załóżmy, że mamy do czynienia z dwoma rodzajami pizzy: włoską i amerykańską. Każdy rodzaj pizzy składa się z ciasta, sosu i dodatków, ale w każdym przypadku składniki są inne. Aby uniknąć bezpośredniego tworzenia obiektów pizzy, można zastosować fabrykę abstrakcyjną.

Najpierw należy utworzyć interfejs abstrakcyjnej fabryki, który definiuje metody abstrakcyjne dla każdego elementu pizzy:

public interface PizzaFactory {
    public Dough createDough();
    public Sauce createSauce();
    public Toppings createToppings();
}

Następnie utworzymy klasy implementujące interfejs fabryki dla każdej rodziny pizzy. Pierwszą będzie włoska fabryka:

public class ItalianPizzaFactory implements PizzaFactory {
    public Dough createDough() {
        return new ThinCrust();
    }

    public Sauce createSauce() {
        return new Marinara();
    }

    public Toppings createToppings() {
        return new ItalianToppings();
    }
}

Drugą fabryką będzie amerykańska fabryka pizzy:

public class AmericanPizzaFactory implements PizzaFactory {
    public Dough createDough() {
        return new ThickCrust();
    }

    public Sauce createSauce() {
        return new BBQ();
    }

    public Toppings createToppings() {
        return new AmericanToppings();
    }
}

Teraz, w celu utworzenia pizzy, można wykorzystać odpowiednią fabrykę w następujący sposób:

PizzaFactory factory = new ItalianPizzaFactory();
Dough dough = factory.createDough();
Sauce sauce = factory.createSauce();
Toppings toppings = factory.createToppings();

Pizza pizza = new Pizza(dough, sauce, toppings);

Dzięki temu, zamiast tworzyć obiekty pizzy bezpośrednio, wykorzystujemy fabrykę abstrakcyjną, która tworzy obiekty konkretne dla każdej rodziny pizzy. W ten sposób wzorzec projektowy fabryka abstrakcyjna pozwala na łatwe dodawanie nowych rodzajów pizzy do systemu, ponieważ wystarczy tylko utworzyć nową klasę fabryki, która implementuje interfejs abstrakcyjnej fabryki.