CodeGym/Java blogg/Slumpmässig/Designmönster: Fabriksmetod
John Squirrels
Nivå
San Francisco

Designmönster: Fabriksmetod

Publicerad i gruppen
Hej! Idag kommer vi att fortsätta att studera designmönster och vi kommer att diskutera fabriksmetodens mönster. Designmönster: Fabriksmetod - 1 Du får reda på vad det är och vilka uppgifter det här mönstret passar för. Vi kommer att överväga detta designmönster i praktiken och studera dess struktur. För att säkerställa att allt är klart måste du förstå följande ämnen:
  1. Arv i Java.
  2. Abstrakta metoder och klasser i Java

Vilket problem löser fabriksmetoden?

Alla fabriksdesignmönster har två typer av deltagare: skapare (fabrikerna själva) och produkter (de föremål som skapas av fabrikerna). Föreställ dig följande situation: vi har en fabrik som tillverkar bilar av märket CodeGym. Den vet hur man skapar modeller av bilar med olika typer av karosser:
  • sedaner
  • kombi
  • kupéer
Vår verksamhet blomstrade så mycket att vi en vacker dag förvärvade en annan biltillverkare – OneAuto. Eftersom vi är förnuftiga företagare vill vi inte förlora några OneAuto-kunder, och därför står vi inför uppgiften att omstrukturera produktionen så att vi kan producera:
  • CodeGym sedaner
  • CodeGym kombi
  • CodeGym kupéer
  • OneAuto sedans
  • OneAuto kombi
  • OneAuto kupéer
Som du kan se, istället för en produktgrupp, har vi nu två, och de skiljer sig åt i vissa detaljer. Fabriksmetodens designmönster är till för när vi behöver skapa olika grupper av produkter, som var och en har några specifika egenskaper . Vi kommer att överväga detta mönsters vägledande princip i praktiken, och gradvis gå från det enkla till det komplexa, med hjälp av exemplet från vårt kafé, som vi skapade i en av de tidigare lektionerna .

Lite om fabriksmönstret

Låt mig påminna er om att vi tidigare byggde ett litet virtuellt kafé. Med hjälp av en enkel fabrik lärde vi oss att skapa olika typer av kaffe. Idag ska vi omarbeta detta exempel. Låt oss komma ihåg hur vårt kafé såg ut, med sin enkla fabrik. Vi hade en kaffeklass:
public class Coffee {
    public void grindCoffee(){
        // Grind the coffee
    }
    public void makeCoffee(){
        // Brew the coffee
    }
    public void pourIntoCup(){
        // Pour into a cup
    }
}
Och flera barnklasser som motsvarar specifika typer av kaffe som vår fabrik kunde producera:
public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Vi skapade en uppräkning för att göra det enkelt att lägga beställningar:
public enum CoffeeType {
    ESPRESSO,
    AMERICANO,
    CAFFE_LATTE,
    CAPPUCCINO
}
Själva kaffefabriken såg ut så här:
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;
    }
}
Och slutligen såg själva kaféet ut så här:
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;
    }
}

Modernisera en enkel fabrik

Vårt kafé går väldigt bra. Så pass att vi överväger att expandera. Vi vill öppna några nya platser. Vi är djärva och företagsamma, så vi kommer inte tycka om tråkiga kaféer. Vi vill att varje butik ska ha en speciell twist. Till att börja med kommer vi därför att öppna två platser: en italiensk och en amerikansk. Dessa förändringar kommer att påverka inte bara inredningen utan även de drycker som erbjuds:
  • i det italienska kaféet kommer vi att använda uteslutande italienska kaffemärken, med speciell malning och rostning.
  • den amerikanska platsen kommer att ha större portioner, och vi kommer att servera marshmallows med varje beställning.
Det enda som är oförändrat är vår affärsmodell, som har visat sig vara utmärkt. När det gäller koden är detta vad som händer. Vi hade 4 klasser motsvarande våra produkter:
public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Men nu kommer vi att ha 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 {}
Eftersom vi vill behålla nuvarande affärsmodell vill vi att orderCoffee(CoffeeType type)metoden ska genomgå så få förändringar som möjligt. Ta en titt på det:
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;
}
Vilka alternativ har vi? Tja, vi vet redan hur man skriver en fabrik, eller hur? Det enklaste som omedelbart kommer att tänka på är att skriva två liknande fabriker och sedan skicka den önskade implementeringen till vårt kafés konstruktör. Genom att göra detta kommer kaféets klass inte att förändras. Först måste vi skapa en ny fabriksklass, få den att ärva vår enkla fabrik och sedan åsidosätta metoden createCoffee(CoffeeType type). Låt oss skriva fabriker för att skapa kaffe i italiensk stil och kaffe i amerikansk stil:
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 kan vi skicka den önskade fabriksimplementeringen till CoffeeShop. Låt oss se hur koden för att beställa kaffe från olika kaféer skulle se ut. Till exempel cappuccino i italiensk och amerikansk stil:
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);
    }
}
Vi skapade två olika kaféer och skickade den önskade fabriken till var och en. Å ena sidan har vi uppnått vårt mål, men å andra sidan... På något sätt faller det här inte bra för företagarna... Låt oss ta reda på vad som är fel. För det första överflödet av fabriker. Vad? Nu för varje ny plats, är det meningen att vi ska skapa en egen fabrik och, utöver det, se till att den relevanta fabriken skickas till konstruktören när vi skapar ett kafé? För det andra är det fortfarande en enkel fabrik. Bara moderniserat något. Men vi är här för att lära oss ett nytt mönster. För det tredje, är inte ett annat tillvägagångssätt möjligt? Det skulle vara bra om vi kunde lägga alla frågor som rör kaffeberedning iCoffeeShopklass genom att koppla samman processerna för att skapa kaffe och servicebeställningar, samtidigt som man bibehåller tillräcklig flexibilitet för att göra olika kaffestilar. Svaret är ja, det kan vi. Detta kallas fabriksmetodens designmönster.

Från en enkel fabrik till en fabriksmetod

För att lösa uppgiften så effektivt som möjligt:
  1. Vi returnerar createCoffee(CoffeeType type)metoden till CoffeeShopklassen.
  2. Vi kommer att göra denna metod abstrakt.
  3. Själva klassen CoffeeShopkommer att bli abstrakt.
  4. Klassen CoffeeShopkommer att ha barnklasser.
Ja kompis. Det italienska kaféet är inget annat än en ättling till klassen, CoffeeShopsom implementerar createCoffee(CoffeeType type)metoden i enlighet med italienska baristas bästa traditioner. Nu, ett steg i taget. Steg 1. Gör Coffeeklassen abstrakt. Vi har två hela familjer av olika produkter. Ändå har det italienska och amerikanska kaffet en gemensam förfader - klassen Coffee. Det vore lämpligt att göra det abstrakt:
public abstract class Coffee {
    public void makeCoffee(){
        // Brew the coffee
    }
    public void pourIntoCup(){
        // Pour into a cup
    }
}
Steg 2. Gör CoffeeShopabstrakt, med en abstrakt createCoffee(CoffeeType type)metod
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);
}
Steg 3. Skapa ett italienskt kafé, som är en ättling till det abstrakta kaféet. Vi implementerar createCoffee(CoffeeType type)metoden i den, med hänsyn till detaljerna i italienska recept.
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;
    }
}
Steg 4. Vi gör samma sak för kaféet i amerikansk 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;
    }
}
Steg 5. Kolla in hur amerikanska och italienska lattes kommer att se ut:
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);
    }
}
Grattis. Vi har precis implementerat designmönstret för fabriksmetoden med vårt kafé som exempel.

Principen bakom fabriksmetoder

Låt oss nu överväga mer i detalj vad vi fick. Diagrammet nedan visar de resulterande klasserna. De gröna blocken är skaparklasser och de blå blocken är produktklasser. Designmönster: Fabriksmetod - 2Vilka slutsatser kan vi dra?
  1. Alla produkter är implementeringar av den abstrakta Coffeeklassen.
  2. Alla skapare är implementeringar av den abstrakta CoffeeShopklassen.
  3. Vi ser två parallella klasshierarkier:
    • Hierarki av produkter. Vi ser italienska ättlingar och amerikanska ättlingar
    • Hierarki av skapare. Vi ser italienska ättlingar och amerikanska ättlingar
  4. Superklassen CoffeeShophar ingen information om vilken specifik produkt ( Coffee) som kommer att skapas.
  5. Superklassen CoffeeShopdelegerar skapandet av en specifik produkt till sina ättlingar.
  6. Varje avkomling av CoffeeShopklassen implementerar en createCoffee()fabriksmetod i enlighet med sina egna specifika egenskaper. Med andra ord, implementeringarna av producentklasserna förbereder specifika produkter baserat på specifikationerna för producentklassen.
Du är nu redo för definitionen av fabriksmetodens mönster . Fabriksmetodmönstret definierar ett gränssnitt för att skapa ett objekt, men tillåter underklasser att välja klass för det skapade objektet . Således delegerar en fabriksmetod skapandet av en instans till underklasser. I allmänhet är det inte lika viktigt att komma ihåg definitionen som att förstå hur det hela fungerar.

Struktur av en fabriksmetod

Designmönster: Fabriksmetod - 3Diagrammet ovan visar den allmänna strukturen för fabriksmetodens mönster. Vad är mer viktigt här?
  1. Klassen Creator implementerar alla metoder som interagerar med produkter, förutom fabriksmetoden.
  2. Den abstrakta factoryMethod()metoden måste implementeras av alla ättlingar till klassen Creator.
  3. Klassen ConcreteCreatorimplementerar factoryMethod()metoden, som direkt skapar produkten.
  4. Denna klass ansvarar för att skapa specifika produkter. Detta är den enda klassen med information om att skapa dessa produkter.
  5. Alla produkter måste implementera ett gemensamt gränssnitt, dvs de måste vara ättlingar till en gemensam produktklass. Detta är nödvändigt så att klasser som använder produkter kan fungera på dem som abstraktioner, snarare än specifika implementeringar.

Läxa

Idag har vi gjort ganska mycket arbete och studerat fabriksmetodens designmönster. Det är dags att förstärka materialet! Övning 1. Gör jobbet för att öppna ytterligare ett kafé. Det kan vara ett kafé i engelsk stil eller spansk stil. Eller till och med rymdskeppsstil. Lägg till matfärg till kaffet för att få det att glöda, och ditt kaffe kommer helt enkelt att vara av den här världen! Övning 2. På förra lektionen hade du en övning där du skapade en virtuell sushibar eller en virtuell pizzeria. Nu är din övning att inte stå still. Idag har du lärt dig hur du använder fabriksmetodens mönster till din fördel. Det är dags att använda denna kunskap och utöka din egen verksamhet ;)
Kommentarer
  • Populär
  • Ny
  • Gammal
Du måste vara inloggad för att lämna en kommentar
Den här sidan har inga kommentarer än