CodeGym /Java Blog /Random-IT /Modelli di progettazione: fabbrica astratta
John Squirrels
Livello 41
San Francisco

Modelli di progettazione: fabbrica astratta

Pubblicato nel gruppo Random-IT
CIAO! Oggi continueremo a studiare i design pattern e parleremo del pattern factory astratto . Modelli di progettazione: Fabbrica astratta - 1Ecco cosa tratteremo nella lezione:
  • 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.
Per comprendere appieno questo modello, devi essere esperto nei seguenti argomenti:
  • 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
Questi tipi di unità sono interconnessi, perché prestano servizio nello stesso esercito. Potremmo dire che le categorie sopra elencate sono una famiglia di oggetti interconnessi. Lo capiamo. Ma il modello astratto della fabbrica viene utilizzato per organizzare la creazione di varie famiglie di oggetti interconnessi. Anche qui non c'è niente di complicato. Continuiamo con l'esempio della strategia militare. In generale, le unità militari appartengono a diverse parti in guerra. A seconda di quale parte stanno, le unità militari possono variare in modo significativo nell'aspetto. I fanti, i cavalieri e gli arcieri dell'esercito romano non sono gli stessi dei fanti, dei cavalieri e degli arcieri vichinghi. Nella strategia militare, i soldati di diversi eserciti sono diverse famiglie di oggetti interconnessi. Sarebbe divertente se un programmatore' Questo errore fece sì che un soldato in uniforme francese dell'era napoleonica, moschetto alla mano, si trovasse a camminare tra le file della fanteria romana. Il modello astratto di progettazione della fabbrica è necessario proprio per risolvere questo problema. No, non il problema dell'imbarazzo che può derivare dal viaggio nel tempo, ma il problema di creare vari gruppi di oggetti interconnessi. Una fabbrica astratta fornisce un'interfaccia per la creazione di tutti i prodotti disponibili (una famiglia di oggetti). Una factory astratta in genere ha più implementazioni. Ognuno di loro è responsabile della creazione dei prodotti di una delle famiglie. La nostra strategia militare includerebbe una fabbrica astratta che crea fanti, arcieri e cavalieri astratti, nonché implementazioni di questa fabbrica. Per esempio, una fabbrica che crea legionari romani e una fabbrica che crea soldati cartaginesi. L'astrazione è il principio guida più importante di questo modello. I clienti della fabbrica lavorano con la fabbrica ei suoi prodotti solo attraverso interfacce astratte. Di conseguenza, non devi pensare a quali soldati vengono attualmente creati. Invece, trasferisci questa responsabilità a qualche implementazione concreta della fabbrica astratta.

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
Dichiarazione di non responsabilità: in ogni interfaccia, potremmo definire metodi come onClick, onValueChangedo 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.
Il risultato è che abbiamo implementato il modello di fabbrica astratto.

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:
  1. Definire famiglie di prodotti. Supponiamo di averne due:
    • SpecificProductA1,SpecificProductB1
    • SpecificProductA2,SpecificProductB2
  2. Per ogni prodotto all'interno della famiglia, definire una classe astratta (interfaccia). Nel nostro caso abbiamo:
    • ProductA
    • ProductB
  3. All'interno di ogni famiglia di prodotti, ogni prodotto deve implementare l'interfaccia definita nel passaggio 2.
  4. 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();
  5. 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.
Il seguente diagramma UML illustra le istruzioni descritte sopra: Modelli di progettazione: Fabbrica astratta - 3Ora scriveremo il codice secondo queste istruzioni:

    // 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:
  1. Perfeziona l'applicazione per ordinare il caffè in modo che funzioni anche su Linux.
  2. 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!
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION