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

Ontwerppatronen: fabrieksmethode

Gepubliceerd in de groep Willekeurig
Hoi! Vandaag gaan we door met het bestuderen van ontwerppatronen en bespreken we het patroon van de fabrieksmethode. Ontwerppatronen: Fabrieksmethode - 1 U zult ontdekken wat het is en voor welke taken dit patroon geschikt is. We zullen dit ontwerppatroon in de praktijk bekijken en de structuur ervan bestuderen. Om ervoor te zorgen dat alles duidelijk is, moet u de volgende onderwerpen begrijpen:
  1. Overerving op Java.
  2. Abstracte methoden en klassen in Java

Welk probleem lost de fabrieksmethode op?

Alle fabrieksontwerppatronen hebben twee soorten deelnemers: makers (de fabrieken zelf) en producten (de objecten die door de fabrieken zijn gemaakt). Stel je de volgende situatie voor: we hebben een fabriek die auto's met het merk CodeGym produceert. Het weet hoe het modellen van auto's moet maken met verschillende soorten carrosserieën:
  • sedans
  • stationwagons
  • coupes
Ons bedrijf bloeide zo goed dat we op een mooie dag een andere autofabrikant overnamen: OneAuto. Als verstandige ondernemers willen we geen OneAuto-klanten verliezen en daarom staan ​​we voor de taak om de productie te herstructureren zodat we het volgende kunnen produceren:
  • CodeGym sedans
  • CodeGym stationwagons
  • CodeGym coupes
  • OneAuto sedans
  • OneAuto stationwagons
  • OneAuto coupés
Zoals u kunt zien, hebben we er nu twee in plaats van één groep producten, en deze verschillen op bepaalde details. Het ontwerppatroon van de fabrieksmethode is voor wanneer we verschillende groepen producten moeten maken, die elk een aantal specifieke eigenschappen hebben. We zullen het leidende principe van dit patroon in de praktijk bekijken, waarbij we geleidelijk van eenvoudig naar complex gaan, met behulp van het voorbeeld van onze coffeeshop, die we in een van de vorige lessen hebben gemaakt .

Een beetje over het fabriekspatroon

Laat me je eraan herinneren dat we eerder een kleine virtuele coffeeshop hebben gebouwd. Met behulp van een eenvoudige fabriek leerden we verschillende soorten koffie te maken. Vandaag gaan we dit voorbeeld herwerken. Laten we ons herinneren hoe onze coffeeshop eruit zag, met zijn eenvoudige fabriek. We hadden een koffieles:

public class Coffee {
    public void grindCoffee(){
        // Grind the coffee
    }
    public void makeCoffee(){
        // Brew the coffee
    }
    public void pourIntoCup(){
        // Pour into a cup
    }
}
En verschillende kinderklassen die overeenkomen met specifieke soorten koffie die onze fabriek zou kunnen produceren:

public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
We hebben een opsomming gemaakt om het plaatsen van bestellingen gemakkelijk te maken:

public enum CoffeeType {
    ESPRESSO,
    AMERICANO,
    CAFFE_LATTE,
    CAPPUCCINO
}
De koffiefabriek zelf zag er zo uit:

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;
    }
}
En tot slot zag de coffeeshop zelf er zo uit:

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;
    }
}

Een simpele fabriek moderniseren

Onze coffeeshop draait erg goed. Zo erg zelfs dat we overwegen uit te breiden. We willen enkele nieuwe locaties openen. We zijn gedurfd en ondernemend, dus we gaan geen saaie coffeeshops uit de grond stampen. We willen dat elke winkel een speciale twist heeft. Daarom openen we om te beginnen twee locaties: een Italiaanse en een Amerikaanse. Deze wijzigingen hebben niet alleen invloed op het interieurontwerp, maar ook op de aangeboden drankjes:
  • in de Italiaanse coffeeshop gebruiken we uitsluitend Italiaanse koffiemerken, met speciale maling en branding.
  • de Amerikaanse locatie heeft grotere porties en we serveren marshmallows bij elke bestelling.
