JavaRush /Java Blog /Random-IT /Modelli di progettazione: AbstractFactory

Modelli di progettazione: AbstractFactory

Pubblicato nel gruppo Random-IT
Ciao! Oggi continueremo a studiare i design pattern e parleremo della fabbrica astratta . Modelli di progettazione: AbstractFactory - 1Cosa 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.
Per comprendere appieno questo modello, è necessario avere una buona conoscenza dei seguenti argomenti:
  • 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.
Questi tipi di unità combattenti sono legati tra loro perché prestano servizio nello stesso esercito. Possiamo dire che le categorie sopra elencate sono una famiglia di oggetti interconnessi. Questo è tutto risolto. Ma il modello astratto della fabbrica viene utilizzato per organizzare la creazione di varie famiglie di oggetti interconnessi. Anche qui niente di complicato. Continuiamo l'esempio con la strategia. Di solito hanno diversi lati opposti. Le unità combattenti di diverse parti possono differire 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 dei Vichinghi. Nell'ambito della strategia, i soldati di diversi eserciti sono diverse famiglie di oggetti interconnessi. Sarebbe divertente se, per errore di un programmatore, un soldato in uniforme francese dell'epoca di Napoleone, con un moschetto pronto, girasse tra la fanteria romana. È per risolvere un problema del genere che è necessario il modello di progettazione astratta della fabbrica. No, non i problemi legati all'imbarazzo del viaggio nel tempo, ma la creazione di vari gruppi di oggetti interconnessi. Una fabbrica astratta fornisce un'interfaccia per la creazione di tutti i prodotti esistenti (oggetti della famiglia). Una factory astratta ha in genere più implementazioni. Ognuno di loro è responsabile della creazione di prodotti di una delle varianti. Come parte della strategia, avremmo una fabbrica astratta che crea fanteria, arcieri e cavalleria astratti, nonché implementazioni di questa fabbrica. Una fabbrica che crea legionari romani e, ad esempio, una fabbrica che crea guerrieri cartaginesi. L’astrazione è il principio più importante di questo modello. I clienti della fabbrica lavorano con esso e con i prodotti solo attraverso interfacce astratte. Pertanto, non dobbiamo pensare al tipo di guerrieri che stiamo creando attualmente, ma trasferire questa responsabilità a qualche implementazione specifica della fabbrica astratta.

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.
Disclaimer. All'interno di ciascuna interfaccia potremmo definire metodi come onClick, onValueChangedo 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.
Totale: abbiamo implementato il modello di fabbrica astratta.

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:
  1. Definire le famiglie di prodotti stessi. Supponiamo di averne due:
    • SpecificProductA1,SpecificProductB1
    • SpecificProductA2,SpecificProductB2
  2. Per ogni prodotto all'interno della famiglia, definire una classe astratta (interfaccia). Nel nostro caso è:
    • ProductA
    • ProductB
  3. All'interno di ciascuna famiglia di prodotti, ciascun prodotto deve implementare l'interfaccia definita nel passaggio 2.
  4. 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();
  5. 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.
Di seguito è riportato un diagramma UML che illustra le istruzioni sopra descritte: Modelli di progettazione: AbstractFactory - 3Ora scriviamo il codice per questa istruzione:
// Определим общие интерфейсы продуктов
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:
  1. Migliora l'applicazione per ordinare il caffè in modo che funzioni su Linux.
  2. 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!
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION