CodeGym /Java blog /Tilfældig /Designmønstre: Abstrakt fabrik
John Squirrels
Niveau
San Francisco

Designmønstre: Abstrakt fabrik

Udgivet i gruppen
Hej! I dag vil vi fortsætte med at studere designmønstre, og vi vil diskutere det abstrakte fabriksmønster . Designmønstre: Abstrakt fabrik - 1Her er, hvad vi vil dække i lektionen:
  • Vi vil diskutere, hvad en abstrakt fabrik er, og hvilket problem dette mønster løser
  • Vi vil skabe skelettet af en applikation på tværs af platforme til bestilling af kaffe gennem en brugergrænseflade
  • Vi vil studere instruktioner om, hvordan man bruger dette mønster, herunder at se på et diagram og en kode
  • Og som en bonus inkluderer denne lektion et skjult påskeæg, der vil hjælpe dig med at lære, hvordan du bruger Java til at bestemme navnet på operativsystemet og, afhængigt af resultatet, udføre en anden handling eller en anden.
For fuldt ud at forstå dette mønster skal du være velbevandret i følgende emner:
  • arv i Java
  • abstrakte klasser og metoder i Java

Hvilke problemer løser en abstrakt fabrik?

En abstrakt fabrik, som alle fabriksmønstre, hjælper os med at sikre, at nye objekter bliver skabt korrekt. Vi bruger det til at styre "produktionen" af forskellige familier af indbyrdes forbundne objekter. Forskellige familier af indbyrdes forbundne objekter... Hvad betyder det? Bare rolig: i praksis er alt enklere, end det ser ud til. Til at begynde med, hvad kunne en familie af indbyrdes forbundne objekter være? Antag, at vi udvikler en militærstrategi, der involverer flere typer enheder:
  • infanteri
  • kavaleri
  • bueskytter
Disse typer enheder er indbyrdes forbundne, fordi de tjener i den samme hær. Vi kan sige, at kategorierne ovenfor er en familie af indbyrdes forbundne objekter. Vi forstår dette. Men det abstrakte fabriksmønster bruges til at arrangere skabelsen af ​​forskellige familier af indbyrdes forbundne objekter. Her er heller ikke noget kompliceret. Lad os fortsætte med militærstrategieksemplet. Generelt set tilhører militære enheder flere forskellige stridende parter. Afhængigt af hvis side de er på, kan militære enheder variere betydeligt i udseende. Den romerske hærs fodsoldater, ryttere og bueskytter er ikke de samme som vikingefodfolk, ryttere og bueskytter. I den militære strategi er soldater fra forskellige hære forskellige familier af indbyrdes forbundne objekter. Det ville være sjovt, hvis en programmør s fejl medførte, at en soldat i en fransk uniform fra Napoleon-tiden, med musket klar, blev fundet gående blandt det romerske infanteri. Det abstrakte fabriksdesignmønster er nødvendigt netop for at løse dette problem. Nej, ikke problemet med den forlegenhed, der kan komme fra tidsrejser, men problemet med at skabe forskellige grupper af indbyrdes forbundne objekter. En abstrakt fabrik giver en grænseflade til at skabe alle tilgængelige produkter (en familie af objekter). En abstrakt fabrik har typisk flere implementeringer. Hver af dem er ansvarlige for at skabe produkter fra en af ​​familierne. Vores militærstrategi vil omfatte en abstrakt fabrik, der skaber abstrakte fodsoldater, bueskytter og kavalerister, samt implementeringer af denne fabrik. For eksempel, en fabrik, der skaber romerske legionærer og en fabrik, der skaber karthagiske soldater. Abstraktion er dette mønsters vigtigste ledende princip. Fabrikkens kunder arbejder kun med fabrikken og dens produkter gennem abstrakte grænseflader. Som følge heraf behøver du ikke tænke på, hvilke soldater der i øjeblikket oprettes. I stedet overfører du dette ansvar til en konkret implementering af den abstrakte fabrik.

Lad os fortsætte med at automatisere vores kaffebar

I sidste lektion, studerede vi fabrikkens metodemønster. Vi brugte det til at udvide vores kaffeforretning og åbne flere nye lokationer. I dag fortsætter vi med at modernisere vores forretning. Ved hjælp af det abstrakte fabriksmønster vil vi lægge grundlaget for en ny desktopapplikation til bestilling af kaffe online. Når vi skriver en desktopapplikation, bør vi altid tænke på support på tværs af platforme. Vores applikation skal fungere på både macOS og Windows (spoiler: Support til Linux er overladt til dig at implementere som hjemmearbejde). Hvordan vil vores ansøgning se ud? Ret simpelt: det vil være en formular, der består af et tekstfelt, et valgfelt og en knap. Hvis du har erfaring med at bruge forskellige operativsystemer, har du helt sikkert bemærket, at knapper på Windows er gengivet anderledes end på en Mac. Som alt andet er... Nå, lad os begynde.
  • knapper
  • tekstfelter
  • udvælgelsesfelter
