Ciao! Oggi continueremo a studiare i design pattern e parleremo della fabbrica astratta . Cosa faremo durante la lezione:
- Parliamo di cos'è una fabbrica astratta e quale problema risolve questo modello;
- creeremo la struttura di un'applicazione multipiattaforma per ordinare il caffè con un'interfaccia utente;
- Studiamo le istruzioni per utilizzare questo modello con un diagramma e un codice;
- Come bonus, nella lezione è nascosto un uovo di Pasqua, grazie al quale imparerai a determinare il nome del sistema operativo utilizzando Java e, a seconda del risultato, eseguirai l'una o l'altra azione.
- eredità in Java;
- classi e metodi astratti in Java.
Quali problemi risolve il modello astratto della fabbrica?
La fabbrica astratta, come tutti i modelli di fabbrica, ci aiuta a organizzare correttamente la creazione di nuovi oggetti. Con il suo aiuto gestiamo il “rilascio” di varie famiglie di oggetti interconnessi. Varie famiglie di oggetti correlati...Che cos'è? Non preoccuparti: in pratica è tutto più semplice di quanto possa sembrare. Cominciamo con quella che potrebbe essere una famiglia di oggetti correlati? Supponiamo che stiamo sviluppando una strategia con te e che contenga diverse unità combattenti:- fanteria;
- cavalleria;
- arcieri.
Continuiamo ad automatizzare la caffetteria
Nell'ultima conferenza abbiamo studiato il modello del metodo di fabbrica, con l'aiuto del quale siamo stati in grado di espandere il business del caffè e aprire diversi nuovi punti vendita di caffè. Oggi continueremo il nostro lavoro per modernizzare la nostra attività. Utilizzando il modello astratto della fabbrica, getteremo le basi per una nuova applicazione desktop per ordinare il caffè online. Quando scriviamo un'applicazione per desktop, dovremmo sempre pensare alla multipiattaforma. La nostra applicazione dovrebbe funzionare sia su macOS che su Windows (spoiler: Linux ti verrà lasciato come compito a casa). Come sarà la nostra applicazione? Abbastanza semplice: questo sarà un modulo composto da un campo di testo, un campo di selezione e un pulsante. Se hai esperienza nell'utilizzo di diversi sistemi operativi, avrai sicuramente notato che su Windows i pulsanti vengono visualizzati in modo diverso rispetto a Mac. Come tutto il resto... Allora cominciamo. Nel ruolo delle famiglie di prodotti, come probabilmente hai già capito, avremo elementi dell'interfaccia grafica:- pulsanti;
- campi di testo;
- campi per la selezione.
onClick
, onValueChanged
o onInputChanged
. Quelli. metodi che ci permetteranno di gestire vari eventi (fare clic su un pulsante, inserire testo, selezionare un valore in una casella di selezione). Tutto questo è stato deliberatamente omesso per non sovraccaricare l'esempio e per renderlo più visivo per lo studio del modello di fabbrica. Definiamo le interfacce astratte per i nostri prodotti:
public interface Button {}
public interface Select {}
public interface TextField {}
Per ciascun sistema operativo, dobbiamo creare elementi di interfaccia nello stile di quel sistema operativo. Scriviamo per Windows e MacOS. Creiamo implementazioni per Windows:
public class WindowsButton implements Button {
}
public class WindowsSelect implements Select {
}
public class WindowsTextField implements TextField {
}
Ora lo stesso per MacOS:
public class MacButton implements Button {
}
public class MacSelect implements Select {
}
public class MacTextField implements TextField {
}
Grande. Ora possiamo avviare la nostra fabbrica astratta, che creerà tutti i tipi di prodotto astratti esistenti:
public interface GUIFactory {
Button createButton();
TextField createTextField();
Select createSelect();
}
Perfetto. Come puoi vedere, finora niente di complicato. Allora tutto è altrettanto semplice. Per analogia con i prodotti, creiamo diverse implementazioni della nostra fabbrica per ciascun sistema operativo. Cominciamo con Windows:
public class WindowsGUIFactory implements GUIFactory {
public WindowsGUIFactory() {
System.out.println("Creating gui factory 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();
}
}
È stato aggiunto l'output della console all'interno di metodi e costruttori per dimostrare ulteriormente come funziona. Ora per macOS:
public class MacGUIFactory implements GUIFactory {
public MacGUIFactory() {
System.out.println("Creating gui factory 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();
}
}
Nota: ogni metodo, in base alla sua firma, restituisce un tipo astratto. Ma all'interno del metodo creiamo un'implementazione concreta del prodotto. Questo è l'unico posto in cui controlliamo la creazione di istanze specifiche. Adesso è il momento di scrivere la classe del form. Questa è una classe Java i cui campi sono elementi di interfaccia:
public class OrderCoffeeForm {
private final TextField customerNameTextField;
private final Select coffeTypeSelect;
private final Button orderButton;
public OrderCoffeeForm(GUIFactory factory) {
System.out.println("Creating order coffee form");
customerNameTextField = factory.createTextField();
coffeTypeSelect = factory.createSelect();
orderButton = factory.createButton();
}
}
Una factory astratta viene passata al costruttore del form, che crea gli elementi dell'interfaccia. Passeremo l'implementazione di fabbrica richiesta al costruttore in modo da poter creare elementi di interfaccia per un particolare sistema operativo.
public class Application {
private OrderCoffeeForm orderCoffeeForm;
public void drawOrderCoffeeForm() {
// Определим Name операционной системы, получив meaning системной проперти через System.getProperty
String osName = System.getProperty("os.name").toLowerCase();
GUIFactory guiFactory;
if (osName.startsWith("win")) { // Для windows
guiFactory = new WindowsGUIFactory();
} else if (osName.startsWith("mac")) { // Для mac
guiFactory = new MacGUIFactory();
} else {
System.out.println("Unknown OS, can't draw form :( ");
return;
}
orderCoffeeForm = new OrderCoffeeForm(guiFactory);
}
public static void main(String[] args) {
Application application = new Application();
application.drawOrderCoffeeForm();
}
}
Se eseguiamo l'applicazione su Windows, otterremo il seguente output:
Creating gui factory for Windows OS
Creating order coffee form
Creating TextField for Windows OS
Creating Select for Windows OS
Creating Button for Windows OS
Su un Mac l'output sarà il seguente:
Creating gui factory for macOS
Creating order coffee form
Creating TextField for macOS
Creating Select for macOS
Creating Button for macOS
Su Linux:
Unknown OS, can't draw form :(
Bene, tu ed io traiamo la seguente conclusione. Abbiamo scritto un framework per un'applicazione GUI che crea esattamente quegli elementi dell'interfaccia appropriati per un dato sistema operativo. Ripetiamo brevemente ciò che abbiamo creato:
- Famiglia di prodotti: campo di immissione, campo di selezione e pulsante.
- Varie implementazioni di questa famiglia di prodotti, per Windows e macOS.
- Una fabbrica astratta, all'interno della quale abbiamo definito l'interfaccia per la realizzazione dei nostri prodotti.
- Due implementazioni della nostra fabbrica, ognuna delle quali è responsabile della creazione di una specifica famiglia di prodotti.
- Un modulo, una classe Java i cui campi sono elementi astratti dell'interfaccia che vengono inizializzati nel costruttore con i valori richiesti utilizzando una factory astratta.
- Classe di applicazione. Al suo interno creiamo un modulo con il quale passiamo al costruttore l'implementazione richiesta dalla nostra fabbrica.
Abstract Factory: istruzioni per l'uso
Abstract Factory è un design pattern per gestire la creazione di diverse famiglie di prodotti senza essere vincolati a specifiche classi di prodotto. Quando utilizzi questo modello, devi:- Definire le famiglie di prodotti stessi. Supponiamo di averne due:
SpecificProductA1
,SpecificProductB1
SpecificProductA2
,SpecificProductB2
- Per ogni prodotto all'interno della famiglia, definire una classe astratta (interfaccia). Nel nostro caso è:
ProductA
ProductB
- All'interno di ciascuna famiglia di prodotti, ciascun prodotto deve implementare l'interfaccia definita nel passaggio 2.
- Crea una fabbrica astratta, con metodi di creazione per ciascun prodotto definiti nel passaggio 2. Nel nostro caso, questi metodi saranno:
ProductA createProductA();
ProductB createProductB();
- Creare implementazioni della fabbrica astratta in modo che ciascuna implementazione controlli la creazione di prodotti della stessa famiglia. Per fare ciò, all'interno di ogni implementazione della fabbrica astratta, è necessario implementare tutti i metodi create, in modo che al loro interno vengano create e restituite implementazioni concrete dei prodotti.
// Определим общие интерфейсы продуктов
public interface ProductA {}
public interface ProductB {}
// Создадим различные реализации (семейства) наших продуктов
public class SpecificProductA1 implements ProductA {}
public class SpecificProductB1 implements ProductB {}
public class SpecificProductA2 implements ProductA {}
public class SpecificProductB2 implements ProductB {}
// Создадим абстрактную фабрику
public interface AbstractFactory {
ProductA createProductA();
ProductB createProductB();
}
// Создадим реализацию абстрактной фабрики для создания продуктов семейства 1
public class SpecificFactory1 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new SpecificProductA1();
}
@Override
public ProductB createProductB() {
return new SpecificProductB1();
}
}
// Создадим реализацию абстрактной фабрики для создания продуктов семейства 1
public class SpecificFactory2 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new SpecificProductA2();
}
@Override
public ProductB createProductB() {
return new SpecificProductB2();
}
}
Compiti a casa
Per consolidare il materiale puoi fare 2 cose:- Migliora l'applicazione per ordinare il caffè in modo che funzioni su Linux.
- Crea la tua fabbrica astratta per produrre unità di qualsiasi strategia. Può trattarsi di una strategia storica con eserciti reali o di una fantasia con orchi, nani ed elfi. La cosa principale è che lo trovi interessante. Diventa creativo, pubblica pin sulla console e divertiti ad imparare i modelli!
GO TO FULL VERSION