Het enige dat onveranderd blijft, is ons businessmodel, dat zich uitstekend heeft bewezen. In termen van de code is dit wat er gebeurt. We hadden 4 klassen die overeenkomen met onze producten:

public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Maar nu hebben we er 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 {}
Omdat we het huidige businessmodel willen behouden, willen we dat de orderCoffee(CoffeeType type)werkwijze zo min mogelijk verandert. Bekijk het eens:

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;
}
Welke opties hebben we? Nou, we weten al hoe we een fabriek moeten schrijven, toch? Het eenvoudigste dat meteen in je opkomt, is om twee vergelijkbare fabrieken te schrijven en vervolgens de gewenste implementatie door te geven aan de constructeur van onze coffeeshop. Hierdoor verandert de klasse van de coffeeshop niet. Eerst moeten we een nieuwe fabrieksklasse maken, deze onze eenvoudige fabriek laten erven en vervolgens de createCoffee(CoffeeType type)methode overschrijven. Laten we fabrieken schrijven voor het maken van koffie in Italiaanse stijl en koffie in Amerikaanse stijl:

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;
    }

}
Nu kunnen we de gewenste fabrieksimplementatie doorgeven aan CoffeeShop. Laten we eens kijken hoe de code voor het bestellen van koffie bij verschillende coffeeshops eruit zou zien. Bijvoorbeeld cappuccino in Italiaanse en Amerikaanse stijl:

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);
    }
}
We creëerden twee verschillende coffeeshops en gaven aan elk de gewenste fabriek door. Aan de ene kant hebben we ons doel bereikt, maar aan de andere kant... Op de een of andere manier zit dit niet goed bij de ondernemers... Laten we uitzoeken wat er mis is. Ten eerste de overvloed aan fabrieken. Wat? Nu moeten we voor elke nieuwe locatie een eigen fabriek maken en er daarnaast voor zorgen dat de betreffende fabriek wordt doorgegeven aan de constructeur bij het maken van een coffeeshop? Ten tweede is het nog steeds een eenvoudige fabriek. Net iets gemoderniseerd. Maar we zijn hier om een ​​nieuw patroon te leren. Ten derde, is een andere benadering niet mogelijk? Het zou mooi zijn als we alle zaken rond de koffiebereiding in deCoffeeShopklasse door de processen van koffie maken en bedienen van bestellingen te koppelen en tegelijkertijd voldoende flexibiliteit te behouden om verschillende soorten koffie te zetten. Het antwoord is ja, dat kunnen we. Dit wordt het ontwerppatroon van de fabrieksmethode genoemd.

Van een simpele fabriek naar een fabrieksmethode

Om de taak zo efficiënt mogelijk op te lossen:
  1. We geven de createCoffee(CoffeeType type)methode terug aan de CoffeeShopklas.
  2. We zullen deze methode abstract maken.
  3. De CoffeeShopklas zelf wordt abstract.
  4. De CoffeeShopklas krijgt kinderlessen.
Ja vriend. De Italiaanse coffeeshop is niets meer dan een afstammeling van de klasse, die de methode volgens de beste tradities van Italiaanse barista's CoffeeShopuitvoert . createCoffee(CoffeeType type)Nu stap voor stap. Stap 1. Maak de Coffeeklas abstract. We hebben twee hele families met verschillende producten. Toch hebben de Italiaanse en Amerikaanse koffiesoorten een gemeenschappelijke voorouder: de Coffeeklasse. Het zou juist zijn om het abstract te maken:

public abstract class Coffee {
    public void makeCoffee(){
        // Brew the coffee
    }
    public void pourIntoCup(){
        // Pour into a cup
    }
}
Stap 2. Maak CoffeeShopabstract, met een abstracte 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);
}
Stap 3. Creëer een Italiaanse coffeeshop, die een afstammeling is van de abstracte coffeeshop. We implementeren de createCoffee(CoffeeType type)methode erin, rekening houdend met de specifieke kenmerken van Italiaanse recepten.

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;
    }
}
Stap 4. Hetzelfde doen we voor de Amerikaanse coffeeshop

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;
    }
}
Stap 5. Bekijk hoe Amerikaanse en Italiaanse lattes eruit zullen zien:

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);
    }
}
Gefeliciteerd. We hebben zojuist het ontwerppatroon van de fabrieksmethode geïmplementeerd met onze coffeeshop als voorbeeld.

Het principe achter fabrieksmethodes

Laten we nu in meer detail bekijken wat we hebben. Het onderstaande diagram toont de resulterende klassen. De groene blokken zijn makerklassen en de blauwe blokken zijn productklassen. Ontwerppatronen: fabrieksmethode - 2Welke conclusies kunnen we trekken?
  1. Alle producten zijn implementaties van de abstracte Coffeeklasse.
  2. Alle makers zijn implementaties van de abstracte CoffeeShopklasse.
  3. We zien twee parallelle klassenhiërarchieën:
    • Hiërarchie van producten. We zien Italiaanse nakomelingen en Amerikaanse nakomelingen
    • Hiërarchie van makers. We zien Italiaanse nakomelingen en Amerikaanse nakomelingen
  4. De CoffeeShopsuperklasse heeft geen informatie over welk specifiek product ( Coffee) zal worden gemaakt.
  5. De CoffeeShopsuperklasse delegeert de creatie van een specifiek product aan zijn nakomelingen.
  6. Elke afstammeling van de CoffeeShopklasse implementeert een createCoffee()fabrieksmethode in overeenstemming met zijn eigen specifieke kenmerken. Met andere woorden, de implementaties van de producentenklassen bereiden specifieke producten voor op basis van de specifieke kenmerken van de producentenklasse.
U bent nu klaar voor de definitie van het fabrieksmethodepatroon . Het fabrieksmethodepatroon definieert een interface voor het maken van een object, maar stelt subklassen in staat om de klasse van het gemaakte object te selecteren. Een fabrieksmethode delegeert dus het maken van een instantie naar subklassen. Over het algemeen is het onthouden van de definitie niet zo belangrijk als begrijpen hoe het allemaal werkt.

Structuur van een fabrieksmethode

Ontwerppatronen: fabrieksmethode - 3Het bovenstaande diagram toont de algemene structuur van het patroon van de fabrieksmethode. Wat is hier verder belangrijk?
  1. De klasse Creator implementeert alle methoden die interactie hebben met producten, behalve de fabrieksmethode.
  2. De abstracte factoryMethod()methode moet worden geïmplementeerd door alle afstammelingen van de Creatorklasse.
  3. De ConcreteCreatorklasse implementeert de factoryMethod()methode, die direct het product creëert.
  4. Deze klasse is verantwoordelijk voor het maken van specifieke producten. Dit is de enige les met informatie over het maken van deze producten.
  5. Alle producten moeten een gemeenschappelijke interface implementeren, dwz ze moeten afstammelingen zijn van een gemeenschappelijke productklasse. Dit is nodig zodat klassen die producten gebruiken erop kunnen werken als abstracties in plaats van als specifieke implementaties.

Huiswerk

Vandaag hebben we behoorlijk wat werk verzet en het ontwerppatroon van de fabrieksmethode bestudeerd. Het is tijd om het materiaal te versterken! Oefening 1. Doe het werk om nog een coffeeshop te openen. Het kan een coffeeshop in Engelse of Spaanse stijl zijn. Of zelfs in ruimteschipstijl. Voeg kleurstof toe aan de koffie om hem te laten gloeien, en je koffie zal gewoon niet van deze wereld zijn! Oefening 2. In de vorige les had je een oefening waarbij je een virtuele sushibar of een virtuele pizzeria maakte. Nu is uw oefening om niet stil te staan. Vandaag heb je geleerd hoe je het patroon van de fabrieksmethode in je voordeel kunt gebruiken. Het is tijd om deze kennis te gebruiken en je eigen bedrijf uit te breiden ;)
Opmerkingen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION