CodeGym /Java-Blog /Random-DE /Entwurfsmuster: Factory-Methode
Autor
Andrey Gorkovenko
Frontend Engineer at NFON AG

Entwurfsmuster: Factory-Methode

Veröffentlicht in der Gruppe Random-DE
Hallo! Heute werden wir uns weiterhin mit Entwurfsmustern befassen und das Fabrikmethodenmuster diskutieren. Entwurfsmuster: Factory-Methode – 1 Sie erfahren, was es ist und für welche Aufgaben dieses Muster geeignet ist. Wir werden dieses Entwurfsmuster in der Praxis betrachten und seine Struktur untersuchen. Um sicherzustellen, dass alles klar ist, müssen Sie die folgenden Themen verstehen:
  1. Vererbung in Java.
  2. Abstrakte Methoden und Klassen in Java

Welches Problem löst die Factory-Methode?

Bei allen Fabrikdesignmustern gibt es zwei Arten von Teilnehmern: Schöpfer (die Fabriken selbst) und Produkte (die von den Fabriken geschaffenen Objekte). Stellen Sie sich die folgende Situation vor: Wir haben eine Fabrik, die Autos der Marke CodeGym produziert. Es weiß, wie man Automodelle mit verschiedenen Karosserietypen erstellt:
  • Limousinen
  • Kombis
  • Coupés
Unser Geschäft florierte so sehr, dass wir eines schönen Tages einen anderen Automobilhersteller erwarben – OneAuto. Da wir als vernünftige Unternehmer keine OneAuto-Kunden verlieren wollen, stehen wir vor der Aufgabe, die Produktion so umzustrukturieren, dass wir produzieren können:
  • CodeGym-Limousinen
  • CodeGym-Kombis
  • CodeGym-Coupés
  • OneAuto-Limousinen
  • OneAuto Kombis
  • OneAuto-Coupés
Wie Sie sehen, haben wir statt einer Produktgruppe nun zwei, die sich in einigen Details unterscheiden. Das Entwurfsmuster der Fabrikmethode ist für den Fall gedacht, dass wir verschiedene Produktgruppen erstellen müssen, von denen jede bestimmte spezifische Merkmale aufweist. Wir werden das Leitprinzip dieses Musters in der Praxis betrachten und dabei schrittweise vom Einfachen zum Komplexen übergehen, am Beispiel unseres Cafés, das wir in einer der vorherigen Lektionen erstellt haben .

Ein bisschen über das Fabrikmuster

Ich möchte Sie daran erinnern, dass wir zuvor ein kleines virtuelles Café aufgebaut haben. Mithilfe einer einfachen Fabrik lernten wir, wie man verschiedene Kaffeesorten herstellt. Heute werden wir dieses Beispiel noch einmal überarbeiten. Erinnern wir uns daran, wie unser Café mit seiner einfachen Fabrik aussah. Wir hatten einen Kaffeekurs:

public class Coffee {
    public void grindCoffee(){
        // Grind the coffee
    }
    public void makeCoffee(){
        // Brew the coffee
    }
    public void pourIntoCup(){
        // Pour into a cup
    }
}
Und mehrere Kinderkurse, die sich auf bestimmte Kaffeesorten beziehen, die unsere Fabrik produzieren könnte:

public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Wir haben eine Enumeration erstellt, um das Aufgeben von Bestellungen zu vereinfachen:

public enum CoffeeType {
    ESPRESSO,
    AMERICANO,
    CAFFE_LATTE,
    CAPPUCCINO
}
Die Kaffeefabrik selbst sah so aus:

public class SimpleCoffeeFactory {
    public Coffee createCoffee(CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new Americano();
                break;
            case ESPRESSO:
                coffee = new Espresso();
                break;
            case CAPPUCCINO:
                coffee = new Cappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new CaffeLatte();
                break;
        }
        
        return coffee;
    }
}
Und schließlich sah das Café selbst so aus:

public class CoffeeShop {

    private final SimpleCoffeeFactory coffeeFactory;

    public CoffeeShop(SimpleCoffeeFactory coffeeFactory) {
        this.coffeeFactory = coffeeFactory;
    }

    public Coffee orderCoffee(CoffeeType type) {
        Coffee coffee = coffeeFactory.createCoffee(type);
        coffee.grindCoffee();
        coffee.makeCoffee();
        coffee.pourIntoCup();

        System.out.println("Here's your coffee! Thanks! Come again!");
        return coffee;
    }
}

Modernisierung einer einfachen Fabrik

Unser Café läuft sehr gut. So sehr, dass wir über eine Erweiterung nachdenken. Wir wollen einige neue Standorte eröffnen. Wir sind mutig und unternehmungslustig, damit wir keine langweiligen Coffeeshops eröffnen. Wir möchten, dass jeder Shop eine besondere Note hat. Dementsprechend werden wir zunächst zwei Standorte eröffnen: einen italienischen und einen amerikanischen. Diese Änderungen wirken sich nicht nur auf die Inneneinrichtung aus, sondern auch auf das Getränkeangebot:
  • Im italienischen Coffeeshop verwenden wir ausschließlich italienische Kaffeemarken mit spezieller Mahlung und Röstung.
  • Am amerikanischen Standort wird es größere Portionen geben und wir servieren zu jeder Bestellung Marshmallows.
Unverändert bleibt lediglich unser Geschäftsmodell, das sich als hervorragend erwiesen hat. In Bezug auf den Code passiert Folgendes. Wir hatten 4 Klassen, die unseren Produkten entsprachen:

public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Aber jetzt haben wir 8:

public class ItalianStyleAmericano extends Coffee {}
public class ItalianStyleCappucino extends Coffee {}
public class ItalianStyleCaffeLatte extends Coffee {}
public class ItalianStyleEspresso extends Coffee {}

public class AmericanStyleAmericano extends Coffee {}
public class AmericanStyleCappucino extends Coffee {}
public class AmericanStyleCaffeLatte extends Coffee {}
public class AmericanStyleEspresso extends Coffee {}
Da wir das aktuelle Geschäftsmodell beibehalten wollen, möchten wir, dass die orderCoffee(CoffeeType type)Methode möglichst wenig Änderungen erfährt. Schau es dir an:

public Coffee orderCoffee(CoffeeType type) {
    Coffee coffee = coffeeFactory.createCoffee(type);
    coffee.grindCoffee();
    coffee.makeCoffee();
    coffee.pourIntoCup();

    System.out.println("Here's your coffee! Thanks! Come again!");
    return coffee;
}
Welche Möglichkeiten haben wir? Nun ja, wir wissen doch schon, wie man eine Fabrik schreibt, oder? Das einfachste, was mir sofort in den Sinn kommt, ist, zwei ähnliche Fabriken zu schreiben und dann die gewünschte Implementierung an den Konstrukteur unseres Cafés zu übergeben. Dadurch ändert sich die Klasse des Coffeeshops nicht. Zuerst müssen wir eine neue Factory-Klasse erstellen, dafür sorgen, dass sie unsere einfache Factory erbt, und dann die createCoffee(CoffeeType type)Methode überschreiben. Schreiben wir Fabriken für die Herstellung von Kaffee nach italienischer Art und Kaffee nach amerikanischer Art:

public class SimpleItalianCoffeeFactory extends SimpleCoffeeFactory {

    @Override
    public Coffee createCoffee(CoffeeType type) {
        Coffee coffee = null;
        switch (type) {
            case AMERICANO:
                coffee = new ItalianStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new ItalianStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new ItalianStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new ItalianStyleCaffeLatte();
                break;
        }
        return coffee;
    }
}

public class SimpleAmericanCoffeeFactory extends SimpleCoffeeFactory{

    @Override
    public Coffee createCoffee (CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new AmericanStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new AmericanStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new AmericanStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new AmericanStyleCaffeLatte();
                break;
        }

        return coffee;
    }

}
Jetzt können wir die gewünschte Factory-Implementierung an CoffeeShop übergeben. Sehen wir uns an, wie der Code zum Bestellen von Kaffee in verschiedenen Coffeeshops aussehen würde. Zum Beispiel italienischer und amerikanischer Cappuccino:

public class Main {
    public static void main(String[] args) {
        /*
            Order an Italian-style cappuccino:
            1. Create a factory for making Italian coffee
            2. Create a new coffee shop, passing the Italian coffee factory to it through the constructor
            3. Order our coffee
         */
        SimpleItalianCoffeeFactory italianCoffeeFactory = new SimpleItalianCoffeeFactory();
        CoffeeShop italianCoffeeShop = new CoffeeShop(italianCoffeeFactory);
        italianCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);
        
        
         /*
            Order an American-style cappuccino
            1. Create a factory for making American coffee
            2. Create a new coffee shop, passing the American coffee factory to it through the constructor
            3. Order our coffee
         */
        SimpleAmericanCoffeeFactory americanCoffeeFactory = new SimpleAmericanCoffeeFactory();
        CoffeeShop americanCoffeeShop = new CoffeeShop(americanCoffeeFactory);
        americanCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);
    }
}
Wir haben zwei verschiedene Coffeeshops geschaffen und jedem die gewünschte Fabrik übergeben. Einerseits haben wir unser Ziel erreicht, aber andererseits... Irgendwie kommt das bei den Unternehmern nicht gut an... Lasst uns herausfinden, was falsch ist. Erstens die Fülle an Fabriken. Was? Nun sollen wir für jeden neuen Standort eine eigene Fabrik erstellen und darüber hinaus sicherstellen, dass bei der Einrichtung eines Cafés die entsprechende Fabrik an den Erbauer übergeben wird? Zweitens ist es immer noch eine einfache Fabrik. Nur leicht modernisiert. Aber wir sind hier, um ein neues Muster zu lernen. Drittens: Ist ein anderer Ansatz nicht möglich? Es wäre toll, wenn wir alle Fragen rund um die Kaffeezubereitung in die packen könntenCoffeeShopKlasse, indem die Prozesse der Kaffeezubereitung und der Auftragsabwicklung miteinander verknüpft werden und gleichzeitig ausreichend Flexibilität für die Zubereitung verschiedener Kaffeesorten erhalten bleibt. Die Antwort lautet: Ja, das können wir. Dies wird als Factory-Methodenentwurfsmuster bezeichnet.

Von einer einfachen Fabrik zu einer Fabrikmethode

Um die Aufgabe möglichst effizient zu lösen:
  1. Wir geben die createCoffee(CoffeeType type)Methode an die CoffeeShopKlasse zurück.
  2. Wir werden diese Methode abstrakt machen.
  3. Die CoffeeShopKlasse selbst wird abstrakt.
  4. Die CoffeeShopKlasse wird Kinderklassen haben.
Ja Freund. Das italienische Café ist nichts anderes als ein Nachkomme der Klasse, die die Methode gemäß den besten Traditionen italienischer Baristas CoffeeShopumsetzt . createCoffee(CoffeeType type)Nun, Schritt für Schritt. Schritt 1. Machen Sie die CoffeeKlasse abstrakt. Wir haben zwei ganze Familien unterschiedlicher Produkte. Dennoch haben der italienische und der amerikanische Kaffee einen gemeinsamen Vorfahren – die CoffeeKlasse. Es wäre angebracht, es abstrakt zu machen:

public abstract class Coffee {
    public void makeCoffee(){
        // Brew the coffee
    }
    public void pourIntoCup(){
        // Pour into a cup
    }
}
Schritt 2. Erstellen Sie CoffeeShopeine Zusammenfassung mit einer abstrakten createCoffee(CoffeeType type)Methode

public abstract class CoffeeShop {

    public Coffee orderCoffee(CoffeeType type) {
        Coffee coffee = createCoffee(type);

        coffee.makeCoffee();
        coffee.pourIntoCup();

        System.out.println("Here's your coffee! Thanks! Come again!");
        return coffee;
    }

    protected abstract Coffee createCoffee(CoffeeType type);
}
Schritt 3. Erstellen Sie ein italienisches Café, das ein Nachkomme des abstrakten Cafés ist. Wir implementieren die createCoffee(CoffeeType type)darin enthaltene Methode unter Berücksichtigung der Besonderheiten italienischer Rezepte.

public class ItalianCoffeeShop extends CoffeeShop {

    @Override
    public Coffee createCoffee (CoffeeType type) {
        Coffee coffee = null;
        switch (type) {
            case AMERICANO:
                coffee = new ItalianStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new ItalianStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new ItalianStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new ItalianStyleCaffeLatte();
                break;
        }
        return coffee;
    }
}
Schritt 4. Dasselbe machen wir für das Café im amerikanischen Stil

public class AmericanCoffeeShop extends CoffeeShop {
    @Override
    public Coffee createCoffee(CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new AmericanStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new AmericanStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new AmericanStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new AmericanStyleCaffeLatte();
                break;
        }

        return coffee;
    }
}
Schritt 5. Schauen Sie sich an, wie amerikanische und italienische Lattes aussehen:

public class Main {
    public static void main(String[] args) {
        CoffeeShop italianCoffeeShop = new ItalianCoffeeShop();
        italianCoffeeShop.orderCoffee(CoffeeType.CAFFE_LATTE);

        CoffeeShop americanCoffeeShop = new AmericanCoffeeShop();
        americanCoffeeShop.orderCoffee(CoffeeType.CAFFE_LATTE);
    }
}
Glückwunsch. Wir haben gerade das Designmuster der Fabrikmethode am Beispiel unseres Cafés implementiert.

Das Prinzip hinter Fabrikmethoden

Schauen wir uns nun genauer an, was wir haben. Das folgende Diagramm zeigt die resultierenden Klassen. Die grünen Blöcke sind Erstellerklassen und die blauen Blöcke sind Produktklassen. Entwurfsmuster: Factory-Methode – 2Welche Schlussfolgerungen können wir ziehen?
  1. Alle Produkte sind Implementierungen der abstrakten CoffeeKlasse.
  2. Alle Ersteller sind Implementierungen der abstrakten CoffeeShopKlasse.
  3. Wir sehen zwei parallele Klassenhierarchien:
    • Hierarchie der Produkte. Wir sehen italienische Nachkommen und amerikanische Nachkommen
    • Hierarchie der Schöpfer. Wir sehen italienische Nachkommen und amerikanische Nachkommen
  4. Die CoffeeShopOberklasse hat keine Informationen darüber, welches spezifische Produkt ( Coffee) erstellt wird.
  5. Die CoffeeShopOberklasse delegiert die Erstellung eines bestimmten Produkts an seine Nachkommen.
  6. Jeder Nachkomme der CoffeeShopKlasse implementiert eine createCoffee()Factory-Methode gemäß seinen eigenen spezifischen Merkmalen. Mit anderen Worten: Die Implementierungen der Produzentenklassen bereiten spezifische Produkte basierend auf den Besonderheiten der Produzentenklasse vor.
Sie sind nun bereit für die Definition des Factory- Methodenmusters. Das Factory-Methodenmuster definiert eine Schnittstelle zum Erstellen eines Objekts, ermöglicht jedoch Unterklassen, die Klasse des erstellten Objekts auszuwählen. Somit delegiert eine Factory-Methode die Erstellung einer Instanz an Unterklassen. Im Allgemeinen ist es nicht so wichtig, sich an die Definition zu erinnern, sondern zu verstehen, wie alles funktioniert.

Struktur einer Factory-Methode

Entwurfsmuster: Factory-Methode – 3Das obige Diagramm zeigt die allgemeine Struktur des Factory-Methodenmusters. Was ist hier noch wichtig?
  1. Die Creator-Klasse implementiert alle Methoden, die mit Produkten interagieren, mit Ausnahme der Factory-Methode.
  2. Die abstrakte factoryMethod()Methode muss von allen Nachkommen der CreatorKlasse implementiert werden.
  3. Die ConcreteCreatorKlasse implementiert die factoryMethod()Methode, die direkt das Produkt erstellt.
  4. Diese Klasse ist für die Erstellung spezifischer Produkte verantwortlich. Dies ist der einzige Kurs mit Informationen zum Erstellen dieser Produkte.
  5. Alle Produkte müssen eine gemeinsame Schnittstelle implementieren, dh sie müssen Nachkommen einer gemeinsamen Produktklasse sein. Dies ist notwendig, damit Klassen, die Produkte verwenden, diese als Abstraktionen und nicht als spezifische Implementierungen verarbeiten können.

Hausaufgaben

Heute haben wir ziemlich viel Arbeit geleistet und das Entwurfsmuster der Fabrikmethode studiert. Es ist Zeit, das Material zu verstärken! Übung 1. Erledigen Sie die Arbeit, um ein weiteres Café zu eröffnen. Es könnte ein Café im englischen oder spanischen Stil sein. Oder sogar im Raumschiff-Stil. Fügen Sie dem Kaffee Lebensmittelfarbe hinzu, um ihn zum Leuchten zu bringen, und Ihr Kaffee wird einfach nicht von dieser Welt sein! Übung 2. In der letzten Lektion hatten Sie eine Übung, bei der Sie eine virtuelle Sushi-Bar oder eine virtuelle Pizzeria erstellt haben. Jetzt besteht Ihre Übung darin, nicht still zu stehen. Heute haben Sie gelernt, wie Sie das Factory-Methodenmuster zu Ihrem Vorteil nutzen können. Es ist Zeit, dieses Wissen zu nutzen und Ihr eigenes Geschäft zu erweitern ;)
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION