Hallo! Heute werden wir uns weiterhin mit Designmustern befassen und das abstrakte Fabrikmuster diskutieren . Folgendes werden wir in der Lektion behandeln:
- Wir werden diskutieren, was eine abstrakte Fabrik ist und welches Problem dieses Muster löst
- Wir erstellen das Grundgerüst einer plattformübergreifenden Anwendung zum Bestellen von Kaffee über eine Benutzeroberfläche
- Wir werden Anweisungen zur Verwendung dieses Musters studieren, einschließlich der Betrachtung eines Diagramms und eines Codes
- Und als Bonus enthält diese Lektion ein verstecktes Easter Egg, mit dem Sie lernen, wie Sie mit Java den Namen des Betriebssystems ermitteln und je nach Ergebnis die eine oder andere Aktion ausführen.
- Vererbung in Java
- abstrakte Klassen und Methoden in Java
Welche Probleme löst eine abstrakte Fabrik?
Eine abstrakte Fabrik hilft uns, wie alle Fabrikmuster, sicherzustellen, dass neue Objekte korrekt erstellt werden. Wir nutzen es, um die „Produktion“ verschiedener Familien miteinander verbundener Objekte zu verwalten. Verschiedene Familien miteinander verbundener Objekte... Was bedeutet das? Keine Sorge: In der Praxis ist alles einfacher, als es scheint. Was könnte zunächst einmal eine Familie miteinander verbundener Objekte sein? Angenommen, wir entwickeln eine Militärstrategie mit mehreren Arten von Einheiten:- Infanterie
- Kavallerie
- Bogenschützen
Lassen Sie uns unser Café weiter automatisieren
In der letzten Lektion, wir haben das Factory-Methodenmuster untersucht. Wir haben es genutzt, um unser Kaffeegeschäft auszubauen und mehrere neue Standorte zu eröffnen. Heute werden wir unser Unternehmen weiter modernisieren. Mithilfe des abstrakten Fabrikmusters legen wir den Grundstein für eine neue Desktop-Anwendung zur Online-Kaffeebestellung. Beim Schreiben einer Desktop-Anwendung sollten wir immer an die plattformübergreifende Unterstützung denken. Unsere Anwendung muss sowohl auf macOS als auch auf Windows funktionieren (Spoiler: Die Implementierung der Linux-Unterstützung bleibt Ihnen als Hausaufgabe überlassen). Wie wird unsere Bewerbung aussehen? Ganz einfach: Es wird ein Formular sein, das aus einem Textfeld, einem Auswahlfeld und einer Schaltfläche besteht. Wenn Sie Erfahrung mit verschiedenen Betriebssystemen haben, ist Ihnen sicherlich aufgefallen, dass Schaltflächen unter Windows anders dargestellt werden als auf einem Mac. Wie alles andere auch... Nun, fangen wir an.- Tasten
- Textfelder
- Auswahlfelder
onClick
, onValueChanged
oder definieren onInputChanged
. Mit anderen Worten: Wir könnten Methoden definieren, die es uns ermöglichen, verschiedene Ereignisse zu verarbeiten (Knopfdruck, Texteingabe, Auswahl eines Werts in einem Auswahlfeld). All dies wird hier bewusst weggelassen, um das Beispiel nicht zu überladen und es beim Studium des Fabrikmusters klarer zu machen. Definieren wir abstrakte Schnittstellen für unsere Produkte:
public interface Button {}
public interface Select {}
public interface TextField {}
Für jedes Betriebssystem müssen wir Schnittstellenelemente im Stil des Betriebssystems erstellen. Wir schreiben Code für Windows und MacOS. Lassen Sie uns Implementierungen für Windows erstellen:
public class WindowsButton implements Button {
}
public class WindowsSelect implements Select {
}
public class WindowsTextField implements TextField {
}
Jetzt machen wir dasselbe für MacOS:
public class MacButton implements Button {
}
public class MacSelect implements Select {
}
public class MacTextField implements TextField {
}
Exzellent. Jetzt können wir mit unserer abstrakten Fabrik fortfahren, die alle verfügbaren abstrakten Produkttypen erstellt:
public interface GUIFactory {
Button createButton();
TextField createTextField();
Select createSelect();
}
Hervorragend. Wie Sie sehen, haben wir noch nichts Kompliziertes gemacht. Auch alles, was folgt, ist einfach. Analog zu den Produkten erstellen wir für jedes Betriebssystem verschiedene Factory-Implementierungen. Beginnen wir mit 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();
}
}
Wir haben einige Konsolenausgaben innerhalb der Methoden und des Konstruktors hinzugefügt, um das Geschehen weiter zu veranschaulichen. Jetzt 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();
}
}
Beachten Sie, dass jede Methodensignatur angibt, dass die Methode einen abstrakten Typ zurückgibt. Aber innerhalb der Methoden erstellen wir spezifische Implementierungen der Produkte. Dies ist der einzige Ort, an dem wir die Erstellung bestimmter Instanzen kontrollieren. Jetzt ist es an der Zeit, eine Klasse für das Formular zu schreiben. Dies ist eine Java-Klasse, deren Felder Schnittstellenelemente sind:
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();
}
}
Eine abstrakte Fabrik, die Schnittstellenelemente erstellt, wird an den Konstruktor des Formulars übergeben. Wir übergeben die notwendige Factory-Implementierung an den Konstruktor, um Schnittstellenelemente für ein bestimmtes Betriebssystem zu erstellen.
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();
}
}
Wenn wir die Anwendung unter Windows ausführen, erhalten wir die folgende Ausgabe:
Creating GUIFactory for Windows OS
Creating coffee order form
Creating TextField for Windows OS
Creating Select for Windows OS
Creating Button for Windows OS
Auf einem Mac sieht die Ausgabe wie folgt aus:
Creating GUIFactory for macOS
Creating coffee order form
Creating TextField for macOS
Creating Select for macOS
Creating Button for macOS
Unter Linux:
Unknown OS. Unable to draw form :(
Und jetzt fassen wir zusammen. Wir haben das Grundgerüst einer GUI-basierten Anwendung geschrieben, in der die Schnittstellenelemente speziell für das jeweilige Betriebssystem erstellt werden. Wir wiederholen kurz und bündig, was wir erstellt haben:
- Eine Produktfamilie bestehend aus einem Eingabefeld, einem Auswahlfeld und einer Schaltfläche.
- Verschiedene Implementierungen der Produktfamilie für Windows und macOS.
- Eine abstrakte Fabrik, die eine Schnittstelle zur Erstellung unserer Produkte definiert.
- Zwei Implementierungen unserer Fabrik, die jeweils für die Herstellung einer bestimmten Produktfamilie verantwortlich sind.
- Ein Formular (eine Java-Klasse), dessen Felder abstrakte Schnittstellenelemente sind, die im Konstruktor mithilfe einer abstrakten Factory mit den erforderlichen Werten initialisiert werden.
- Anwendungsklasse Innerhalb dieser Klasse erstellen wir ein Formular und übergeben die gewünschte Factory-Implementierung an seinen Konstruktor.
Abstrakte Fabrik: Verwendung
Eine abstrakte Fabrik ist ein Entwurfsmuster zur Verwaltung der Erstellung verschiedener Produktfamilien, ohne an konkrete Produktklassen gebunden zu sein. Wenn Sie dieses Muster verwenden, müssen Sie:- Produktfamilien definieren. Angenommen, wir haben zwei davon:
SpecificProductA1
,SpecificProductB1
SpecificProductA2
,SpecificProductB2
- Definieren Sie für jedes Produkt innerhalb der Familie eine abstrakte Klasse (Schnittstelle). In unserem Fall haben wir:
ProductA
ProductB
- Innerhalb jeder Produktfamilie muss jedes Produkt die in Schritt 2 definierte Schnittstelle implementieren.
- Erstellen Sie eine abstrakte Fabrik mit Methoden zum Erstellen jedes in Schritt 2 definierten Produkts. In unserem Fall sind diese Methoden:
ProductA createProductA();
ProductB createProductB();
- Erstellen Sie abstrakte Factory-Implementierungen, sodass jede Implementierung die Erstellung von Produkten einer einzelnen Familie steuert. Dazu müssen Sie in jeder Implementierung der abstrakten Fabrik alle Erstellungsmethoden implementieren, damit sie bestimmte Produktimplementierungen erstellen und zurückgeben.
// 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();
}
}
Hausaufgaben
Um das Material zu verstärken, können Sie zwei Dinge tun:- Verfeinern Sie die Kaffee-Bestellanwendung so, dass sie auch unter Linux funktioniert.
- Erstellen Sie Ihre eigene abstrakte Fabrik zur Herstellung von Einheiten, die an jeder militärischen Strategie beteiligt sind. Dies kann entweder eine historische Militärstrategie mit echten Armeen oder eine Fantasy-Strategie mit Orks, Gnomen und Elfen sein. Wichtig ist, dass Sie etwas auswählen, das Sie interessiert. Seien Sie kreativ, drucken Sie Nachrichten auf der Konsole und lernen Sie gerne etwas über Muster!
GO TO FULL VERSION