Hej! Idag kommer vi att fortsätta att studera designmönster och vi kommer att diskutera fabriksmetodens mönster.
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:
Vilka slutsatser kan vi dra?
Diagrammet ovan visar den allmänna strukturen för fabriksmetodens mönster. Vad är mer viktigt här?

- Arv i Java.
- 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
- CodeGym sedaner
- CodeGym kombi
- CodeGym kupéer
- OneAuto sedans
- OneAuto kombi
- OneAuto kupéer
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.
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 iCoffeeShop
klass 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:- Vi returnerar
createCoffee(CoffeeType type)
metoden tillCoffeeShop
klassen. - Vi kommer att göra denna metod abstrakt.
- Själva klassen
CoffeeShop
kommer att bli abstrakt. - Klassen
CoffeeShop
kommer att ha barnklasser.
CoffeeShop
som implementerar createCoffee(CoffeeType type)
metoden i enlighet med italienska baristas bästa traditioner. Nu, ett steg i taget. Steg 1. Gör Coffee
klassen 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 CoffeeShop
abstrakt, 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.
- Alla produkter är implementeringar av den abstrakta
Coffee
klassen. - Alla skapare är implementeringar av den abstrakta
CoffeeShop
klassen. - 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
- Superklassen
CoffeeShop
har ingen information om vilken specifik produkt (Coffee
) som kommer att skapas. - Superklassen
CoffeeShop
delegerar skapandet av en specifik produkt till sina ättlingar. - Varje avkomling av
CoffeeShop
klassen implementerar encreateCoffee()
fabriksmetod i enlighet med sina egna specifika egenskaper. Med andra ord, implementeringarna av producentklasserna förbereder specifika produkter baserat på specifikationerna för producentklassen.
Struktur av en fabriksmetod

- Klassen Creator implementerar alla metoder som interagerar med produkter, förutom fabriksmetoden.
- Den abstrakta
factoryMethod()
metoden måste implementeras av alla ättlingar till klassenCreator
. - Klassen
ConcreteCreator
implementerarfactoryMethod()
metoden, som direkt skapar produkten. - Denna klass ansvarar för att skapa specifika produkter. Detta är den enda klassen med information om att skapa dessa produkter.
- 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.
GO TO FULL VERSION