Metoda wytwórcza

Metoda wytwórcza (Factory Method)

Metoda wytwórcza jest kreacyjnym wzorcem projektowym.
Znanna też jako: Konstruktor wirtualny, Virtual constructor

Cel

Metoda wytwórcza to wzorzec projektowy, którego celem jest dostarczenie interfejsu dla klas odpowiedzialnych za tworzenie konkretnego typu obiektów. Metoda wytwórcza definiuje standardowy sposób tworzenia obiektów w sposób niezależny od ich rodzaju. Rozwiązuje problem tworzenia obiektów bez określania ich konkretnych klas.

Metoda wytwórcza udostępnia interfejs do tworzenia obiektów w ramach klasy bazowej, ale pozwala podklasom zmieniać typ tworzonych obiektów. Ten wzorzec pozwala klasie odroczyć tworzenie instancji do podklas. Podklasy mogą nadpisać tę metodę w celu zmiany klasy tworzonych obiektów.

Problem

Powiedzmy, że piszemy apkę dla fabryki produkującej telewizory.
I obecnie nasza aplikacja pozwala tylko na produkcje telewizorów (TV). Dlatego większość naszego kodu znajduje się w klasie TV. Z czasem fabryka się rozrasta. I chcemy dodać do naszej aplikacji produkcję komputerów (Computer), a w przyszłości może produkcję zegarków (Watch).

I co z naszym kodem który już mamy?

Teraz większość kodu naszej aplikacji jest powiązana z klasą TV. Dodanie do naszej aplikacji klasy Computer wymagałoby dokonania zmian w całym naszym kodzie. Co więcej, jeśli później zdecydujemy się dodać Watch, zapewne będziemy musieli dokonać tych zmian kolejny raz. Czego rezultatem będzie brzydki kod, pełen instrukcji warunkowych, których zadaniem będzie dostosowanie zachowania aplikacji zależnie od klasy wybranego produktu.

Rozwiązanie

Wzorzec Metody wytwórczej proponuje zamianę bezpośrednich wywołań konstruktorów obiektów na wywołania specjalnej metody wytwórczej.

Obiekty nadal powstają za pośrednictwem operatora new, ale teraz dokonuje się to za kulisami w środku metody wytwórczej. Obiekty zwracane przez metodę wytwórczą często nazywane są produktami.

Zmiana ta może wydawać się bezcelowa. Przenieśliśmy tylko wywołanie konstruktora z jednej części programu do drugiej. Ale, teraz możemy nadpisać metodę wytwórczą w każdej podklasie, czyli możemy zmienić produkt zwracany przez metodę. Potrzebujemy Telewizora będzie telewizor potrzebujemy Komputera będzie komputer itd.

Oczywiście, podklasy mogą zwracać różne typy produktów tylko wtedy, gdy produkty te mają wspólną klasę bazową lub wspólny interfejs, o tym musimy pamiętać. Ponadto, zwracany typ metody wytwórczej powinien być zgodny z tym interfejsem.

Na przykład zarówno klasa TV, jak i Computer powinny implementować interfejs IProdukt, który z kolei deklaruje metodę Operation. A każda klasa oczywiście różnie implementuje tę metodę. Metoda wytwórcza z klasy TVCreator zwraca obiekty TV, natomiast metoda wytwórcza z klasy ComputerCreator zwraca Computer.

Kod, który korzysta z metody wytwórczej zwany jest często kodem klienckim i nie widzi żadnej różnicy pomiędzy faktycznymi produktami zwróconymi przez różne podklasy. Kod kliencki traktuje wszystkie produkty jako abstrakcyjnie pojęty Produkty. Kod klienta wie, że wszystkie Produkty muszą posiadać metodę Operation, a szczegóły jej działania nie są dla niego w ogóle istotne, są bez znaczenia. Ważne jest tylko to że posiadają metodę Operation.

Przykłady użycia

Wzorzec Metoda wytwórcza jest często stosowany w kodzie C#. Przydaje się gdy chcesz nadać swojemu kodowi wysoki poziom elastyczności.

Identyfikacja

Metody wytwórcze można poznać po metodach kreacyjnych tworzących obiekty na podstawie konkretnych klas, ale zwracających typ abstrakcyjny lub interfejs.

Przykład implementacji

IProduct

Interfejs produktu deklaruje wszystkie operacje, które muszą zaimplementować wszystkie konkretne produkty.

public interface IProduct
{
   string Operation();
}

TV

Konkretne produkty który posiada inną implementacje interfejsu produktu.

class TV : IProduct
{
    public string Operation()
    {
       return "{Operacja z klasy Telewizor}";
    }
}

Computer

Konkretne produkty który posiada inną implementacje interfejsu produktu.

class Computer : IProduct
{
   public string Operation()
   {
       return "{Operacja z klasy Komputer}";
   }
}

Creator

Creator deklaruje metodę fabryczną, która ma zwrócić obiekt klasy Product. Poszczególne podklasy kreatora zwykle zapewniają implementacja tej metody. Kreator może również posiadać jakąś domyślną implementację metody wytwórczej.

Uwaga, pomimo swojej nazwy, głównym zadaniem kreatora nie jest tworzenie produktów. Zamiast tego zawiera jakąś kluczową logikę biznesową która jest zależna od obiektów-produktów zwróconych przez metodę wytwórczą. Podklasy mogą pośrednio zmieniać tę logikę biznesową poprzez nadpisywanie metody wytwórczej i zwracanie innych typów produktów.

abstract class Creator
{            
   public abstract IProduct FactoryMethod();


   public string SomeOperation()
   {
      var product = FactoryMethod();

      var result = "Kod Creator: " + product.Operation();

      return result;
    }
 }

TVCreator

TVCreator – konkretny kreator nadpisują metodę wytwórczą w celu zmiany zwracanego typu produktu.

Zauważ, że podpis metody nadal używa abstrakcyjnego typu produktu, nawet jeśli konkretny produkt jest faktycznie zwracany z metody. W ten sposób Kreator może pozostać niezależny od konkretnych klas produktów.

class TVCreator : Creator
{

   public override IProduct FactoryMethod()
   {
      return new TV(); 
   }
}

ComputerCreator

ComputerCreator – konkretny kreator nadpisują metodę wytwórczą w celu zmiany zwracanego typu produktu.

Zauważ, że podpis metody nadal używa abstrakcyjnego typu produktu, nawet jeśli konkretny produkt jest faktycznie zwracany z metody. W ten sposób Kreator może pozostać niezależny od konkretnych klas produktów.

class ComputerCreator : Creator
{
   public override IProduct FactoryMethod()
   {
      return new Computer();
   }
}

Client

Client – Kod klienta współpracuje z instancją konkretnego kreatora, aczkolwiek poprzez jego bazowy interfejs. Tak długo jak klient będzie współpracował z kreatorem za pośrednictwem interfejsu bazowego, można będzie mu przekazywać dowolną podklasę kreatora.

class Client
{
   public void Main()
   {
      Console.WriteLine("Aplikacja: uruchomiona z TVCreator.");
      ClientCode(new TVCreator());

      Console.WriteLine("");

      Console.WriteLine("Aplikacja: uruchomiona z ComputerCreator.");
      ClientCode(new ComputerCreator());
   }

   public void ClientCode(Creator creator)
   {
      // ...
      Console.WriteLine("Kod Klienta: Nie znam klasy twórcy i nadal pracuje.");
      Console.WriteLine(creator.ToString());
      Console.WriteLine(creator.SomeOperation());
      // ...
   }
}

I wywwołanie klinta w kasie Program

new Client().Main();

Wynik działania

Aplikacja: uruchomiona z TVCreator.
Kod Klienta: Nie znam klasy twórcy i nadal pracuje.
FactoryMethodMoj.TVCreator
Kod Creator: {Operacja z klasy Telewizor}

Aplikacja: uruchomiona z ComputerCreator.
Kod Klienta: Nie znam klasy twórcy i nadal pracuje.
FactoryMethodMoj.ComputerCreator
Kod Creator: {Operacja z klasy Komputer}

Zastosowanie

Metoda Wytwórcza oddziela kod konstruujący produkty od kodu który z tych produktów korzysta. Dlatego łatwiej jest rozszerzać kod konstruujący produkty bez konieczności ingerencji w resztę kodu. Przykładowo, aby dodać nowy typ produktu do aplikacji, będziemy musieli napisać jedynie podklasę kreacyjną i nadpisać jej metodę wytwórczą.

Metodę Wytwórczą stosujemy gdy nie wiemy od razu jakie typy obiektów pojawią się w naszym programie i jakie będą między nimi zależności.

Metodę Wytwórczą stosujemy gdy zamierzamy pozwolić użytkującym naszą bibliotekę lub framework rozbudowywać jej wewnętrzne komponenty.

Zalety i wady

  • Zasada pojedynczej odpowiedzialności. Możesz przenieść kod kreacyjny produktów w jedno miejsce programu, ułatwiając tym samym utrzymanie kodu.
  • Zasada otwarte/zamknięte. Możesz wprowadzić do programu nowe typy produktów bez psucia istniejącego kodu klienckiego.
  • Unikasz ścisłego sprzęgnięcia pomiędzy twórcą a konkretnymi produktami. Oznacza to, że może współpracować z każdym konkretnym wdrożeniem produktu.
  • Kod może się skomplikować, ponieważ aby zaimplementować wzorzec, musisz utworzyć liczne podklasy. W najlepszej sytuacji wprowadzisz ów wzorzec projektowy do już istniejącej hierarchii klas kreacyjnych.

Kod źródłowy

Video YT

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *