Abstrakt fabriksmønster - Abstract factory pattern

UML klassediagram

Det abstrakte fabriksmønster giver en måde at indkapsle en gruppe individuelle fabrikker, der har et fælles tema uden at specificere deres konkrete klasser. Under normal brug skaber klientsoftwaren en konkret implementering af den abstrakte fabrik og bruger derefter fabrikens generiske grænseflade til at skabe de konkrete objekter, der er en del af temaet. Den klient ikke kender (eller pleje), som konkrete genstande det får fra hver af disse interne fabrikker, da den bruger kun de generiske grænseflader deres produkter. Dette mønster adskiller detaljerne i implementeringen af ​​et sæt objekter fra deres generelle brug og er afhængige af objektsammensætning, da oprettelse af objekter implementeres i metoder, der er eksponeret i fabriksgrænsefladen.

Et eksempel på dette ville være en abstrakt fabriksklasse, DocumentCreatorder giver grænseflader til at oprette et antal produkter (f.eks. createLetter()Og createResume()). Systemet ville have et hvilket som helst antal afledte konkrete versioner af DocumentCreatorklassen som FancyDocumentCreatoreller ModernDocumentCreator, hver med en anden implementering af, createLetter()og createResume()som ville skabe et tilsvarende objekt som FancyLettereller ModernResume. Hvert af disse produkter er afledt af en simpel abstrakt klasse som Lettereller Resumehvoraf kunden er klar over. Klientkoden ville få en passende forekomst af DocumentCreatorog kalde dens fabriksmetoder . Hver af de resulterende objekter blev oprettet fra den samme DocumentCreatorimplementering og ville dele et fælles tema (de ville alle være smarte eller moderne objekter). Klienten behøver kun at vide, hvordan man håndterer abstrakt Lettereller Resumeklasse, ikke den specifikke version, den fik fra betonfabrikken.

En fabrik er placeringen af ​​en betonklasse i koden, hvor genstande konstrueres . Hensigten med at anvende mønsteret er at isolere oprettelsen af ​​objekter fra deres brug og skabe familier af relaterede objekter uden at skulle afhænge af deres konkrete klasser. Dette muliggør introduktion af nye afledte typer uden ændring af koden, der bruger basisklassen .

Brug af dette mønster gør det muligt at udveksle konkrete implementeringer uden at ændre den kode, der bruger dem, selv ved kørselstid . Imidlertid kan anvendelse af dette mønster, som med lignende designmønstre , resultere i unødvendig kompleksitet og ekstra arbejde i den oprindelige skrivning af kode. Derudover kan højere niveauer af adskillelse og abstraktion resultere i systemer, der er sværere at debugge og vedligeholde.

Oversigt

Abstract Factory-designmønsteret er et af de 23 velkendte GoF-designmønstre, der beskriver, hvordan man løser tilbagevendende designproblemer for at designe fleksibel og genanvendelig objektorienteret software, dvs. objekter, der er lettere at implementere, ændre, teste, og genbrug.

Abstrakt Factory design mønster løser problemer som:

  • Hvordan kan en applikation være uafhængig af, hvordan dens objekter oprettes?
  • Hvordan kan en klasse være uafhængig af, hvordan de objekter, den kræver, oprettes?
  • Hvordan kan familier med relaterede eller afhængige genstande oprettes?

Oprettelse af objekter direkte inden for klassen, der kræver objekter, er ufleksibel, fordi det forpligter klassen til bestemte objekter og gør det umuligt at ændre instantiering senere uafhængigt af (uden at skulle ændre) klassen. Det forhindrer, at klassen kan genbruges, hvis der kræves andre objekter, og det gør klassen svært at teste, fordi virkelige objekter ikke kan erstattes med mock-objekter.

Abstract Factory designmønsteret beskriver, hvordan man løser sådanne problemer:

  • Indkapsling af oprettelse af objekt i et separat (fabrik) objekt. Det vil sige definere en grænseflade (AbstractFactory) til oprettelse af objekter og implementere grænsefladen.
  • En klasse delegerer oprettelse af objekt til et fabriksobjekt i stedet for at oprette objekter direkte.

Dette gør en klasse uafhængig af, hvordan dens objekter oprettes (hvilke konkrete klasser er instantierede). En klasse kan konfigureres med et fabriksobjekt, som den bruger til at oprette objekter, og endnu mere kan fabriksobjektet udveksles i løbetid.

