CIAO! Oggi continueremo a studiare i design pattern e parleremo del pattern factory astratto .
Ecco cosa tratteremo nella lezione:
Ora scriveremo il codice secondo queste istruzioni:

- Discuteremo cos'è una fabbrica astratta e quale problema risolve questo schema
- Creeremo lo scheletro di un'applicazione multipiattaforma per ordinare il caffè attraverso un'interfaccia utente
- Studieremo le istruzioni su come utilizzare questo modello, incluso l'esame di un diagramma e di un codice
- E come bonus, questa lezione include un uovo di Pasqua nascosto che ti aiuterà a imparare come usare Java per determinare il nome del sistema operativo e, a seconda del risultato, eseguire l'una o l'altra azione.
- ereditarietà in Java
- classi e metodi astratti in Java
Quali problemi risolve una fabbrica astratta?
Una fabbrica astratta, come tutti i modelli di fabbrica, ci aiuta a garantire che i nuovi oggetti vengano creati correttamente. Lo usiamo per gestire la "produzione" di varie famiglie di oggetti interconnessi. Varie famiglie di oggetti interconnessi... Cosa significa? Niente paura: in pratica è tutto più semplice di quanto possa sembrare. Tanto per cominciare, cosa potrebbe essere una famiglia di oggetti interconnessi? Supponiamo di sviluppare una strategia militare che coinvolga diversi tipi di unità:- fanteria
- cavalleria
- arcieri
Continuiamo ad automatizzare la nostra caffetteria
Nell'ultima lezione, abbiamo studiato il pattern del metodo di fabbrica. L'abbiamo utilizzato per espandere la nostra attività nel settore del caffè e aprire diverse nuove sedi. Oggi continueremo a modernizzare la nostra attività. Utilizzando il modello astratto della fabbrica, getteremo le basi per una nuova applicazione desktop per ordinare il caffè online. Quando si scrive un'applicazione desktop, dovremmo sempre pensare al supporto multipiattaforma. La nostra applicazione deve funzionare sia su macOS che su Windows (spoiler: il supporto per Linux è lasciato a te da implementare come compito a casa). Come sarà la nostra applicazione? Abbastanza semplice: sarà un form composto da un campo di testo, un campo di selezione e un pulsante. Se hai esperienza nell'uso di diversi sistemi operativi, avrai sicuramente notato che i pulsanti su Windows sono resi in modo diverso rispetto a un Mac. Come tutto il resto... Bene, cominciamo.- pulsanti
- campi di testo
- campi di selezione
onClick
, onValueChanged
o onInputChanged
. In altre parole, potremmo definire metodi che ci permetteranno di gestire vari eventi (pressione di un pulsante, inserimento di testo, selezione di un valore in una casella di selezione). Tutto questo è volutamente omesso qui per non sovraccaricare l'esempio e per renderlo più chiaro mentre studiamo il modello di fabbrica. Definiamo interfacce astratte per i nostri prodotti:
public interface Button {}
public interface Select {}
public interface TextField {}
Per ogni sistema operativo, dobbiamo creare elementi di interfaccia nello stile del sistema operativo. Scriveremo codice per Windows e MacOS. Creiamo implementazioni per Windows:
public class WindowsButton implements Button {
}
public class WindowsSelect implements Select {
}
public class WindowsTextField implements TextField {
}
Ora facciamo lo stesso per MacOS:
public class MacButton implements Button {
}
public class MacSelect implements Select {
}
public class MacTextField implements TextField {
}
Eccellente. Ora possiamo procedere alla nostra fabbrica astratta, che creerà tutti i tipi di prodotti astratti disponibili:
public interface GUIFactory {
Button createButton();
TextField createTextField();
Select createSelect();
}
Stupendo. Come puoi vedere, non abbiamo ancora fatto nulla di complicato. Anche tutto ciò che segue è semplice. Per analogia con i prodotti, creiamo varie implementazioni di fabbrica per ciascun sistema operativo. Iniziamo con 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();
}
}
Abbiamo aggiunto alcuni output della console all'interno dei metodi e del costruttore per illustrare ulteriormente cosa sta accadendo. Ora per 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();
}
}
Si noti che ogni firma del metodo indica che il metodo restituisce un tipo astratto. Ma all'interno dei metodi, stiamo creando implementazioni specifiche dei prodotti. Questo è l'unico posto in cui controlliamo la creazione di istanze specifiche. Ora è il momento di scrivere una classe per il modulo. Questa è una classe Java i cui campi sono elementi di interfaccia:
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();
}
}
Una factory astratta che crea elementi di interfaccia viene passata al costruttore del form. Passeremo l'implementazione di fabbrica necessaria al costruttore per creare elementi di interfaccia per un particolare sistema operativo.
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();
}
}
Se eseguiamo l'applicazione su Windows, otteniamo il seguente output:
Creating GUIFactory for Windows OS
Creating coffee order 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 GUIFactory for macOS
Creating coffee order form
Creating TextField for macOS
Creating Select for macOS
Creating Button for macOS
Su Linux:
Unknown OS. Unable to draw form :(
E ora riassumiamo. Abbiamo scritto lo scheletro di un'applicazione basata su GUI in cui gli elementi dell'interfaccia vengono creati appositamente per il relativo sistema operativo. Ripetiamo brevemente ciò che abbiamo creato:
- Una famiglia di prodotti costituita da un campo di immissione, un campo di selezione e un pulsante.
- Diverse implementazioni della famiglia di prodotti per Windows e macOS.
- Una fabbrica astratta che definisce un'interfaccia per la creazione dei nostri prodotti.
- Due implementazioni della nostra fabbrica, ciascuna responsabile della creazione di una specifica famiglia di prodotti.
- Un form (una classe Java) i cui campi sono elementi di interfaccia astratti che vengono inizializzati con i valori necessari nel costruttore utilizzando una factory astratta.
- Classe Application All'interno di questa classe, creiamo un form, passando l'implementazione factory desiderata al suo costruttore.
Fabbrica astratta: come si usa
Una fabbrica astratta è un modello di progettazione per gestire la creazione di varie famiglie di prodotti senza essere legato a classi di prodotti concrete. Quando si utilizza questo modello, è necessario:- Definire famiglie di prodotti. Supponiamo di averne due:
SpecificProductA1
,SpecificProductB1
SpecificProductA2
,SpecificProductB2
- Per ogni prodotto all'interno della famiglia, definire una classe astratta (interfaccia). Nel nostro caso abbiamo:
ProductA
ProductB
- All'interno di ogni famiglia di prodotti, ogni prodotto deve implementare l'interfaccia definita nel passaggio 2.
- Creare una fabbrica astratta, con i metodi per la creazione di ciascun prodotto definiti nel passaggio 2. Nel nostro caso, questi metodi saranno:
ProductA createProductA();
ProductB createProductB();
- Crea implementazioni factory astratte in modo che ogni implementazione controlli la creazione di prodotti di una singola famiglia. Per fare ciò, all'interno di ogni implementazione della factory astratta, è necessario implementare tutti i metodi creations in modo che creino e restituiscano implementazioni di prodotto specifiche.

// 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();
}
}
Compiti a casa
Per rinforzare il materiale, puoi fare 2 cose:- Perfeziona l'applicazione per ordinare il caffè in modo che funzioni anche su Linux.
- Crea la tua fabbrica astratta per la produzione di unità coinvolte in qualsiasi strategia militare. Questa può essere una strategia militare storica che coinvolge eserciti reali o una fantasia con orchi, gnomi ed elfi. L'importante è scegliere qualcosa che ti interessi. Sii creativo, stampa i messaggi sulla console e divertiti a conoscere i modelli!
GO TO FULL VERSION