Abstraktní tovární vzor - Abstract factory pattern

Diagram tříd UML

Abstraktní továrna poskytuje způsob zapouzdření skupinu jednotlivých závodů , které mají společné téma bez určení jejich konkrétní třídy. Za normálního použití vytvoří klientský software konkrétní implementaci abstraktní továrny a poté použije generické rozhraní továrny k vytvoření konkrétních objektů, které jsou součástí tématu. Klient neví (nebo péče), které konkrétní předměty se dostane z každé z těchto vnitřních továren, protože používá pouze generické rozhraní svých výrobků. Tento vzor odděluje podrobnosti implementace sady objektů od jejich obecného použití a spoléhá na složení objektu, protože vytváření objektů je implementováno v metodách vystavených v rozhraní továrny.

Příkladem toho by mohla být abstraktní tovární třída, DocumentCreatorkterá poskytuje rozhraní pro vytvoření řady produktů (např. createLetter()A createResume()). Systém by měl libovolný počet odvozených konkrétních verzí DocumentCreatortřídy jako FancyDocumentCreatornebo ModernDocumentCreator, každá s jinou implementací createLetter()a createResume()která by vytvořila odpovídající objekt jako FancyLetternebo ModernResume. Každý z těchto produktů je odvozeno od jednoduchého abstraktní třídy , jako Letternebo Resumeze kterého klient si je vědom. Kód by klient získat odpovídající instanci z DocumentCreatora volat jeho tovární metody . Každý z výsledných objektů by byl vytvořen ze stejné DocumentCreatorimplementace a sdílel by společné téma (všechny by byly efektní nebo moderní objekty). Klient by potřeboval vědět pouze to, jak zacházet s abstraktem Letternebo Resumetřídou, nikoli s konkrétní verzí, kterou získal z konkrétní továrny.

Továrna je umístění konkrétní třídy v kódu, ve kterém jsou konstruovány objekty . Záměrem při použití vzoru je izolovat vytváření objektů od jejich použití a vytvářet rodiny souvisejících objektů, aniž by bylo nutné záviset na jejich konkrétních třídách. To umožňuje zavedení nových odvozených typů bez změny kódu, který používá základní třídu .

Použití tohoto vzoru umožňuje vzájemnou výměnu konkrétních implementací beze změny kódu, který je používá, a to i za běhu . Avšak použití tohoto vzoru, stejně jako u podobných návrhových vzorů , může mít za následek zbytečnou složitost a další práci při počátečním psaní kódu. Vyšší úrovně oddělení a abstrakce mohou navíc vést k systémům, které je obtížnější ladit a udržovat.

Přehled

Návrhový vzor Abstract Factory je jedním z dvaceti tří známých návrhových vzorů GoF, které popisují, jak řešit opakující se problémy s návrhem, aby bylo možné navrhnout flexibilní a opakovaně použitelný objektově orientovaný software, tj. Objekty, které lze snadněji implementovat, měnit, testovat, a znovu použít.

Návrhový vzor Abstract Factory řeší problémy jako:

  • Jak může být aplikace nezávislá na tom, jak jsou její objekty vytvořeny?
  • Jak může být třída nezávislá na tom, jak jsou vytvářeny objekty, které vyžaduje?
  • Jak lze vytvořit rodiny souvisejících nebo závislých objektů?

Vytváření objektů přímo v rámci třídy, která objekty vyžaduje, je nepružné, protože zavazuje třídu k určitým objektům a znemožňuje pozdější změnu instance (nezávisle na změně) třídy. Zastaví opětovné použití třídy, pokud jsou vyžadovány další objekty, a ztěžuje testování třídy, protože skutečné objekty nelze nahradit falešnými objekty.

Návrhový vzor Abstract Factory popisuje, jak tyto problémy vyřešit:

  • Zapouzdřit vytvoření objektu do samostatného (továrního) objektu. To znamená, definovat rozhraní (AbstractFactory) pro vytváření objektů a implementovat rozhraní.
  • Třída deleguje vytváření objektů na objekt továrny namísto přímého vytváření objektů.

Díky tomu je třída nezávislá na tom, jak jsou její objekty vytvořeny (které konkrétní třídy jsou instancovány). Třídu lze nakonfigurovat s objektem továrny, který používá k vytváření objektů, a ještě více lze objekt továrny vyměnit za běhu.

Definice

Podstatou vzoru abstraktní továrny je „Poskytnout rozhraní pro vytváření rodin souvisejících nebo závislých objektů bez určení jejich konkrétních tříd.“

Používání

Továrna určuje skutečný konkrétní typ objektu, který má být vytvořen, a je to tady, že objekt je ve skutečnosti vytvořena (v jazyce Java, například tím, že se novým provozovatelem ). Továrna však vrací pouze abstraktní ukazatel na vytvořený konkrétní objekt .

To izoluje kód klienta od vytvoření objektu tím, že klienti požádají objekt továrny o vytvoření objektu požadovaného abstraktního typu a vrácení abstraktního ukazatele na objekt.

Jelikož továrna vrací pouze abstraktní ukazatel, klientský kód (který požadoval objekt z továrny) nezná - a není zatěžován - skutečným konkrétním typem objektu, který byl právě vytvořen. Typ konkrétního objektu (a tedy betonárnu) však abstraktní továrna zná; například továrna jej může číst z konfiguračního souboru. Klient nemusí specifikovat typ, protože již byl zadán v konfiguračním souboru. To zejména znamená:

  • Klientský kód nemá vůbec žádné znalosti konkrétního typu , nemusí obsahovat žádné soubory záhlaví ani deklarace tříd, které se k němu vztahují. Kód klienta se zabývá pouze abstraktním typem. Objekty konkrétního typu skutečně vytváří továrna, ale kód klienta přistupuje k těmto objektům pouze prostřednictvím jejich abstraktního rozhraní .
  • Přidávání nových konkrétních typů se provádí úpravou kódu klienta tak, aby používal jinou továrnu, modifikaci, která je obvykle jeden řádek v jednom souboru. Různá továrna poté vytváří objekty jiného konkrétního typu, ale stále vrací ukazatel stejného abstraktního typu jako dříve - čímž izoluje kód klienta před změnou. To je podstatně jednodušší než úprava kódu klienta k vytvoření instance nového typu, což by vyžadovalo změnu každého umístění v kódu, kde je vytvořen nový objekt (a také zajistit, aby všechna taková umístění kódu měla také znalosti nového konkrétního typu, například zahrnutím konkrétního souboru záhlaví třídy). Pokud jsou všechny objekty továrny uloženy globálně v objektu singleton a veškerý kód klienta prochází singletonem, aby získal přístup do správné továrny pro vytváření objektů, je změna továrny stejně snadná jako změna objektu singleton.

Struktura

UML diagram

Příklad diagramu třídy Metoda createButton na rozhraní GUIFactory vrací objekty typu Button.  Jaká implementace Button je vrácena, závisí na tom, která implementace GUIFactory zpracovává volání metody.
Příklad diagramu tříd Metoda createButtonna GUIFactoryrozhraní vrací objekty typu Button. Jaká implementace Buttonje vrácena, závisí na tom, která implementace GUIFactoryje zpracování volání metody.
Ukázkový diagram třídy UML a sekvence pro návrhový vzor Abstract Factory.  [8]
Ukázkový diagram třídy UML a sekvence pro návrhový vzor Abstract Factory.

Ve výše uvedeném diagramu tříd UML třída , Clientkterá vyžaduje ProductAa ProductBobjekty, neprovádí instanci instancí ProductA1a ProductB1tříd přímo. Místo toho Clientodkazuje na AbstractFactoryrozhraní pro vytváření objektů, díky čemuž je Clientnezávislý na tom, jak jsou objekty vytvářeny (které konkrétní třídy jsou instancovány). Factory1Třída implementuje AbstractFactoryrozhraní vyvoláváním instancí ProductA1a ProductB1tříd.
V UML sekvenční diagram ukazuje run-time interakce: Clientobjekt hovory createProductA()na Factory1objekt, který vytvoří a vrátí ProductA1objekt. Poté se Clientžádá createProductB()o Factory1, která vytvoří a vrátí se ProductB1objekt.

Graf LePUS3

Příklad Pythonu

from abc import ABC, abstractmethod
from sys import platform


class Button(ABC):
    @abstractmethod
    def paint(self):
        pass


class LinuxButton(Button):
    def paint(self):
        return "Render a button in a Linux style"


class WindowsButton(Button):
    def paint(self):
        return "Render a button in a Windows style"


class MacOSButton(Button):
    def paint(self):
        return "Render a button in a MacOS style"


class GUIFactory(ABC):
    @abstractmethod
    def create_button(self):
        pass


class LinuxFactory(GUIFactory):
    def create_button(self):
        return LinuxButton()


class WindowsFactory(GUIFactory):
    def create_button(self):
        return WindowsButton()


class MacOSFactory(GUIFactory):
    def create_button(self):
        return MacOSButton()


if platform == "linux":
    factory = LinuxFactory()
elif platform == "darwin":
    factory = MacOSFactory()
elif platform == "win32":
    factory = WindowsFactory()
else:
    raise NotImplementedError(f"Not implemented for your platform: {platform}")

button = factory.create_button()
result = button.paint()
print(result)

Alternativní implementace využívající samotné třídy jako továrny:

from abc import ABC, abstractmethod
from sys import platform


class Button(ABC):
    @abstractmethod
    def paint(self):
        pass


class LinuxButton(Button):
    def paint(self):
        return "Render a button in a Linux style"


class WindowsButton(Button):
    def paint(self):
        return "Render a button in a Windows style"


class MacOSButton(Button):
    def paint(self):
        return "Render a button in a MacOS style"


if platform == "linux":
    factory = LinuxButton
elif platform == "darwin":
    factory = MacOSButton
elif platform == "win32":
    factory = WindowsButton
else:
    raise NotImplementedError(f"Not implemented for your platform: {platform}")

button = factory()
result = button.paint()
print(result)

Viz také

Reference

externí odkazy