Ansvarsfraskrivelse: I hver grænseflade kunne vi definere metoder som onClick, onValueChanged, eller onInputChanged. Med andre ord kunne vi definere metoder, der giver os mulighed for at håndtere forskellige begivenheder (tryk på en knap, indtastning af tekst, valg af en værdi i en valgboks). Alt dette er bevidst udeladt her for ikke at overbelaste eksemplet og for at gøre det tydeligere, mens vi studerer fabriksmønsteret. Lad os definere abstrakte grænseflader for vores produkter:

public interface Button {}
public interface Select {}
public interface TextField {}
For hvert operativsystem skal vi oprette grænsefladeelementer i stil med operativsystemet. Vi skriver kode til Windows og MacOS. Lad os lave implementeringer til Windows:

public class WindowsButton implements Button {
}

public class WindowsSelect implements Select {
}

public class WindowsTextField implements TextField {
}
Nu gør vi det samme for MacOS:

public class MacButton implements Button {
}

public class MacSelect implements Select {
}

public class MacTextField implements TextField {
}
Fremragende. Nu kan vi fortsætte til vores abstrakte fabrik, som vil skabe alle tilgængelige abstrakte produkttyper:

public interface GUIFactory {

    Button createButton();
    TextField createTextField();
    Select createSelect();

}
Fantastisk. Som du kan se, har vi ikke gjort noget kompliceret endnu. Alt, hvad der følger, er også enkelt. Analogt med produkterne skaber vi forskellige fabriksimplementeringer for hvert OS. Lad os starte med Windows:

public class WindowsGUIFactory implements GUIFactory {
    public WindowsGUIFactory() {
        System.out.println("Creating GUIFactory for Windows OS");
    }

    public Button createButton() {
        System.out.println("Creating Button for Windows OS");
        return new WindowsButton();
    }

    public TextField createTextField() {
        System.out.println("Creating TextField for Windows OS");
        return new WindowsTextField();
    }

    public Select createSelect() {
        System.out.println("Creating Select for Windows OS");
        return new WindowsSelect();
    }
}
Vi har tilføjet noget konsoloutput inde i metoderne og konstruktøren for yderligere at illustrere, hvad der sker. Nu til macOS:

public class MacGUIFactory implements GUIFactory {
    public MacGUIFactory() {
        System.out.println("Creating GUIFactory for macOS");
    }

    @Override
    public Button createButton() {
        System.out.println("Creating Button for macOS");
        return new MacButton();
    }

    @Override
    public TextField createTextField() {
        System.out.println("Creating TextField for macOS");
        return new MacTextField();
    }

    @Override
    public Select createSelect() {
        System.out.println("Creating Select for macOS");
        return new MacSelect();
    }
}
Bemærk, at hver metodesignatur angiver, at metoden returnerer en abstrakt type. Men inde i metoderne skaber vi specifikke implementeringer af produkterne. Dette er det eneste sted, hvor vi kontrollerer oprettelsen af ​​specifikke forekomster. Nu er det tid til at skrive en klasse til formularen. Dette er en Java-klasse, hvis felter er grænsefladeelementer:

public class CoffeeOrderForm {
    private final TextField customerNameTextField;
    private final Select coffeeTypeSelect;
    private final Button orderButton;

    public CoffeeOrderForm(GUIFactory factory) {
        System.out.println("Creating coffee order form");
        customerNameTextField = factory.createTextField();
        coffeeTypeSelect = factory.createSelect();
        orderButton = factory.createButton();
    }
}
En abstrakt fabrik, der skaber grænsefladeelementer, videregives til formularens konstruktør. Vi vil videregive den nødvendige fabriksimplementering til konstruktøren for at skabe grænsefladeelementer til et bestemt OS.

public class Application {
    private CoffeeOrderForm coffeeOrderForm;

    public void drawCoffeeOrderForm() {
        // Determine the name of the operating system through System.getProperty()
        String osName = System.getProperty("os.name").toLowerCase();
        GUIFactory guiFactory;

        if (osName.startsWith("win")) { // For Windows
            guiFactory = new WindowsGUIFactory();
        } else if (osName.startsWith("mac")) { // For Mac
            guiFactory = new MacGUIFactory();
        } else {
            System.out.println("Unknown OS. Unable to draw form :(");
            return;
        }
        coffeeOrderForm = new CoffeeOrderForm(guiFactory);
    }

    public static void main(String[] args) {
        Application application = new Application();
        application.drawCoffeeOrderForm();
    }
}
Hvis vi kører programmet på Windows, får vi følgende output:

Creating GUIFactory for Windows OS
Creating coffee order form
Creating TextField for Windows OS
Creating Select for Windows OS
Creating Button for Windows OS
På en Mac vil outputtet være som følger:

Creating GUIFactory for macOS
Creating coffee order form
Creating TextField for macOS
Creating Select for macOS
Creating Button for macOS
På Linux:

Unknown OS. Unable to draw form :( 
Og nu opsummerer vi. Vi skrev skelettet af en GUI-baseret applikation, hvor grænsefladeelementerne er skabt specifikt til det relevante OS. Vi gentager kortfattet, hvad vi har lavet:
  • En produktfamilie bestående af et inputfelt, et valgfelt og en knap.
  • Forskellige implementeringer af produktfamilien til Windows og macOS.
  • En abstrakt fabrik, der definerer en grænseflade til at skabe vores produkter.
  • To implementeringer af vores fabrik, hver ansvarlig for at skabe en bestemt familie af produkter.
  • En formular (en Java-klasse), hvis felter er abstrakte grænsefladeelementer, der initialiseres med de nødvendige værdier i konstruktøren ved hjælp af en abstrakt fabrik.
  • Applikationsklasse Inde i denne klasse opretter vi en formular, der sender den ønskede fabriksimplementering til dens konstruktør.
Resultatet er, at vi implementerede det abstrakte fabriksmønster.

Abstrakt fabrik: hvordan man bruger

En abstrakt fabrik er et designmønster til styring af skabelsen af ​​forskellige produktfamilier uden at være bundet til konkrete produktklasser. Når du bruger dette mønster, skal du:
  1. Definer produktfamilier. Antag, at vi har to af dem:
    • SpecificProductA1,SpecificProductB1
    • SpecificProductA2,SpecificProductB2
  2. For hvert produkt i familien skal du definere en abstrakt klasse (grænseflade). I vores tilfælde har vi:
    • ProductA
    • ProductB
  3. Inden for hver produktfamilie skal hvert produkt implementere den grænseflade, der er defineret i trin 2.
  4. Opret en abstrakt fabrik med metoder til at skabe hvert produkt defineret i trin 2. I vores tilfælde vil disse metoder være:
    • ProductA createProductA();
    • ProductB createProductB();
  5. Opret abstrakte fabriksimplementeringer, så hver implementering styrer oprettelsen af ​​produkter fra en enkelt familie. For at gøre dette skal du inde i hver implementering af den abstrakte fabrik implementere alle skabelsesmetoder, så de skaber og returnerer specifikke produktimplementeringer.
Følgende UML-diagram illustrerer instruktionerne skitseret ovenfor: Designmønstre: Abstrakt fabrik - 3Nu vil vi skrive kode i henhold til disse instruktioner:

    // Define common product interfaces
    public interface ProductA {}
    public interface ProductB {}

    // Create various implementations (families) of our products
    public class SpecificProductA1 implements ProductA {}
    public class SpecificProductB1 implements ProductB {}

    public class SpecificProductA2 implements ProductA {}
    public class SpecificProductB2 implements ProductB {}

    // Create an abstract factory
    public interface AbstractFactory {
        ProductA createProductA();
        ProductB createProductB();
    }

    // Implement the abstract factory in order to create products in family 1
    public class SpecificFactory1 implements AbstractFactory {

        @Override
        public ProductA createProductA() {
            return new SpecificProductA1();
        }

        @Override
        public ProductB createProductB() {
            return new SpecificProductB1();
        }
    }

    // Implement the abstract factory in order to create products in family 2
    public class SpecificFactory2 implements AbstractFactory {

        @Override
        public ProductA createProductA() {
            return new SpecificProductA2();
        }

        @Override
        public ProductB createProductB() {
            return new SpecificProductB2();
        }
    }

Lektier

For at forstærke materialet kan du gøre 2 ting:
  1. Forfin kaffebestillingsapplikationen, så den også fungerer på Linux.
  2. Skab din egen abstrakte fabrik til at producere enheder involveret i enhver militær strategi. Dette kan enten være en historisk militærstrategi, der involverer rigtige hære, eller en fantasistrategi med orker, nisser og elvere. Det vigtige er at vælge noget, der interesserer dig. Vær kreativ, udskriv beskeder på konsollen, og nyd at lære om mønstre!
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION