Hej! Idag kommer vi att fortsätta att studera designmönster och vi kommer att diskutera det abstrakta fabriksmönstret . Här är vad vi kommer att ta upp i lektionen:
- Vi kommer att diskutera vad en abstrakt fabrik är och vilket problem detta mönster löser
- Vi kommer att skapa skelettet av en plattformsoberoende applikation för att beställa kaffe genom ett användargränssnitt
- Vi kommer att studera instruktioner om hur man använder det här mönstret, inklusive att titta på ett diagram och en kod
- Och som en bonus innehåller den här lektionen ett dolt påskägg som hjälper dig att lära dig hur du använder Java för att bestämma namnet på operativsystemet och, beroende på resultatet, utföra en eller annan åtgärd.
- arv i Java
- abstrakta klasser och metoder i Java
Vilka problem löser en abstrakt fabrik?
En abstrakt fabrik, som alla fabriksmönster, hjälper oss att säkerställa att nya objekt skapas på rätt sätt. Vi använder den för att hantera "produktionen" av olika familjer av sammanlänkade objekt. Olika familjer av sammanlänkade föremål... Vad betyder det? Oroa dig inte: i praktiken är allt enklare än det kan verka. Till att börja med, vad kan en familj av sammankopplade objekt vara? Anta att vi utvecklar en militär strategi som involverar flera typer av enheter:- infanteri
- kavalleri
- bågskyttar
Låt oss fortsätta att automatisera vårt kafé
I sista lektionen, studerade vi fabriksmetodens mönster. Vi använde den för att utöka vår kaffeverksamhet och öppna flera nya platser. Idag fortsätter vi att modernisera vår verksamhet. Med hjälp av det abstrakta fabriksmönstret kommer vi att lägga grunden för en ny skrivbordsapplikation för att beställa kaffe online. När vi skriver en skrivbordsapplikation bör vi alltid tänka på plattformsoberoende stöd. Vår applikation måste fungera på både macOS och Windows (spoiler: Support för Linux är kvar för dig att implementera som läxa). Hur kommer vår ansökan att se ut? Ganska enkelt: det blir ett formulär som består av ett textfält, ett urvalsfält och en knapp. Om du har erfarenhet av att använda olika operativsystem har du säkert märkt att knappar på Windows renderas annorlunda än på en Mac. Precis som allt annat... Nåväl, låt oss börja.- knappar
- textfält
- urvalsfält
onClick
, onValueChanged
eller onInputChanged
. Med andra ord kan vi definiera metoder som gör att vi kan hantera olika händelser (trycka på en knapp, skriva in text, välja ett värde i en urvalsruta). Allt detta är medvetet utelämnat här för att inte överbelasta exemplet och för att göra det tydligare när vi studerar fabriksmönstret. Låt oss definiera abstrakta gränssnitt för våra produkter:
public interface Button {}
public interface Select {}
public interface TextField {}
För varje operativsystem måste vi skapa gränssnittselement i samma stil som operativsystemet. Vi kommer att skriva kod för Windows och MacOS. Låt oss skapa implementeringar för Windows:
public class WindowsButton implements Button {
}
public class WindowsSelect implements Select {
}
public class WindowsTextField implements TextField {
}
Nu gör vi samma sak för MacOS:
public class MacButton implements Button {
}
public class MacSelect implements Select {
}
public class MacTextField implements TextField {
}
Excellent. Nu kan vi fortsätta till vår abstrakta fabrik, som kommer att skapa alla tillgängliga abstrakta produkttyper:
public interface GUIFactory {
Button createButton();
TextField createTextField();
Select createSelect();
}
Utmärkt. Som ni ser har vi inte gjort något komplicerat än. Allt som följer är också enkelt. I analogi med produkterna skapar vi olika fabriksimplementeringar för varje OS. Låt oss börja med Windows:
public class WindowsGUIFactory implements GUIFactory {
public WindowsGUIFactory() {
System.out.println("Creating GUIFactory for Windows OS");
}
public Button createButton() {
System.out.println("Creating Button for Windows OS");
return new WindowsButton();
}
public TextField createTextField() {
System.out.println("Creating TextField for Windows OS");
return new WindowsTextField();
}
public Select createSelect() {
System.out.println("Creating Select for Windows OS");
return new WindowsSelect();
}
}
Vi har lagt till lite konsolutdata i metoderna och konstruktören för att ytterligare illustrera vad som händer. Nu för macOS:
public class MacGUIFactory implements GUIFactory {
public MacGUIFactory() {
System.out.println("Creating GUIFactory for macOS");
}
@Override
public Button createButton() {
System.out.println("Creating Button for macOS");
return new MacButton();
}
@Override
public TextField createTextField() {
System.out.println("Creating TextField for macOS");
return new MacTextField();
}
@Override
public Select createSelect() {
System.out.println("Creating Select for macOS");
return new MacSelect();
}
}
Observera att varje metodsignatur indikerar att metoden returnerar en abstrakt typ. Men inuti metoderna skapar vi specifika implementeringar av produkterna. Detta är det enda stället vi kontrollerar skapandet av specifika instanser. Nu är det dags att skriva en klass för formuläret. Detta är en Java-klass vars fält är gränssnittselement:
public class CoffeeOrderForm {
private final TextField customerNameTextField;
private final Select coffeeTypeSelect;
private final Button orderButton;
public CoffeeOrderForm(GUIFactory factory) {
System.out.println("Creating coffee order form");
customerNameTextField = factory.createTextField();
coffeeTypeSelect = factory.createSelect();
orderButton = factory.createButton();
}
}
En abstrakt fabrik som skapar gränssnittselement skickas till formulärets konstruktor. Vi kommer att skicka den nödvändiga fabriksimplementeringen till konstruktören för att skapa gränssnittselement för ett visst operativsystem.
public class Application {
private CoffeeOrderForm coffeeOrderForm;
public void drawCoffeeOrderForm() {
// Determine the name of the operating system through System.getProperty()
String osName = System.getProperty("os.name").toLowerCase();
GUIFactory guiFactory;
if (osName.startsWith("win")) { // For Windows
guiFactory = new WindowsGUIFactory();
} else if (osName.startsWith("mac")) { // For Mac
guiFactory = new MacGUIFactory();
} else {
System.out.println("Unknown OS. Unable to draw form :(");
return;
}
coffeeOrderForm = new CoffeeOrderForm(guiFactory);
}
public static void main(String[] args) {
Application application = new Application();
application.drawCoffeeOrderForm();
}
}
Om vi kör programmet på Windows får vi följande utdata:
Creating GUIFactory for Windows OS
Creating coffee order form
Creating TextField for Windows OS
Creating Select for Windows OS
Creating Button for Windows OS
På en Mac blir utdata som följer:
Creating GUIFactory for macOS
Creating coffee order form
Creating TextField for macOS
Creating Select for macOS
Creating Button for macOS
På Linux:
Unknown OS. Unable to draw form :(
Och nu sammanfattar vi. Vi skrev skelettet till en GUI-baserad applikation där gränssnittselementen skapas specifikt för det relevanta operativsystemet. Vi ska kortfattat upprepa vad vi har skapat:
- En produktfamilj som består av ett inmatningsfält, ett urvalsfält och en knapp.
- Olika implementeringar av produktfamiljen för Windows och macOS.
- En abstrakt fabrik som definierar ett gränssnitt för att skapa våra produkter.
- Två implementeringar av vår fabrik, var och en ansvarig för att skapa en specifik produktfamilj.
- Ett formulär (en Java-klass) vars fält är abstrakta gränssnittselement som initieras med de nödvändiga värdena i konstruktorn med hjälp av en abstrakt fabrik.
- Applikationsklass Inuti denna klass skapar vi ett formulär som skickar den önskade fabriksimplementeringen till dess konstruktör.
Abstrakt fabrik: hur man använder
En abstrakt fabrik är ett designmönster för att hantera skapandet av olika produktfamiljer utan att vara bunden till konkreta produktklasser. När du använder det här mönstret måste du:- Definiera produktfamiljer. Anta att vi har två av dem:
SpecificProductA1
,SpecificProductB1
SpecificProductA2
,SpecificProductB2
- För varje produkt inom familjen, definiera en abstrakt klass (gränssnitt). I vårt fall har vi:
ProductA
ProductB
- Inom varje produktfamilj måste varje produkt implementera det gränssnitt som definierades i steg 2.
- Skapa en abstrakt fabrik, med metoder för att skapa varje produkt som definieras i steg 2. I vårt fall kommer dessa metoder att vara:
ProductA createProductA();
ProductB createProductB();
- Skapa abstrakta fabriksimplementeringar så att varje implementering styr skapandet av produkter från en enda familj. För att göra detta, inuti varje implementering av den abstrakta fabriken, måste du implementera alla skapelsemetoder så att de skapar och returnerar specifika produktimplementationer.
// Define common product interfaces
public interface ProductA {}
public interface ProductB {}
// Create various implementations (families) of our products
public class SpecificProductA1 implements ProductA {}
public class SpecificProductB1 implements ProductB {}
public class SpecificProductA2 implements ProductA {}
public class SpecificProductB2 implements ProductB {}
// Create an abstract factory
public interface AbstractFactory {
ProductA createProductA();
ProductB createProductB();
}
// Implement the abstract factory in order to create products in family 1
public class SpecificFactory1 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new SpecificProductA1();
}
@Override
public ProductB createProductB() {
return new SpecificProductB1();
}
}
// Implement the abstract factory in order to create products in family 2
public class SpecificFactory2 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new SpecificProductA2();
}
@Override
public ProductB createProductB() {
return new SpecificProductB2();
}
}
Läxa
För att förstärka materialet kan du göra två saker:- Förfina kaffebeställningsapplikationen så att den även fungerar på Linux.
- Skapa din egen abstrakta fabrik för att producera enheter som är involverade i alla militära strategier. Detta kan antingen vara en historisk militär strategi som involverar riktiga arméer, eller en fantasistrategi med orcher, tomtar och alver. Det viktiga är att välja något som intresserar dig. Var kreativ, skriv ut meddelanden på konsolen och njut av att lära dig om mönster!