CodeGym /Java-blogg /Tilfeldig /Designmønstre: Fabrikkmetode
John Squirrels
Nivå
San Francisco

Designmønstre: Fabrikkmetode

Publisert i gruppen
Hei! I dag vil vi fortsette å studere designmønstre, og vi vil diskutere fabrikkmetodemønsteret. Designmønstre: Fabrikkmetode - 1 Du vil finne ut hva det er og hvilke oppgaver dette mønsteret passer for. Vi vil vurdere dette designmønsteret i praksis og studere strukturen. For å sikre at alt er klart, må du forstå følgende emner:
  1. Arv i Java.
  2. Abstrakte metoder og klasser i Java

Hvilket problem løser fabrikkmetoden?

Alle fabrikkdesignmønstre har to typer deltakere: skapere (fabrikkene selv) og produkter (gjenstandene skapt av fabrikkene). Tenk deg følgende situasjon: vi har en fabrikk som produserer biler av CodeGym-merket. Den vet hvordan man lager modeller av biler med forskjellige typer karosserier:
  • sedaner
  • stasjonsvogner
  • kupeer
Virksomheten vår blomstret så mye at vi en vakker dag kjøpte en annen bilprodusent – ​​OneAuto. Som fornuftige bedriftseiere ønsker vi ikke å miste noen OneAuto-kunder, og derfor står vi overfor oppgaven med å omstrukturere produksjonen slik at vi kan produsere:
  • CodeGym sedaner
  • CodeGym stasjonsvogner
  • CodeGym kupéer
  • OneAuto sedaner
  • OneAuto stasjonsvogner
  • OneAuto kupéer
Som du kan se, i stedet for én gruppe produkter, har vi nå to, og de er forskjellige i visse detaljer. Fabrikkmetodens designmønster er for når vi trenger å lage forskjellige grupper av produkter, som hver har noen spesifikke egenskaper . Vi vil vurdere dette mønsterets ledende prinsipp i praksis, og gradvis gå fra det enkle til det komplekse, ved å bruke eksemplet på kaffebaren vår, som vi laget i en av de forrige leksjonene .

Litt om fabrikkmønsteret

La meg minne deg på at vi tidligere bygde en liten virtuell kaffebar. Ved hjelp av en enkel fabrikk lærte vi å lage ulike typer kaffe. I dag skal vi omarbeide dette eksemplet. La oss huske hvordan kaffebaren vår så ut, med sin enkle fabrikk. Vi hadde en kaffetime:

public class Coffee {
    public void grindCoffee(){
        // Grind the coffee
    }
    public void makeCoffee(){
        // Brew the coffee
    }
    public void pourIntoCup(){
        // Pour into a cup
    }
}
Og flere barneklasser som tilsvarer bestemte typer kaffe som fabrikken vår kunne produsere:

public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Vi har laget en oppsummering for å gjøre det enkelt å legge inn bestillinger:

public enum CoffeeType {
    ESPRESSO,
    AMERICANO,
    CAFFE_LATTE,
    CAPPUCCINO
}
Selve kaffefabrikken så slik ut:

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;
    }
}
Og til slutt så selve kaffebaren slik ut:

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

Modernisering av en enkel fabrikk

Kaffebaren vår går veldig bra. Så mye at vi vurderer å utvide. Vi ønsker å åpne noen nye lokasjoner. Vi er dristige og driftige, så vi vil ikke skru ut kjedelige kaffebarer. Vi ønsker at hver butikk skal ha en spesiell vri. Til å begynne med åpner vi derfor to lokasjoner: en italiensk og en amerikansk. Disse endringene vil påvirke ikke bare interiørdesignet, men også drikkene som tilbys:
  • i den italienske kaffebaren vil vi bruke utelukkende italienske kaffemerker, med spesialmaling og brenning.
  • det amerikanske stedet vil ha større porsjoner, og vi serverer marshmallows med hver bestilling.
Det eneste som forblir uendret er vår forretningsmodell, som har vist seg å være utmerket. Når det gjelder koden, er det dette som skjer. Vi hadde 4 klasser som tilsvarer produktene våre:

public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Men nå vil vi 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 {}
Siden vi ønsker å beholde dagens forretningsmodell, ønsker vi at orderCoffee(CoffeeType type)metoden skal gjennomgå så få endringer som mulig. 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;
}
Hvilke alternativer har vi? Vel, vi vet allerede hvordan man skriver en fabrikk, ikke sant? Det enkleste som umiddelbart kommer til tankene er å skrive to lignende fabrikker, og deretter sende ønsket implementering til kaffebarens konstruktør. Ved å gjøre dette vil ikke kaffebarens klasse endre seg. Først må vi opprette en ny fabrikkklasse, få den til å arve vår enkle fabrikk, og deretter overstyre createCoffee(CoffeeType type)metoden. La oss skrive fabrikker for å lage kaffe i italiensk stil og 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;
    }

}
Nå kan vi sende ønsket fabrikkimplementering til CoffeeShop. La oss se hvordan koden for å bestille kaffe fra forskjellige kaffebarer vil se ut. For eksempel cappuccino i italiensk og 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 opprettet to forskjellige kaffebarer, og ga den ønskede fabrikken videre til hver. På den ene siden har vi oppnådd målet vårt, men på den andre siden... På en eller annen side passer dette ikke bra med gründerne... La oss finne ut hva som er galt. For det første overfloden av fabrikker. Hva? Nå for hver ny plassering skal vi opprette sin egen fabrikk og i tillegg sørge for at den aktuelle fabrikken blir overført til konstruktøren når vi oppretter en kaffebar? For det andre er det fortsatt en enkel fabrikk. Bare modernisert litt. Men vi er her for å lære et nytt mønster. For det tredje, er ikke en annen tilnærming mulig? Det ville vært flott om vi kunne legge alle spørsmål knyttet til kaffetilberedning inn iCoffeeShopklasse ved å koble sammen prosessene for å lage kaffe og servicebestillinger, samtidig som man opprettholder tilstrekkelig fleksibilitet til å lage ulike kaffestiler. Svaret er ja, det kan vi. Dette kalles fabrikkmetodens designmønster.

Fra en enkel fabrikk til en fabrikkmetode

For å løse oppgaven så effektivt som mulig:
  1. Vi returnerer createCoffee(CoffeeType type)metoden til CoffeeShopklassen.
  2. Vi vil gjøre denne metoden abstrakt.
  3. Selve klassen CoffeeShopvil bli abstrakt.
  4. Klassen CoffeeShopvil ha barneklasser.
Ja venn. Den italienske kaffebaren er ikke annet enn en etterkommer av klassen CoffeeShop, som implementerer createCoffee(CoffeeType type)metoden i samsvar med de beste tradisjonene til italienske baristaer. Nå, ett skritt av gangen. Trinn 1. Gjør Coffeeklassen abstrakt. Vi har to hele familier med forskjellige produkter. Likevel har den italienske og amerikanske kaffen en felles stamfar - klassen Coffee. Det ville være riktig å gjøre det abstrakt:

public abstract class Coffee {
    public void makeCoffee(){
        // Brew the coffee
    }
    public void pourIntoCup(){
        // Pour into a cup
    }
}
Trinn 2. Lag CoffeeShopabstrakt, med en abstrakt createCoffee(CoffeeType type)metode

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);
}
Trinn 3. Lag en italiensk kaffebar, som er en etterkommer av den abstrakte kaffebaren. Vi implementerer createCoffee(CoffeeType type)metoden i den, tar hensyn til spesifikasjonene til italienske oppskrifter.

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;
    }
}
Trinn 4. Vi gjør det samme for kaffebaren 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;
    }
}
Trinn 5. Sjekk ut hvordan amerikanske og italienske lattes vil 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);
    }
}
Gratulerer. Vi implementerte nettopp designmønsteret for fabrikkmetoden ved å bruke kaffebaren vår som et eksempel.

Prinsippet bak fabrikkmetoder

La oss nå vurdere mer detaljert hva vi fikk. Diagrammet nedenfor viser de resulterende klassene. De grønne blokkene er skaperklasser, og de blå blokkene er produktklasser. Designmønstre: Fabrikkmetode - 2Hvilke konklusjoner kan vi trekke?
  1. Alle produktene er implementeringer av abstraktklassen Coffee.
  2. Alle skaperne er implementeringer av den abstrakte CoffeeShopklassen.
  3. Vi ser to parallelle klassehierarkier:
    • Hierarki av produkter. Vi ser italienske etterkommere og amerikanske etterkommere
    • Hierarki av skapere. Vi ser italienske etterkommere og amerikanske etterkommere
  4. Superklassen CoffeeShophar ingen informasjon om hvilket spesifikt produkt ( Coffee) som skal opprettes.
  5. Superklassen CoffeeShopdelegerer opprettelsen av et spesifikt produkt til sine etterkommere.
  6. Hver etterkommer av CoffeeShopklassen implementerer en createCoffee()fabrikkmetode i samsvar med sine egne spesifikke funksjoner. Med andre ord, implementeringene av produsentklassene forbereder spesifikke produkter basert på spesifikasjonene til produsentklassen.
Du er nå klar for definisjonen av fabrikkmetodemønsteret . Fabrikkmetodemønsteret definerer et grensesnitt for å lage et objekt, men lar underklasser velge klassen til det opprettede objektet. Dermed delegerer en fabrikkmetode opprettelse av en forekomst til underklasser. Generelt er det ikke like viktig å huske definisjonen som å forstå hvordan det hele fungerer.

Struktur av en fabrikkmetode

Designmønstre: Fabrikkmetode - 3Diagrammet ovenfor viser den generelle strukturen til fabrikkmetodemønsteret. Hva annet er viktig her?
  1. Creator-klassen implementerer alle metoder som samhandler med produkter, bortsett fra fabrikkmetoden.
  2. Den abstrakte factoryMethod()metoden må implementeres av alle etterkommere av Creatorklassen.
  3. Klassen ConcreteCreatorimplementerer factoryMethod()metoden, som direkte skaper produktet.
  4. Denne klassen er ansvarlig for å lage spesifikke produkter. Dette er den eneste klassen med informasjon om å lage disse produktene.
  5. Alle produkter må implementere et felles grensesnitt, dvs. de må være etterkommere av en felles produktklasse. Dette er nødvendig slik at klasser som bruker produkter kan operere på dem som abstraksjoner, snarere enn spesifikke implementeringer.

Hjemmelekser

I dag har vi gjort ganske mye arbeid og studert fabrikkmetodens designmønster. Det er på tide å forsterke materialet! Oppgave 1. Gjør jobben med å åpne enda en kaffebar. Det kan være en kaffebar i engelsk eller spansk stil. Eller til og med romskipstil. Legg konditorfarge til kaffen for å få den til å gløde, og kaffen din vil rett og slett være ute av denne verden! Oppgave 2. I siste leksjon hadde du en øvelse der du laget en virtuell sushibar eller en virtuell pizzeria. Nå er øvelsen din å ikke stå stille. I dag lærte du hvordan du bruker fabrikkmetodemønsteret til din fordel. Det er på tide å bruke denne kunnskapen og utvide din egen virksomhet ;)
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION