Hei! I dag vil vi fortsette å studere designmønstre, og vi vil diskutere fabrikkmetodemønsteret.
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:
Hvilke konklusjoner kan vi trekke?
Diagrammet ovenfor viser den generelle strukturen til fabrikkmetodemønsteret. Hva annet er viktig her?

- Arv i Java.
- 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
- CodeGym sedaner
- CodeGym stasjonsvogner
- CodeGym kupéer
- OneAuto sedaner
- OneAuto stasjonsvogner
- OneAuto kupéer
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.
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 iCoffeeShop
klasse 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:- Vi returnerer
createCoffee(CoffeeType type)
metoden tilCoffeeShop
klassen. - Vi vil gjøre denne metoden abstrakt.
- Selve klassen
CoffeeShop
vil bli abstrakt. - Klassen
CoffeeShop
vil ha barneklasser.
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 Coffee
klassen 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 CoffeeShop
abstrakt, 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.
- Alle produktene er implementeringer av abstraktklassen
Coffee
. - Alle skaperne er implementeringer av den abstrakte
CoffeeShop
klassen. - 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
- Superklassen
CoffeeShop
har ingen informasjon om hvilket spesifikt produkt (Coffee
) som skal opprettes. - Superklassen
CoffeeShop
delegerer opprettelsen av et spesifikt produkt til sine etterkommere. - Hver etterkommer av
CoffeeShop
klassen implementerer encreateCoffee()
fabrikkmetode i samsvar med sine egne spesifikke funksjoner. Med andre ord, implementeringene av produsentklassene forbereder spesifikke produkter basert på spesifikasjonene til produsentklassen.
Struktur av en fabrikkmetode

- Creator-klassen implementerer alle metoder som samhandler med produkter, bortsett fra fabrikkmetoden.
- Den abstrakte
factoryMethod()
metoden må implementeres av alle etterkommere avCreator
klassen. - Klassen
ConcreteCreator
implementererfactoryMethod()
metoden, som direkte skaper produktet. - Denne klassen er ansvarlig for å lage spesifikke produkter. Dette er den eneste klassen med informasjon om å lage disse produktene.
- 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.
GO TO FULL VERSION