Factory method

Przeznaczenie

  • Definiuje interfejs pozwalający na tworzenie obiektów, ale odpowiedzialność za tworzenie obiektów jest delegowana do klas pochodnych

  • Wykorzystuje mechanizm dziedziczenia pozwala klasom pochodnym decydować, jakiej klasy obiekt zostanie utworzony

Kontekst

  • Klasy konkretne charakteryzują się dużą zmiennością

Problem

  • Chcemy tworzyć instancje konkretnych klas w warunkach zależności tylko od abstrakcyjnych interfejsów

  • Klasa nie może przewidzieć, jakich klas obiekty musi tworzyć

  • Informacja o typie tworzonego obiektu (produktu) znana jest dopiero w czasie wykonywania programu

Scenariusz

Chcemy napisać framework do szybkiego tworzenia aplikacji, które zarządzają dokumentami (tekstowymi, html, xml, etc.). Typy dokumentów będą definiowane przez użytkownika. Na poziomie frameworka nie znamy typu dokumentu. Klasa abstrakcyjna Application, zarządzająca dokumentami w aplikacji, definiuje interfejs tworzenia nowego dokumentu – metodę CreateDocument(). Użytkownik, definiując typ dokumentu, musi w klasie pochodnej po Application zaimplementować metodę CreateDocument() tak, aby zwracała prawidłowy typ dokumentu.

_images/Factory_Document_Application_2.png

Struktura

_images/Factory.png

Uczestnicy

Product – definiuje interfejs obiektów tworzonych przez metodę wytwórczą

ConcreteProduct – dziedziczy po klasie abstrakcyjnej Product

Creator

  • deklaruje metodę wytwórczą, która przekazuje obiekt typu Product

  • może implementować domyślną implementację metody wytwórczej, która przekazuje domyślny obiekt typu Product

  • może wywoływać metodę wytwórczą, aby utworzyć obiekt typu Product

ConcreteCreator – przedefiniowuje metodę wytwórczą, żeby przekazywała konkretny produkt

Współpraca

Obiekt klasy Creator deleguje do swoich klas pochodnych odpowiedzialność za takie zdefiniowanie metody wytwórczej, by przekazywała egzemplarz odpowiedniej klasy ConcreteProduct (podklasy klasy abstrakcyjnej Product).

Konsekwencje

  1. Eliminuje potrzebę wstawiania specyficznych dla danej aplikacji klas w kod. Kod aplikacji odwołuje się jedynie do klasy Product, dlatego może działać z dowolnym produktem konkretnym (dowolną podklasą klasy Product).

  2. Tworzenie obiektów wewnątrz klasy za pomocą metody wytwórczej jest bardziej elastyczne niż tworzenie ich bezpośrednio. Wzorzec Factory Method daje podklasom punkt zaczepienia do dostarczenia rozszerzonej wersji obiektu.

  3. Promuje luźne powiązania między obiektami, ponieważ redukuje zależność kodu aplikacji od konkretnych klas.

  4. Umożliwia łączenie równoległych hierarchii klas:

  • równoległe hierarchie klas powstają wtedy, gdy klasa przekazuje niektóre ze swych zobowiązań odrębnej klasie,

  • metoda wytwórcza pozwala zdefiniować związek między dwiema hierarchiami.

_images/Factory_Figure_Manipulator.png

Implementacja

Istnieją dwa główne sposoby implementacji wzorca:

  1. Definiowanie klasy Creator jako:

  • klasy abstrakcyjnej, która nie zapewnia implementacji dla deklarowanej metody wytwórczej - podklasy muszą definiować własną implementację

  • klasy konkretnej zapewniającej domyślną implementację metody wytwórczej

  1. Sparametryzowane metody wytwórcze:

  • metoda wytwórcza ma parametr identyfikujący rodzaj tworzonego obiektu

  • umożliwia pojedynczej metodzie wytwórczej tworzenie wielu rodzajów produktów

class Creator
{
public:
   virtual std::unique_ptr<Product> Create(const std::string& id) const
   {
      if (id == "MY") return std::make_unique<MyProduct>();
      if (id == "YOURS") return std::make_unique<YourProduct>();

      throw std::runtime_error("Unknown ID");
   }
};

class MyCreator : public Creator
{
public:
   std::unique_ptr<Product> Create(const std::string& id) const override
   {
      if (id == "OURS") return std::make_unique<OursProduct>();
      return Creator::Create(id);
   }
};

auto factory = std::make_unique<MyCreator>();
std::unique_ptr<Product> p1 = factory->Create("YOURS");
std::unique_ptr<Product> p2 = factory->Create("OURS");

Wzorce pokrewne

Abstract Factory – fabrykę abstrakcyjną często implementuje się za pomocą metod wytwórczych.

Podsumowanie

  1. Umożliwia inicjowanie przez jeden obiekt procesu tworzenia innego obiektu w sytuacji, gdy nie jest znana klasa tworzonego obiektu.

  2. Kod klienta jest nakierowany na interfejsy.

  3. Umożliwia łączenie równoległych hierarchii klas.