Definition

Essensen af ​​det abstrakte fabriksmønster er at "Tilvejebringe en grænseflade til oprettelse af familier af relaterede eller afhængige objekter uden at specificere deres konkrete klasser."

Anvendelse

Den Fabrikken bestemmer den faktiske beton type objekt , der skal oprettes, og det er her, at objektet faktisk er skabt (i Java, for eksempel ved den nye operatør ). Imidlertid returnerer fabrikken kun en abstrakt markør til det oprettede konkrete objekt .

Dette isolerer klientkode fra oprettelse af objekt ved at lade klienter bede et fabriksobjekt om at oprette et objekt af den ønskede abstrakte type og returnere en abstrakt markør til objektet.

Da fabrikken kun returnerer en abstrakt markør, ved klientkoden (der anmodede om objektet fra fabrikken) ikke - og er ikke belastet af - den faktiske konkrete type af objektet, der netop blev oprettet. Imidlertid er typen af ​​et konkret objekt (og dermed en betonfabrik) kendt af den abstrakte fabrik; for eksempel kan fabrikken læse det fra en konfigurationsfil. Klienten har ikke behov for at specificere typen, da den allerede er specificeret i konfigurationsfilen. Dette betyder især:

  • Klienten koden har intet kendskab overhovedet af betonen typen , der ikke behøver at indeholde nogen header filer eller klasse erklæringer relateret til det. Klientkoden behandler kun den abstrakte type. Objekter af en konkret type oprettes faktisk af fabrikken, men klientkoden får kun adgang til sådanne objekter gennem deres abstrakte interface .
  • Tilføjelse af nye betontyper sker ved at ændre klientkoden for at bruge en anden fabrik, en ændring, der typisk er en linje i en fil. Den forskellige fabrik opretter derefter objekter af en anden konkret type, men returnerer stadig en markør af samme abstrakte type som før - og isolerer dermed klientkoden fra ændring. Dette er betydeligt nemmere end at ændre klientkoden for at starte en ny type, hvilket vil kræve ændring af hver placering i koden, hvor et nyt objekt oprettes (samt at sikre, at alle sådanne kodelokationer også har kendskab til den nye betontype, ved f.eks. at medtage en konkret klasseoverskriftfil). Hvis alle fabriksobjekter er lagret globalt i et singleton- objekt, og al klientkode går gennem singleton for at få adgang til den rette fabrik til oprettelse af objekt, er det lige så let at ændre fabrikker som at ændre singleton-objektet.

Struktur

UML-diagram

Eksempel på klassediagram Metoden createButton på GUIFactory-grænsefladen returnerer objekter af typen Button.  Hvilken implementering af Button, der returneres, afhænger af, hvilken implementering af GUIFactory, der håndterer metodekaldet.
Eksempel på klassediagram Metoden createButtonGUIFactorygrænsefladen returnerer objekter af typen Button. Hvilken implementering af Buttonreturneres afhænger af, hvilken implementering GUIFactoryder håndterer metodekaldet.
Et eksempel på UML-klasse og sekvensdiagram for abstrakt fabriksmønster.  [8]
Et eksempel på UML-klasse og sekvensdiagram for abstrakt fabriksmønster.

I ovenstående UML- klassediagram instanterer den Clientklasse, der kræver ProductAog ProductBobjekter, ikke ProductA1og ProductB1klasser direkte. I stedet Clienthenviser henvisningerne til AbstractFactorygrænsefladen til oprettelse af objekter, hvilket gør det Clientuafhængigt af, hvordan objekterne oprettes (hvilke konkrete klasser der instantieres). Den Factory1klasse implementerer AbstractFactorygrænsefladen ved instantiere ProductA1og ProductB1klasser.
De UML sekvensdiagram viser de runtime-interaktioner: Clientobjekt opkald createProductA()Factory1objektet, som skaber og returnerer et ProductA1objekt. Derefter Clientkaldes createProductB()op Factory1, som opretter og returnerer et ProductB1objekt.

LePUS3-diagram

Python- eksempel

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)

Alternativ implementering ved hjælp af klasserne selv som fabrikker:

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)

Se også

Referencer

eksterne links