Jakie są różnice między wzorami projektowania Abstract Factory i Factory?


454

Wiem, że istnieje wiele postów na temat różnic między tymi dwoma wzorami, ale jest kilka rzeczy, których nie mogę znaleźć.

Z tego, co czytałem, widzę, że wzorzec metody fabrycznej pozwala ci zdefiniować, jak stworzyć pojedynczy konkretny produkt, ale ukrywając implementację przed klientem, ponieważ zobaczą ogólny produkt. Moje pierwsze pytanie dotyczy fabryki abstrakcyjnej. Czy jego rola polega na umożliwieniu tworzenia rodzin konkretnych obiektów (które mogą zależeć od konkretnej fabryki, której używasz), a nie tylko jednego konkretnego obiektu? Czy fabryka abstrakcyjna zwraca tylko jeden bardzo duży obiekt lub wiele obiektów w zależności od wywoływanych metod?

Moje ostatnie dwa pytania dotyczą jednego cytatu, którego nie rozumiem w pełni w wielu miejscach:

Jedna różnica między nimi polega na tym, że we wzorcu fabryki abstrakcyjnej klasa przenosi odpowiedzialność tworzenia instancji obiektu na inny obiekt poprzez kompozycję, podczas gdy wzorzec metody fabryki używa dziedziczenia i korzysta z podklasy do obsługi żądanej instancji obiektu.

Rozumiem, że wzorzec metody fabrycznej ma interfejs Creatora, który sprawi, że ConcreteCreator będzie odpowiedzialny za określenie, który konkretny produkt betonowy utworzyć. Czy to oznacza używanie dziedziczenia do obsługi instancji obiektów?

W odniesieniu do tego cytatu, w jaki sposób wzorzec Fabryki Abstrakcyjnej przenosi odpowiedzialność tworzenia instancji obiektu na inny obiekt poprzez kompozycję? Co to znaczy? Wygląda na to, że wzorzec Fabryki Abstrakcyjnej również korzysta z dziedziczenia, aby wykonać proces budowy również w moich oczach, ale znowu uczę się o tych wzorach.

Każda pomoc, szczególnie w przypadku ostatniego pytania, byłaby bardzo mile widziana.



Zobaczenie „jak utworzono instancję” z punktu widzenia klienta, pomoże ci zrozumieć cytat.
Karthik Bose

@nawfal, odpowiedzi w tym wątku są okropne.
jaco0646

Odpowiedzi:


494

Różnica między tymi dwoma

Główna różnica między „metodą fabryczną” a „fabryką abstrakcyjną” polega na tym, że metoda fabryczna jest pojedynczą metodą, a fabryka abstrakcyjna jest obiektem. Myślę, że wielu ludzi myli te dwa terminy i zaczęło ich używać zamiennie. Pamiętam, że ciężko mi było znaleźć dokładnie różnicę, kiedy się ich nauczyłem.

Ponieważ metoda fabryczna jest tylko metodą, można ją zastąpić w podklasie, stąd druga połowa cytatu:

... wzorzec metody fabrycznej wykorzystuje dziedziczenie i wykorzystuje podklasę do obsługi żądanej instancji obiektu.

Cytat zakłada, że ​​obiekt wywołuje tutaj własną metodę fabryczną. Dlatego jedyną rzeczą, która mogłaby zmienić wartość zwracaną, byłaby podklasa.

Fabryka abstrakcyjna to obiekt, który ma wiele metod fabrycznych. Patrząc na pierwszą połowę wyceny:

... dzięki wzorowi fabryki abstrakcyjnej klasa przenosi odpowiedzialność za tworzenie instancji obiektu na inny obiekt poprzez kompozycję ...

Mówią, że istnieje obiekt A, który chce stworzyć obiekt Foo. Zamiast tworzyć sam obiekt Foo (np. Metodą fabryczną), otrzyma inny obiekt (fabryka abstrakcyjna), aby utworzyć obiekt Foo.

Przykłady kodu

Aby pokazać różnicę, stosowana jest metoda fabryczna:

class A {
    public void doSomething() {
        Foo f = makeFoo();
        f.whatever();   
    }

    protected Foo makeFoo() {
        return new RegularFoo();
    }
}

class B extends A {
    protected Foo makeFoo() {
        //subclass is overriding the factory method 
        //to return something different
        return new SpecialFoo();
    }
}

A oto używana abstrakcyjna fabryka:

class A {
    private Factory factory;

    public A(Factory factory) {
        this.factory = factory;
    }

    public void doSomething() {
        //The concrete class of "f" depends on the concrete class
        //of the factory passed into the constructor. If you provide a
        //different factory, you get a different Foo object.
        Foo f = factory.makeFoo();
        f.whatever();
    }
}

interface Factory {
    Foo makeFoo();
    Bar makeBar();
    Aycufcn makeAmbiguousYetCommonlyUsedFakeClassName();
}

//need to make concrete factories that implement the "Factory" interface here

15
To takie świetne wytłumaczenie. Ale to, co najważniejsze, pozostaje bez odpowiedzi, a mianowicie: kiedy użyć jednego, a kiedy drugiego wzoru?
croraf

11
Nie jestem pewien, czy to jest poprawne. Całkiem pewna metoda fabryczna to wzorzec projektowy nazwany na cześć metod fabrycznych, ale obejmuje strukturę klasy i dziedziczenie. To nie jest jedna metoda.
Aviv Cohn

2
Prawidłowe jest więc powiedzenie: metoda Factory może być metodą we wszystkich regularnych klasach o różnych celach. Ale Abstract Factory to klasa / obiekt używany przez klienta i jest TYLKO odpowiedzialny za tworzenie niektórych produktów w rodzinie?
Hieu Nguyen,

Czy używasz słowa „Super” w „SuperFoo” tylko po to, aby oznaczać specjalny przypadek Foo, czy też oznacza to superklasę? Tak jak zakładałem, musi to być podklasa.
dahui

@dahui Tak, to podklasa. Zmieniłem to, SpecialFooaby było bardziej jasne.
Tom Dalling,

125

Fabryka abstrakcyjna tworzy klasę podstawową z metodami abstrakcyjnymi definiującymi metody dla obiektów, które powinny zostać utworzone. Każda klasa fabryki, która wywodzi klasę podstawową, może stworzyć własną implementację każdego typu obiektu.

wprowadź opis zdjęcia tutaj

Metoda fabryczna to po prostu prosta metoda służąca do tworzenia obiektów w klasie. Zwykle jest dodawany w zagregowanym katalogu głównym ( Orderklasa ma metodę o nazwie CreateOrderLine)

wprowadź opis zdjęcia tutaj

Fabryka abstrakcyjna

W poniższym przykładzie projektujemy interfejs, dzięki czemu możemy oddzielić tworzenie kolejek od systemu przesyłania komunikatów, a zatem możemy tworzyć implementacje dla różnych systemów kolejek bez konieczności zmiany podstawy kodu.

interface IMessageQueueFactory
{
  IMessageQueue CreateOutboundQueue(string name);
  IMessageQueue CreateReplyQueue(string name);
}

public class AzureServiceBusQueueFactory : IMessageQueueFactory
{
      IMessageQueue CreateOutboundQueue(string name)
      {
           //init queue
           return new AzureMessageQueue(/*....*/);
      }

      IMessageQueue CreateReplyQueue(string name)
      {
           //init response queue
           return new AzureResponseMessageQueue(/*....*/);
      }

}

public class MsmqFactory : IMessageQueueFactory
{
      IMessageQueue CreateOutboundQueue(string name)
      {
           //init queue
           return new MsmqMessageQueue(/*....*/);
      }

      IMessageQueue CreateReplyQueue(string name)
      {
           //init response queue
           return new MsmqResponseMessageQueue(/*....*/);
      }
}

Metoda fabryczna

Problem w serwerach HTTP polega na tym, że zawsze potrzebujemy odpowiedzi na każde żądanie.

public interface IHttpRequest
{
    // .. all other methods ..

    IHttpResponse CreateResponse(int httpStatusCode);
}

Bez metody fabrycznej użytkownicy serwera HTTP (tj. Programiści) byliby zmuszeni do korzystania z klas specyficznych dla implementacji, które podważają przeznaczenie IHttpRequestinterfejsu.

Dlatego wprowadzamy metodę fabryki, aby tworzenie klasy odpowiedzi również zostało oderwane.

Podsumowanie

Różnica polega na tym, że przeznaczeniem klasy zawierającej metodę fabryczną nie jest tworzenie obiektów , podczas gdy abstrakcyjna fabryka powinna być używana tylko do tworzenia obiektów.

Należy zachować ostrożność podczas korzystania z metod fabrycznych, ponieważ łatwo jest złamać LSP ( zasada podstawienia Liskowa ) podczas tworzenia obiektów.


3
Dlaczego potrzebujemy konkretnego produktu?
Andrew S

60
Ponieważ nikt nie chce inwestować w pomysły.
jgauffin

4
Fabryka abstrakcyjna powinna stworzyć coś więcej niż tylko Button()„rodzinę powiązanych produktów”. Na przykład kanoniczny przykład GoF tworzy ScrollBar()i Window(). Zaletą jest to, że Fabryka abstrakcyjna może egzekwować wspólny temat dla wielu swoich produktów.
jaco0646

Jaco ma rację. Weź pod uwagę, że oba diagramy UML są zasadniczo takie same (oprócz błędnego UML fabryki abstrakcyjnej). W obu przypadkach klient wywołuje fabryczną metodę tworzenia jednego produktu.
hobby

1
@AndrewS: Aby odpowiedzieć na twoje pytanie. Jeśli nie musimy mieć różnych konkretnych produktów (klas) dla tej samej abstrakcji (interfejsu), prawdopodobnie potrzebujemy wzorca konstruktora, a nie wzorca fabrycznego. (lepiej późno niż nigdy;))
jgauffin

95

Różnice między wzorcami projektowymi AbstractFactory i Factory są następujące:

  • Metoda Fabryczna służy do tworzenia tylko jednego produktu, ale w Fabryce Abstrakcyjnej chodzi o tworzenie rodzin powiązanych lub zależnych produktów.
  • Wzorzec metody fabrycznej udostępnia klientowi metodę tworzenia obiektu, natomiast w przypadku fabryki abstrakcyjnej ujawnia rodzinę powiązanych obiektów, które mogą składać się z tych metod fabrycznych.
  • Wzorzec metody fabrycznej ukrywa konstrukcję pojedynczego obiektu, podczas gdy fabryka abstrakcyjna ukrywa budowę rodziny powiązanych obiektów. Fabryki abstrakcyjne są zwykle wdrażane przy użyciu (zestawu) metod fabrycznych.
  • Abstrakcyjny wzorzec fabryczny wykorzystuje kompozycję do przekazania odpowiedzialności za utworzenie obiektu do innej klasy, podczas gdy wzorzec projektowy metody przemysłowej wykorzystuje dziedziczenie i opiera się na klasie pochodnej lub podklasie w celu utworzenia obiektu.
  • Ideą wzorca metody fabrycznej jest to, że pozwala ona na przypadek, w którym klient nie wie, jakie konkretne klasy będzie musiał utworzyć w czasie wykonywania, ale po prostu chce uzyskać klasę, która wykona zadanie, podczas gdy wzorzec abstrakcyjnej fabryki jest najlepiej wykorzystać, gdy system musi tworzyć wiele rodzin produktów lub chcesz udostępnić bibliotekę produktów bez ujawniania szczegółów implementacji.!

Wdrożenie wzorca metody fabrycznej: Metoda fabryczna UML

Wdrożenie abstrakcyjnego wzoru fabrycznego:

Abstract Factory UML


13
Mmm, nie jestem pewien co do przykładu fabryki abstrakcyjnej. Myślę, że fabryka kształtów i fabryka kolorów powinny stosować te same metody. Ale jeśli mam rację, próbka nie ma sensu.
Joaquin Iurchuk

4
Punktory są prawidłowe; jednak oba diagramy są całkowicie błędne i bardzo mylące. Zobacz poniższy schemat z @Trying, aby uzyskać dokładny model fabryki abstrakcyjnej.
jaco0646

1
Muszę się zgodzić, że 2 diagramy rzeczywiście wprowadzają w błąd. Widziałem je na stronie tutorialspoint i szczerze mówiąc, w 100% się z nimi nie zgadzam. Opisy wyglądają jednak dobrze
SoftwareDeveloper

To jest bardzo mylące.
diyoda_

Ponad 50 głosów pozytywnych i diagramy są bardzo błędne. Dowód, że nie można ufać wielu odpowiedziom na wzorce projektowe w SO.
Fuhrmanator,

27

Główną różnicą między Fabryką abstrakcyjną a Metodą fabryczną jest to, że Fabryka abstrakcyjna jest implementowana przez Composition ; ale metoda fabryczna jest wdrażana przez dziedziczenie .

Tak, czytasz to poprawnie: główną różnicą między tymi dwoma wzorami jest stara debata nad kompozycją a dziedziczeniem .

Diagramy UML można znaleźć w książce (GoF). Chcę podać przykłady kodu, ponieważ myślę, że połączenie przykładów z dwóch pierwszych odpowiedzi w tym wątku da lepszą demonstrację niż każda z odpowiedzi osobno. Dodatkowo użyłem terminologii z książki w nazwach klas i metod.

Fabryka abstrakcyjna

  1. Najważniejszym punktem, który należy tutaj zrozumieć, jest wstrzyknięcie klientowi abstrakcyjnej fabryki . Dlatego mówimy, że Abstract Factory jest wdrażany przez Composition. Często środowisko wstrzykiwania zależności wykonuje to zadanie; ale szkielet nie jest wymagany dla DI.
  2. Drugim krytycznym punktem jest to, że konkretne fabryki tutaj nie są implementacjami Metody Fabrycznej! Przykładowy kod metody fabrycznej pokazano poniżej.
  3. I wreszcie trzeci punkt, na który należy zwrócić uwagę, to związek między produktami: w tym przypadku kolejki wychodzące i kolejki odpowiedzi. Jedna betonowa fabryka produkuje kolejki Azure, druga MSMQ. GoF określa tę relację produktu jako „rodzinę” i ważne jest, aby pamiętać, że rodzina w tym przypadku nie oznacza hierarchii klas.
public class Client {
    private final AbstractFactory_MessageQueue factory;

    public Client(AbstractFactory_MessageQueue factory) {
        // The factory creates message queues either for Azure or MSMQ.
        // The client does not know which technology is used.
        this.factory = factory;
    }

    public void sendMessage() {
        //The client doesn't know whether the OutboundQueue is Azure or MSMQ.
        OutboundQueue out = factory.createProductA();
        out.sendMessage("Hello Abstract Factory!");
    }

    public String receiveMessage() {
        //The client doesn't know whether the ReplyQueue is Azure or MSMQ.
        ReplyQueue in = factory.createProductB();
        return in.receiveMessage();
    }
}

public interface AbstractFactory_MessageQueue {
    OutboundQueue createProductA();
    ReplyQueue createProductB();
}

public class ConcreteFactory_Azure implements AbstractFactory_MessageQueue {
    @Override
    public OutboundQueue createProductA() {
        return new AzureMessageQueue();
    }

    @Override
    public ReplyQueue createProductB() {
        return new AzureResponseMessageQueue();
    }
}

public class ConcreteFactory_Msmq implements AbstractFactory_MessageQueue {
    @Override
    public OutboundQueue createProductA() {
        return new MsmqMessageQueue();
    }

    @Override
    public ReplyQueue createProductB() {
        return new MsmqResponseMessageQueue();
    }
}

Metoda fabryczna

  1. Najważniejszą kwestią do zrozumienia tutaj jest ConcreteCreator to, że klient. Innymi słowy, klient jest podklasą, której rodzic definiuje factoryMethod(). Dlatego mówimy, że Metoda Fabryczna jest wdrażana przez Dziedziczenie.
  2. Drugim krytycznym punktem jest zapamiętanie, że Wzorzec Metody Fabrycznej jest niczym innym jak specjalizacją Wzorzec Metody Szablonu. Dwa wzorce mają identyczną strukturę. Różnią się tylko celem. Metoda fabryczna jest kreacyjna (coś buduje), podczas gdy metoda szablonowa jest behawioralna (coś oblicza).
  3. I wreszcie trzecią kwestią do odnotowania jest to, że Creatorklasa (macierzysta) wywołuje swoją własną factoryMethod(). Jeśli usuniemy anOperation()z klasy nadrzędnej, pozostawiając za sobą tylko jedną metodę, nie będzie to już wzorzec metody fabrycznej. Innymi słowy, metoda fabryczna nie może zostać zaimplementowana przy użyciu mniej niż dwóch metod w klasie nadrzędnej; i jedno musi przywołać drugie.
public abstract class Creator {
    public void anOperation() {
        Product p = factoryMethod();
        p.whatever();
    }

    protected abstract Product factoryMethod();
}

public class ConcreteCreator extends Creator {
    @Override
    protected Product factoryMethod() {
        return new ConcreteProduct();
    }
}

Misc. I różne wzory fabryczne

Należy pamiętać, że chociaż GoF definiują dwa różne wzorce fabryczne, nie są to jedyne istniejące wzorce fabryczne. Niekoniecznie są to najczęściej używane wzory fabryczne. Słynny trzeci przykład to Static Factory Pattern Josha Blocha z Effective Java. Książka Head First Design Patterns zawiera jeszcze jeden wzór, który nazywają Simple Factory.

Nie wpadnij w pułapkę zakładając, że każdy wzór Fabryki musi pasować do jednego z GoF.


3
Świetna i bardzo jasna odpowiedź oparta na dobrych przykładach, najlepsza w tym temacie IMO.
Luke Duda

Świetne wyjaśnienie. +1 dla metody Factory musi wywoływać abstrakcyjny punkt metody Factory. Z tym punktem jest o wiele jasne, bez zrozumienia tego punktu: jeśli mamy metodę fabryczną, która nie jest przez nią wywoływana, oznacza to, że będzie używana przez inną klasę, która ją skomponuje, a jej podklasy zostaną wstrzyknięte, zamieni się w abstrakcyjną fabrykę , różnica staje się mniej wyraźna, jeśli chodzi o to, że abstrakcyjna metoda fabryczna musi być przywołana przez samą fabrykę, tak jakby wzorzec metody szablonu nie został zrozumiany
nits.kk

Jeszcze jedna uwaga do pytania. Czy factoryMethod()zawsze powinna być protected metoda we wzorcu „Metoda fabryczna”? (Myślę, że tak)
Jarosław Fedoruk

1
@YaroslavFedoruk, książka GoF dopuszcza publicmetody fabryczne, a metoda nie musi nawet być abstract; ale krytycznym punktem jest to, że sposób ten jest przeznaczony do dziedziczenia, tak że nie może (na przykład) jest albo staticczy final. Stworzyłem metodę protectedi abstracttutaj, aby podkreślić (wymaganą) rozszerzalność.
jaco0646

@ nits.kk, możesz być zainteresowany powiązaną odpowiedzią .
jaco0646

26

Abstract Factory to interfejs do tworzenia powiązanych produktów, ale Factory Method to tylko jedna metoda. Fabrykę abstrakcyjną można wdrożyć wieloma metodami fabrycznymi.

Abstract Factory UML


10
Masz już tę samą odpowiedź tutaj . Jeśli uważasz, że to pytanie jest podobne, oflaguj je jako duplikat.
Ja͢ck,

11

Rozważ ten przykład dla łatwego zrozumienia.

Co zapewniają firmy telekomunikacyjne? Na przykład internet szerokopasmowy, linia telefoniczna i telefon komórkowy. Zostaniesz poproszony o stworzenie aplikacji, aby oferować swoje produkty klientom.

Ogólnie rzecz biorąc, to co tutaj robisz, to tworzenie produktów, tj. Łącza szerokopasmowego, linii telefonicznej i urządzeń mobilnych, za pomocą metody fabrycznej której wiesz, jakie masz właściwości tych produktów i jest to dość proste.

Teraz firma chce zaoferować swojemu klientowi pakiet swoich produktów, tj. Łączność szerokopasmową, linię telefoniczną i telefon komórkowy, i oto gra Abstract Factory .

Innymi słowy, Abstract Factory to skład innych fabryk, które są odpowiedzialne za tworzenie własnych produktów, a Abstract Factory wie, jak umieścić te produkty w sposób bardziej znaczący w odniesieniu do własnych obowiązków.

W tym przypadku BundleFactoryjest Abstract Factory BroadbandFactory, PhonelineFactoryi MobileFactoryFactory. Aby jeszcze bardziej uprościć, fabryki te będą miały metodę fabryczną inicjowania poszczególnych produktów.

Zobacz przykładowy kod poniżej:

public class BroadbandFactory : IFactory {
    public static Broadband CreateStandardInstance() {
        // broadband product creation logic goes here
    }
}

public class PhonelineFactory : IFactory {
    public static Phoneline CreateStandardInstance() {
        // phoneline product creation logic goes here
    }
}

public class MobileFactory : IFactory {
    public static Mobile CreateStandardInstance() {
        // mobile product creation logic goes here
    }
}

public class BundleFactory : IAbstractFactory {

    public static Bundle CreateBundle() {
        broadband = BroadbandFactory.CreateStandardInstance();
        phoneline = PhonelineFactory.CreateStandardInstance();
        mobile = MobileFactory.CreateStandardInstance();

        applySomeDiscountOrWhatever(broadband, phoneline, mobile);
    }

    private static void applySomeDiscountOrWhatever(Broadband bb, Phoneline pl, Mobile m) {
        // some logic here
        // maybe manange some variables and invoke some other methods/services/etc.
    }
}

Mam nadzieję że to pomoże.


1
Nie ma żadnych staticmetod we wzorcach fabrycznych GoF. To jest źle.
jaco0646

5

Przykład z prawdziwego życia. (Łatwe do zapamiętania)

Fabryka

Wyobraź sobie, że budujesz dom i zbliżasz się do cieśli w poszukiwaniu drzwi. Dajesz pomiar drzwi i twoich wymagań, a on zbuduje dla ciebie drzwi. W tym przypadku stolarz jest fabryką drzwi. Twoje dane techniczne są danymi wejściowymi dla fabryki, a drzwi są wyjściem lub produktem z fabryki.

Fabryka abstrakcyjna

Rozważmy teraz ten sam przykład drzwi. Możesz iść do cieśli lub do sklepu z plastikowymi drzwiami lub sklepu z PCV. Wszystkie są fabrykami drzwi. Na podstawie sytuacji decydujesz, do jakiego rodzaju fabryki chcesz się udać. To jest jak Fabryka Abstrakcyjna.

Wyjaśniłem tutaj zarówno wzorzec metody fabrycznej, jak i abstrakcyjny wzorzec fabryczny, zaczynając od nieużywania ich wyjaśniania problemów, a następnie rozwiązywania problemów za pomocą powyższych wzorców https://github.com/vikramnagineni/Design-Patterns/tree/master


3

Wyjaśnijmy jasno, że przez większość czasu w kodzie produkcyjnym używamy abstrakcyjnego wzorca fabrycznego, ponieważ klasa A jest programowana za pomocą interfejsu B. A A musi tworzyć instancje B. Tak więc A musi mieć obiekt fabryczny do tworzenia instancji B Tak więc A nie jest zależne od żadnego konkretnego wystąpienia B. Mam nadzieję, że to pomoże.


3

Zrozum różnice w motywacjach:

Załóżmy, że budujesz narzędzie, w którym masz obiekty i konkretną implementację wzajemnych powiązań obiektów. Ponieważ przewidujesz różnice w obiektach, stworzyłeś pośredniość, przypisując odpowiedzialność za tworzenie wariantów obiektów do innego obiektu ( nazywamy to fabryką abstrakcyjną ). Ta abstrakcja ma dużą zaletę, ponieważ przewiduje się przyszłe rozszerzenia wymagające wariantów tych obiektów.

Inną dość intrygującą motywacją w tej linii myśli jest przypadek, w którym każdy przedmiot z całej grupy będzie miał odpowiedni wariant. W zależności od pewnych warunków, zastosowany zostanie jeden z wariantów, i za każdym razem wszystkie obiekty muszą być tego samego wariantu. Może to być nieco sprzeczne z intuicją, ponieważ często wydaje nam się, że - o ile warianty obiektu są zgodne ze wspólnym jednolitym kontraktem ( interfejs w szerszym znaczeniu ), konkretny kod implementacyjny nigdy nie powinien ulec awarii. Intrygującym faktem jest to, że nie zawsze jest to prawdą, szczególnie gdy oczekiwanego zachowania nie można modelować w umowie programowej.

Prosty ( zapożyczenie pomysłu z GoF ) jest każda aplikacja GUI, która mówi, że wirtualny monitor emuluje wygląd MS, Mac lub Fedory. Tutaj, na przykład, gdy wszystkie obiekty widżetów, takie jak okno, przycisk itp. Mają wariant MS, z wyjątkiem paska przewijania, który pochodzi z wariantu MAC, przeznaczenie narzędzia nie powiedzie się.

Te powyższe przypadki stanowią podstawową potrzebę abstrakcyjnego wzorca fabrycznego .

Z drugiej strony, wyobraź sobie, że piszesz platformę, aby wiele osób mogło zbudować różne narzędzia ( takie jak powyższe przykłady ) za pomocą tej platformy. Zgodnie z ideą frameworku nie musisz tego robić, chociaż nie możesz użyć konkretnych obiektów w swojej logice. Raczej stawiasz kontrakty na wysokim poziomie między różnymi obiektami i sposób ich interakcji. Podczas gdy Ty ( jako programista frameworka ) pozostajesz na bardzo abstrakcyjnym poziomie, każdy budowniczy narzędzia jest zmuszony podążać za twoimi konstrukcjami frameworka. Jednak oni ( twórcy narzędzi ) mają swobodę decydowania, który obiekt ma zostać zbudowany i w jaki sposób wszystkie obiekty, które utworzą, będą oddziaływać. W przeciwieństwie do poprzedniego przypadku ( abstrakcyjnego wzoru fabrycznego ), ty ( jako twórca frameworka)) w tym przypadku nie trzeba pracować z konkretnymi obiektami; i raczej może pozostać na poziomie umownym przedmiotów. Co więcej, w przeciwieństwie do drugiej części poprzednich motywacji, ty lub twórcy narzędzi nigdy nie masz sytuacji mieszania przedmiotów z wariantów. Tutaj, podczas gdy kod frameworka pozostaje na poziomie kontraktu, każdy twórca narzędzi jest ograniczony ( ze względu na naturę samej sprawy ) do używania własnych obiektów. Tworzenie obiektów w tym przypadku jest delegowane do każdego implementatora, a dostawcy ram zapewniają tylko jednolite metody tworzenia i zwracania obiektów. Takie metody są nieuniknione dla programisty frameworka, aby kontynuowały kod i mają specjalną nazwę o nazwie Metoda fabryczna ( Wzorzec metody fabrycznej dla wzorca bazowego ).

Kilka uwag:

  • Jeśli znasz „metodę szablonów”, zobaczysz, że metody fabryczne są często wywoływane z metod szablonów w przypadku programów dotyczących dowolnej formy frameworka. Natomiast metody szablonów programów użytkowych są często prostą implementacją określonego algorytmu i pozbawione metod fabrycznych.
  • Co więcej, dla kompletności myśli, przy użyciu frameworka ( wspomnianego powyżej ), gdy budowniczy narzędzi buduje narzędzie, w ramach każdej metody fabrycznej, zamiast tworzyć konkretny obiekt, może on / ona dalej przekazać odpowiedzialność streszczeniu - obiekt fabryczny, pod warunkiem, że konstruktor narzędzia przewiduje odmiany konkretnych obiektów dla przyszłych rozszerzeń.

Przykładowy kod:

//Part of framework-code
BoardGame {
    Board createBoard() //factory method. Default implementation can be provided as well
    Piece createPiece() //factory method

    startGame(){        //template method
         Board borad = createBoard()
         Piece piece = createPiece()
         initState(board, piece)
    }
}


//Part of Tool-builder code
Ludo inherits  BoardGame {
     Board createBoard(){ //overriding of factory method
         //Option A: return new LudoBoard() //Lodu knows object creation
         //Option B: return LudoFactory.createBoard() //Lodu asks AbstractFacory
     }
….
}

//Part of Tool-builder code
Chess inherits  BoardGame {
    Board createBoard(){ //overriding of factory method
        //return a Chess board
    }
    ….
}

3
  1. Moje pierwsze pytanie dotyczy fabryki abstrakcyjnej. Czy jego rola polega na umożliwieniu tworzenia rodzin konkretnych obiektów (które mogą zależeć od konkretnej fabryki, której używasz), a nie tylko jednego konkretnego obiektu?

Tak. Intencją fabryki abstrakcyjnej jest:

Zapewnij interfejs do tworzenia rodzin powiązanych lub zależnych obiektów bez określania ich konkretnych klas.


  1. Czy fabryka abstrakcyjna zwraca tylko jeden bardzo duży obiekt lub wiele obiektów w zależności od wywoływanych metod?

Idealnie powinien zwrócić jeden obiekt na metodę, którą wywołuje klient.

  1. Rozumiem, że wzorzec metody fabrycznej ma interfejs Creatora, który sprawi, że ConcreteCreator będzie odpowiedzialny za określenie, który konkretny produkt betonowy utworzyć. Czy to oznacza używanie dziedziczenia do obsługi instancji obiektów?

Tak. Metoda fabryczna wykorzystuje dziedziczenie.

  1. Abstrakcyjny wzór fabryki deleguje odpowiedzialność tworzenia instancji obiektu na inny obiekt poprzez kompozycję? Co to znaczy?

AbstractFactory definiuje FactoryMethod, a ConcreteFactory odpowiada za budowę produktu betonowego. Wystarczy postępować zgodnie z przykładem kodu w tym artykule .

Możesz znaleźć więcej szczegółów w powiązanych postach SE:

Jaka jest podstawowa różnica między fabrycznymi i abstrakcyjnymi wzorami fabrycznymi?

Wzory projektowe: Fabryka kontra metoda fabryczna kontra fabryka abstrakcyjna


3

Metoda fabryczna polega na dziedziczeniu: tworzenie obiektów jest delegowane do podklas, które implementują metodę fabryczną do tworzenia obiektów.

Fabryka abstrakcyjna opiera się na składzie obiektu: tworzenie obiektu jest realizowane metodami ujawnionymi w interfejsie fabryki.

Schemat wysokiego poziomu schematu fabrycznego i abstrakcyjnego,

diagram

Aby uzyskać więcej informacji na temat metody Factory, zapoznaj się z tym artykułem .

Aby uzyskać więcej informacji na temat metody fabryki abstrakcyjnej, patrz tym artykułem .


2

Aby było to bardzo proste z minimalnym interfejsem i skoncentruj się na „// 1”:

class FactoryProgram
    {
        static void Main()
        {
            object myType = Program.MyFactory("byte");
            Console.WriteLine(myType.GetType().Name);

            myType = Program.MyFactory("float"); //3
            Console.WriteLine(myType.GetType().Name);

            Console.ReadKey();
        }

        static object MyFactory(string typeName)
        {
            object desiredType = null; //1
            switch (typeName)
            {
                case "byte": desiredType = new System.Byte(); break; //2
                case "long": desiredType = new System.Int64(); break;
                case "float": desiredType = new System.Single(); break;
                default: throw new System.NotImplementedException();
            }
            return desiredType;
        }
    }

Oto ważne punkty: 1. Mechanizmy Factory i AbstractFactory muszą używać dziedziczenia (System.Object-> bajt, zmiennoprzecinkowy ...); więc jeśli masz dziedziczenie w programie, wówczas Fabryka (Fabryka Abstrakcyjna najprawdopodobniej tam nie byłaby) jest już tam według projektu 2. Twórca (MyFactory) wie o typie betonu, więc zwraca obiekt typu betonowego do obiektu wywołującego (Główny); W abstrakcyjnym typie zwrotu fabrycznego byłby interfejs.

interface IVehicle { string VehicleName { get; set; } }
interface IVehicleFactory
    {
        IVehicle CreateSingleVehicle(string vehicleType);
    }
class HondaFactory : IVehicleFactory
    {
        public IVehicle CreateSingleVehicle(string vehicleType)
        {
            switch (vehicleType)
            {
                case "Sports": return new SportsBike();
                case "Regular":return new RegularBike();
                default: throw new ApplicationException(string.Format("Vehicle '{0}' cannot be created", vehicleType));
            }
        }
    }
class HeroFactory : IVehicleFactory
    {
        public IVehicle CreateSingleVehicle(string vehicleType)
        {
            switch (vehicleType)
            {
                case "Sports":  return new SportsBike();
                case "Scooty": return new Scooty();
                case "DarkHorse":return new DarkHorseBike();
                default: throw new ApplicationException(string.Format("Vehicle '{0}' cannot be created", vehicleType));
            }
        }
    }

class RegularBike : IVehicle { public string VehicleName { get { return "Regular Bike- Name"; } set { VehicleName = value; } } }
class SportsBike : IVehicle { public string VehicleName { get { return "Sports Bike- Name"; } set { VehicleName = value; } } }
class RegularScooter : IVehicle { public string VehicleName { get { return "Regular Scooter- Name"; } set { VehicleName = value; } } }
class Scooty : IVehicle { public string VehicleName { get { return "Scooty- Name"; } set { VehicleName = value; } } }
class DarkHorseBike : IVehicle { public string VehicleName { get { return "DarkHorse Bike- Name"; } set { VehicleName = value; } } }

class Program
{
    static void Main(string[] args)
    {
        IVehicleFactory honda = new HondaFactory(); //1
        RegularBike hondaRegularBike = (RegularBike)honda.CreateSingleVehicle("Regular"); //2
        SportsBike hondaSportsBike = (SportsBike)honda.CreateSingleVehicle("Sports");
        Console.WriteLine("******* Honda **********"+hondaRegularBike.VehicleName+ hondaSportsBike.VehicleName);

        IVehicleFactory hero = new HeroFactory();
        DarkHorseBike heroDarkHorseBike = (DarkHorseBike)hero.CreateSingleVehicle("DarkHorse");
        SportsBike heroSportsBike = (SportsBike)hero.CreateSingleVehicle("Sports");
        Scooty heroScooty = (Scooty)hero.CreateSingleVehicle("Scooty");
        Console.WriteLine("******* Hero **********"+heroDarkHorseBike.VehicleName + heroScooty.VehicleName+ heroSportsBike.VehicleName);

        Console.ReadKey();
    }
}

Ważne punkty: 1. Wymaganie: Honda stworzy „Regular”, „Sport”, ale Hero stworzy „DarkHorse”, „Sports” i „Scooty”. 2. dlaczego dwa interfejsy? Jeden dla typu producenta (IVehicleFactory), a drugi dla fabryki produktu (IVehicle); innym sposobem na zrozumienie 2 interfejsów jest fabryka abstrakcyjna, polegająca na tworzeniu powiązanych obiektów 2. Chwytem jest powrót dzieci IVehicleFactory i IVehicle (zamiast betonu w fabryce); więc otrzymuję zmienną nadrzędną (IVehicle); następnie tworzę rzeczywisty typ betonu, wywołując CreateSingleVehicle, a następnie rzutując obiekt nadrzędny na rzeczywisty obiekt podrzędny. Co by się stało, gdybym to zrobił RegularBike heroRegularBike = (RegularBike)hero.CreateSingleVehicle("Regular");; dostaniesz ApplicationException i dlatego potrzebujemy generycznej fabryki abstrakcyjnych, które wyjaśnię w razie potrzeby.



0

W każdej chwili wolałbym Abstract Factory od Factory Method. Z powyższego przykładu Toma Dallinga (świetne wyjaśnienie btw) widzimy, że Abstract Factory jest bardziej podatna na kompozycję, ponieważ wszystko, co musimy zrobić, to przekazać konstruktorowi inną Fabrykę (tutaj stosuje się wstrzykiwanie zależności konstruktorów). Ale metoda fabryczna wymaga od nas wprowadzenia nowej klasy (więcej rzeczy do zarządzania) i zastosowania podklas. Zawsze preferuj kompozycję niż dziedziczenie.


0

pozwól mi to precyzyjnie ująć. większość odpowiedzi już wyjaśniła, podając również schematy i przykłady. więc moja odpowiedź będzie tylko jedną linią. moje własne słowa: - „abstrakcyjny wzór fabryki dodaje warstwę abstrakcyjną w wielu implementacjach metod fabrycznych. oznacza, że ​​abstrakcyjna fabryka zawiera lub składa jeden lub więcej wzorów metod fabrycznych ”


To nie jest poprawne. Jest to zbyt powszechne błędne przekonanie, że Abstract Factory to nic innego jak fabryka fabryk.
jaco0646,

0

Wiele z powyższych odpowiedzi nie zapewnia porównania kodu między wzorcem Abstract Factory i Factory Method. Oto moja próba wyjaśnienia tego za pomocą Java. Mam nadzieję, że pomoże to komuś potrzebującemu prostego wyjaśnienia.

Jak trafnie mówi GoF: Abstract Factory zapewnia interfejs do tworzenia rodzin powiązanych lub zależnych obiektów bez określania ich konkretnych klas.

        public class Client {
            public static void main(String[] args) {
               ZooFactory zooFactory = new HerbivoreZooFactory();       
               Animal animal1 = zooFactory.animal1();
               Animal animal2 = zooFactory.animal2();
               animal1.sound();
               animal2.sound();

               System.out.println();

               AnimalFactory animalFactory = new CowAnimalFactory();
               Animal animal = animalFactory.createAnimal();
               animal.sound();
            }
        }

        public interface Animal {
            public void sound();
        }

        public class Cow implements Animal {

            @Override
            public void sound() {
                System.out.println("Cow moos");
            }

        }

        public class Deer implements Animal {

            @Override
            public void sound() {
                System.out.println("Deer grunts");
            }

        }

        public class Hyena implements Animal {

            @Override
            public void sound() {
                System.out.println("Hyena.java");
            }

        }

        public class Lion implements Animal {

            @Override
            public void sound() {
                System.out.println("Lion roars");
            }

        }

        public interface ZooFactory {
            Animal animal1();

            Animal animal2();
        }

        public class CarnivoreZooFactory implements ZooFactory {

            @Override
            public Animal animal1() {
                return new Lion();
            }

            @Override
            public Animal animal2() {
                return new Hyena();
            }

        }

        public class HerbivoreZooFactory implements ZooFactory{

            @Override
            public Animal animal1() {
                return new Cow();
            }

            @Override
            public Animal animal2() {
                return new Deer();
            }

        }

        public interface AnimalFactory {
            public Animal createAnimal();
        }

        public class CowAnimalFactory implements AnimalFactory{

            @Override
            public Animal createAnimal() {
                return new Cow();
            }

        }

        public class DeerAnimalFactory implements AnimalFactory{

            @Override
            public Animal createAnimal() {
                return new Deer();
            }

        }

        public class HyenaAnimalFactory implements AnimalFactory{

            @Override
            public Animal createAnimal() {
                return new Hyena();
            }

        }

        public class LionAnimalFactory implements AnimalFactory{

            @Override
            public Animal createAnimal() {
                return new Lion();
            }

        }

To nie jest poprawne. Ten kod implementuje zbyt powszechne błędne przekonanie, że Abstract Factory to nic innego jak fabryka fabryk.
jaco0646,

1
@ jaco0646 Wierzę, że we wzorcu metody fabrycznej nacisk kładzie się na wydobycie tylko jednego konkretnego produktu z FactoryImpl. Podczas gdy w abstrakcyjnym wzorze fabrycznym FactoryImpls są odpowiedzialne za zapewnienie wielu podobnych / powiązanych produktów betonowych, dla których interfejs Factory zapewnia umowę. ZooFactory nie jest więc, jak mówisz, fabryką fabryk, a jedynie interfejsem, którego Impls dostarcza produkty betonowe, które są ze sobą powiązane. Jeśli nie zgadzasz się, popraw moje rozumienie.
Jatin Shashoo,

W metodzie fabrycznej nacisk kładziony jest na dziedziczenie poprzez podklasę, ponieważ metoda fabryczna jest specjalizacją wzorca metody szablonowej. Powyższa najczęściej głosowana odpowiedź pokazuje przykład porządnego kodu.
jaco0646

@ jaco0646 1. Czy to oznacza, że ​​w powyższym przykładzie zamiast używania interfejsów dla AnimalFactory i zapewniania jego implementacji, powinienem był użyć klasy i zastąpić metodę createAnimal () w jej podklasach: CowAnimalFactory, LionAnimalFactory itp.? 2. Co myślisz o przykładzie pokazanym dla ZooFactory?
Jatin Shashoo,

Na pierwsze pytanie: tak. Do drugiego dodałem własną odpowiedź do tego wątku zamiast krytykować każdą indywidualną odpowiedź.
jaco0646
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.