CodeGym /Java Blog /Willekeurig /Ontwerppatronen: abstracte fabriek
John Squirrels
Niveau 41
San Francisco

Ontwerppatronen: abstracte fabriek

Gepubliceerd in de groep Willekeurig
Hoi! Vandaag gaan we verder met het bestuderen van ontwerppatronen en bespreken we het abstracte fabriekspatroon . Ontwerppatronen: Abstracte fabriek - 1Dit is wat we in de les behandelen:
  • We bespreken wat een abstracte fabriek is en welk probleem dit patroon oplost
  • We maken het skelet van een platformonafhankelijke applicatie voor het bestellen van koffie via een gebruikersinterface
  • We bestuderen instructies voor het gebruik van dit patroon, inclusief het bekijken van een diagram en code
  • En als bonus bevat deze les een verborgen paasei waarmee u leert Java te gebruiken om de naam van het besturingssysteem te bepalen en, afhankelijk van het resultaat, de ene of de andere actie uit te voeren.
Om dit patroon volledig te begrijpen, moet u goed thuis zijn in de volgende onderwerpen:
  • erfenis op Java
  • abstracte klassen en methoden in Java

Welke problemen lost een abstracte fabriek op?

Een abstracte fabriek helpt ons, net als alle fabriekspatronen, ervoor te zorgen dat nieuwe objecten correct worden gemaakt. We gebruiken het om de "productie" van verschillende families van onderling verbonden objecten te beheren. Verschillende families van onderling verbonden objecten... Wat betekent dat? Maak je geen zorgen: in de praktijk is alles eenvoudiger dan het lijkt. Wat zou om te beginnen een familie van onderling verbonden objecten kunnen zijn? Stel dat we een militaire strategie ontwikkelen waarbij verschillende soorten eenheden betrokken zijn:
  • infanterie
  • cavalerie
  • boogschutters
Dit soort eenheden zijn met elkaar verbonden, omdat ze in hetzelfde leger dienen. We zouden kunnen zeggen dat de hierboven genoemde categorieën een familie van onderling verbonden objecten zijn. Wij begrijpen dit. Maar het abstracte fabriekspatroon wordt gebruikt om de creatie van verschillende families van onderling verbonden objecten te regelen. Ook hier is niets ingewikkelds. Laten we doorgaan met het voorbeeld van de militaire strategie. Over het algemeen behoren militaire eenheden tot verschillende strijdende partijen. Afhankelijk van aan wiens kant ze staan, kunnen militaire eenheden qua uiterlijk aanzienlijk verschillen. De voetsoldaten, ruiters en boogschutters van het Romeinse leger zijn niet hetzelfde als Viking voetsoldaten, ruiters en boogschutters. In de militaire strategie zijn soldaten van verschillende legers verschillende families van onderling verbonden objecten. Het zou grappig zijn als een programmeur' Door zijn fout liep een soldaat in een Frans uniform uit het Napoleontische tijdperk, met zijn musket in de aanslag, tussen de gelederen van de Romeinse infanterie. Het abstracte fabrieksontwerppatroon is precies nodig om dit probleem op te lossen. Nee, niet het probleem van de schaamte die kan voortvloeien uit tijdreizen, maar het probleem van het creëren van verschillende groepen onderling verbonden objecten. Een abstracte fabriek biedt een interface voor het maken van alle beschikbare producten (een familie van objecten). Een abstracte fabriek heeft doorgaans meerdere implementaties. Elk van hen is verantwoordelijk voor het maken van producten van een van de families. Onze militaire strategie zou een abstracte fabriek omvatten die abstracte voetsoldaten, boogschutters en cavaleristen creëert, evenals implementaties van deze fabriek. Bijvoorbeeld, een fabriek die Romeinse legionairs maakt en een fabriek die Carthaagse soldaten maakt. Abstractie is de belangrijkste leidraad van dit patroon. De klanten van de fabriek werken alleen met de fabriek en haar producten via abstracte interfaces. Hierdoor hoef je niet na te denken over welke soldaten er momenteel worden gemaakt. In plaats daarvan geef je deze verantwoordelijkheid door aan een concrete implementatie van de abstracte fabriek.

Laten we doorgaan met het automatiseren van onze coffeeshop

In de laatste les, bestudeerden we het patroon van de fabrieksmethode. We gebruikten het om onze koffiebusiness uit te breiden en verschillende nieuwe locaties te openen. Vandaag gaan we verder met het moderniseren van ons bedrijf. Aan de hand van het abstracte fabriekspatroon leggen we de basis voor een nieuwe desktop-applicatie om online koffie te bestellen. Bij het schrijven van een desktop-applicatie moeten we altijd denken aan platformonafhankelijke ondersteuning. Onze applicatie moet werken op zowel macOS als Windows (spoiler: ondersteuning voor Linux wordt aan u overgelaten als huiswerk). Hoe gaat onze applicatie eruit zien? Vrij eenvoudig: het wordt een formulier dat bestaat uit een tekstveld, een selectieveld en een knop. Als je ervaring hebt met het gebruik van verschillende besturingssystemen, is het je zeker opgevallen dat knoppen op Windows anders worden weergegeven dan op een Mac. Zoals al het andere... Nou, laten we beginnen.
  • toetsen
  • tekst velden
  • selectie velden
Disclaimer: in elke interface kunnen we methoden definiëren zoals onClick, onValueChanged, of onInputChanged. Met andere woorden, we zouden methoden kunnen definiëren waarmee we verschillende gebeurtenissen kunnen afhandelen (op een knop drukken, tekst invoeren, een waarde in een selectievak selecteren). Dit alles is hier bewust weggelaten om het voorbeeld niet te overbelasten en duidelijker te maken als we het fabriekspatroon bestuderen. Laten we abstracte interfaces voor onze producten definiëren:

public interface Button {}
public interface Select {}
public interface TextField {}
Voor elk besturingssysteem moeten we interface-elementen maken in de stijl van het besturingssysteem. We zullen code schrijven voor Windows en MacOS. Laten we implementaties voor Windows maken:

public class WindowsButton implements Button {
}

public class WindowsSelect implements Select {
}

public class WindowsTextField implements TextField {
}
Nu doen we hetzelfde voor MacOS:

public class MacButton implements Button {
}

public class MacSelect implements Select {
}

public class MacTextField implements TextField {
}
Uitstekend. Nu kunnen we doorgaan naar onze abstracte fabriek, die alle beschikbare abstracte producttypes zal creëren:

public interface GUIFactory {

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

}
Fantastisch. Zoals je kunt zien, hebben we nog niets ingewikkelds gedaan. Alles wat volgt is ook eenvoudig. Naar analogie van de producten maken we per OS verschillende fabrieksimplementaties. Laten we beginnen met 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();
    }
}
We hebben wat console-uitvoer toegevoegd aan de methoden en constructor om verder te illustreren wat er gebeurt. Nu voor 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();
    }
}
Merk op dat elke methodehandtekening aangeeft dat de methode een abstract type retourneert. Maar binnen de methoden creëren we specifieke implementaties van de producten. Dit is de enige plaats waar we het maken van specifieke instanties beheren. Nu is het tijd om een ​​klasse voor het formulier te schrijven. Dit is een Java-klasse waarvan de velden interface-elementen zijn:

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();
    }
}
Een abstracte fabriek die interface-elementen maakt, wordt doorgegeven aan de constructor van het formulier. We zullen de noodzakelijke fabrieksimplementatie doorgeven aan de constructeur om interface-elementen voor een bepaald besturingssysteem te creëren.

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();
    }
}
Als we de toepassing op Windows uitvoeren, krijgen we de volgende uitvoer:

Creating GUIFactory for Windows OS
Creating coffee order form
Creating TextField for Windows OS
Creating Select for Windows OS
Creating Button for Windows OS
Op een Mac is de uitvoer als volgt:

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

Unknown OS. Unable to draw form :( 
En nu vatten we samen. We schreven het skelet van een GUI-gebaseerde applicatie waarin de interface-elementen specifiek voor het relevante besturingssysteem zijn gemaakt. We zullen beknopt herhalen wat we hebben gemaakt:
  • Een productfamilie bestaande uit een invoerveld, een selectieveld en een knop.
  • Verschillende implementaties van de productfamilie voor Windows en macOS.
  • Een abstracte fabriek die een interface definieert voor het maken van onze producten.
  • Twee implementaties van onze fabriek, elk verantwoordelijk voor het creëren van een specifieke productfamilie.
  • Een formulier (een Java-klasse) waarvan de velden abstracte interface-elementen zijn die worden geïnitialiseerd met de benodigde waarden in de constructor met behulp van een abstracte fabriek.
  • Applicatieklasse Binnen deze klasse maken we een formulier en geven we de gewenste fabrieksimplementatie door aan de constructeur.
Het resultaat is dat we het abstracte fabriekspatroon hebben geïmplementeerd.

Abstracte fabriek: hoe te gebruiken

Een abstracte fabriek is een ontwerppatroon voor het beheer van de creatie van verschillende productfamilies zonder gebonden te zijn aan concrete productklassen. Wanneer u dit patroon gebruikt, moet u:
  1. Definieer productfamilies. Stel dat we er twee hebben:
    • SpecificProductA1,SpecificProductB1
    • SpecificProductA2,SpecificProductB2
  2. Definieer voor elk product binnen de familie een abstracte klasse (interface). In ons geval hebben we:
    • ProductA
    • ProductB
  3. Binnen elke productfamilie moet elk product de in stap 2 gedefinieerde interface implementeren.
  4. Maak een abstracte fabriek, met methoden voor het maken van elk product gedefinieerd in stap 2. In ons geval zijn deze methoden:
    • ProductA createProductA();
    • ProductB createProductB();
  5. Maak abstracte fabrieksimplementaties zodat elke implementatie de creatie van producten van een enkele familie regelt. Om dit te doen, moet u binnen elke implementatie van de abstracte fabriek alle creatiemethoden implementeren, zodat ze specifieke productimplementaties creëren en retourneren.
Het volgende UML-diagram illustreert de hierboven geschetste instructies: Ontwerppatronen: Abstracte fabriek - 3Nu gaan we code schrijven volgens deze instructies:

    // 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();
        }
    }

Huiswerk

Om het materiaal te verstevigen kun je 2 dingen doen:
  1. Verfijn de koffie-bestelapplicatie zodat deze ook op Linux werkt.
  2. Creëer je eigen abstracte fabriek voor het produceren van eenheden die betrokken zijn bij elke militaire strategie. Dit kan een historische militaire strategie zijn met echte legers, of een fantasiestrategie met orks, kabouters en elven. Het belangrijkste is om iets te kiezen dat u interesseert. Wees creatief, druk berichten af ​​op de console en geniet van het leren over patronen!
Opmerkingen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION