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

Modelli di progettazione: Metodo di fabbrica

Pubblicato nel gruppo Random-IT
CIAO! Oggi continueremo a studiare i design pattern e parleremo del factory method pattern. Modelli di progettazione: Metodo di fabbrica - 1 Scoprirai di cosa si tratta e per quali compiti è adatto questo modello. Prenderemo in considerazione questo modello di progettazione nella pratica e ne studieremo la struttura. Per garantire che tutto sia chiaro, è necessario comprendere i seguenti argomenti:
  1. Ereditarietà in Java.
  2. Metodi astratti e classi in Java

Quale problema risolve il metodo factory?

Tutti i pattern di factory design hanno due tipi di partecipanti: creatori (le fabbriche stesse) e prodotti (gli oggetti creati dalle fabbriche). Immagina la seguente situazione: abbiamo una fabbrica che produce auto a marchio CodeGym. Sa creare modelli di auto con vari tipi di carrozzeria:
  • berline
  • station wagon
  • coupé
La nostra attività prosperò così tanto che un bel giorno acquisimmo un'altra casa automobilistica: OneAuto. Essendo imprenditori ragionevoli, non vogliamo perdere alcun cliente OneAuto, quindi ci troviamo di fronte al compito di ristrutturare la produzione in modo da poter produrre:
  • berline CodeGym
  • Le station wagon CodeGym
  • Coupé CodeGym
  • Le berline OneAuto
  • Le station wagon OneAuto
  • OneAuto coupé
Come puoi vedere, invece di un gruppo di prodotti, ora ne abbiamo due e differiscono in alcuni dettagli. Il modello di progettazione del metodo di fabbrica è per quando abbiamo bisogno di creare diversi gruppi di prodotti, ognuno dei quali ha alcuni tratti specifici. Prenderemo in pratica il principio guida di questo modello, passando gradualmente dal semplice al complesso, utilizzando l'esempio della nostra caffetteria, che abbiamo creato in una delle lezioni precedenti .

Un po 'sul modello di fabbrica

Lascia che ti ricordi che in precedenza abbiamo creato un piccolo bar virtuale. Con l'aiuto di una semplice fabbrica, abbiamo imparato a creare diversi tipi di caffè. Oggi rielaboreremo questo esempio. Ricordiamo come appariva la nostra caffetteria, con la sua semplice fabbrica. Abbiamo tenuto una lezione di caffè:

public class Coffee {
    public void grindCoffee(){
        // Grind the coffee
    }
    public void makeCoffee(){
        // Brew the coffee
    }
    public void pourIntoCup(){
        // Pour into a cup
    }
}
E diverse classi figlio corrispondenti a specifici tipi di caffè che la nostra fabbrica potrebbe produrre:

public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Abbiamo creato un'enumerazione per semplificare l'inserimento degli ordini:

public enum CoffeeType {
    ESPRESSO,
    AMERICANO,
    CAFFE_LATTE,
    CAPPUCCINO
}
La stessa fabbrica di caffè aveva questo aspetto:

public class SimpleCoffeeFactory {
    public Coffee createCoffee(CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new Americano();
                break;
            case ESPRESSO:
                coffee = new Espresso();
                break;
            case CAPPUCCINO:
                coffee = new Cappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new CaffeLatte();
                break;
        }
        
        return coffee;
    }
}
E infine, la caffetteria stessa aveva questo aspetto:

public class CoffeeShop {

    private final SimpleCoffeeFactory coffeeFactory;

    public CoffeeShop(SimpleCoffeeFactory coffeeFactory) {
        this.coffeeFactory = coffeeFactory;
    }

    public Coffee orderCoffee(CoffeeType type) {
        Coffee coffee = coffeeFactory.createCoffee(type);
        coffee.grindCoffee();
        coffee.makeCoffee();
        coffee.pourIntoCup();

        System.out.println("Here's your coffee! Thanks! Come again!");
        return coffee;
    }
}

Modernizzazione di una fabbrica semplice

La nostra caffetteria funziona molto bene. Tanto che stiamo pensando di espanderci. Vogliamo aprire alcune nuove sedi. Siamo audaci e intraprendenti, quindi non sforneremo noiosi bar. Vogliamo che ogni negozio abbia un tocco speciale. Per cominciare, quindi, apriremo due sedi: una italiana e una americana. Questi cambiamenti riguarderanno non solo il design degli interni, ma anche le bevande offerte:
  • nella caffetteria italiana utilizzeremo esclusivamente marche di caffè italiano, con macinatura e tostatura speciali.
  • la sede americana avrà porzioni più grandi e serviremo marshmallow con ogni ordine.
L'unica cosa che rimane invariata è il nostro modello di business, che si è dimostrato eccellente. In termini di codice, questo è ciò che accade. Avevamo 4 classi corrispondenti ai nostri prodotti:

public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Ma ora ne avremo 8:

public class ItalianStyleAmericano extends Coffee {}
public class ItalianStyleCappucino extends Coffee {}
public class ItalianStyleCaffeLatte extends Coffee {}
public class ItalianStyleEspresso extends Coffee {}

public class AmericanStyleAmericano extends Coffee {}
public class AmericanStyleCappucino extends Coffee {}
public class AmericanStyleCaffeLatte extends Coffee {}
public class AmericanStyleEspresso extends Coffee {}
Dal momento che vogliamo mantenere l'attuale modello di business, vogliamo che il orderCoffee(CoffeeType type)metodo subisca il minor numero di modifiche possibile. Dai un'occhiata:

public Coffee orderCoffee(CoffeeType type) {
    Coffee coffee = coffeeFactory.createCoffee(type);
    coffee.grindCoffee();
    coffee.makeCoffee();
    coffee.pourIntoCup();

    System.out.println("Here's your coffee! Thanks! Come again!");
    return coffee;
}
Quali opzioni abbiamo? Bene, sappiamo già come scrivere una fabbrica, giusto? La cosa più semplice che mi viene subito in mente è scrivere due factory simili e poi passare l'implementazione desiderata al costruttore del nostro coffee shop. In questo modo, la classe del bar non cambierà. Per prima cosa, dobbiamo creare una nuova classe factory, fare in modo che erediti la nostra factory semplice e quindi sovrascrivere il createCoffee(CoffeeType type)metodo. Scriviamo fabbriche per creare caffè all'italiana e caffè all'americana:

public class SimpleItalianCoffeeFactory extends SimpleCoffeeFactory {

    @Override
    public Coffee createCoffee(CoffeeType type) {
        Coffee coffee = null;
        switch (type) {
            case AMERICANO:
                coffee = new ItalianStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new ItalianStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new ItalianStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new ItalianStyleCaffeLatte();
                break;
        }
        return coffee;
    }
}

public class SimpleAmericanCoffeeFactory extends SimpleCoffeeFactory{

    @Override
    public Coffee createCoffee (CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new AmericanStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new AmericanStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new AmericanStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new AmericanStyleCaffeLatte();
                break;
        }

        return coffee;
    }

}
Ora possiamo passare l'implementazione di fabbrica desiderata a CoffeeShop. Vediamo come sarebbe il codice per ordinare il caffè da diverse caffetterie. Ad esempio, cappuccino all'italiana e all'americana:

public class Main {
    public static void main(String[] args) {
        /*
            Order an Italian-style cappuccino:
            1. Create a factory for making Italian coffee
            2. Create a new coffee shop, passing the Italian coffee factory to it through the constructor
            3. Order our coffee
         */
        SimpleItalianCoffeeFactory italianCoffeeFactory = new SimpleItalianCoffeeFactory();
        CoffeeShop italianCoffeeShop = new CoffeeShop(italianCoffeeFactory);
        italianCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);
        
        
         /*
            Order an American-style cappuccino
            1. Create a factory for making American coffee
            2. Create a new coffee shop, passing the American coffee factory to it through the constructor
            3. Order our coffee
         */
        SimpleAmericanCoffeeFactory americanCoffeeFactory = new SimpleAmericanCoffeeFactory();
        CoffeeShop americanCoffeeShop = new CoffeeShop(americanCoffeeFactory);
        americanCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);
    }
}
Abbiamo creato due diverse caffetterie, passando a ciascuna la fabbrica desiderata. Da un lato, abbiamo raggiunto il nostro obiettivo, ma dall'altro... In qualche modo questo non va bene per gli imprenditori... Scopriamo cosa c'è che non va. Innanzitutto, l'abbondanza di fabbriche. Che cosa? Ora, per ogni nuova sede, dovremmo creare la propria fabbrica e, in aggiunta a ciò, assicurarci che la relativa fabbrica venga passata al costruttore durante la creazione di una caffetteria? In secondo luogo, è ancora una semplice fabbrica. Appena modernizzato leggermente. Ma siamo qui per imparare un nuovo schema. Terzo, non è possibile un approccio diverso? Sarebbe bello se potessimo inserire tutte le questioni relative alla preparazione del caffè nelCoffeeShopclasse collegando i processi di creazione del caffè e di servizio degli ordini, pur mantenendo una flessibilità sufficiente per realizzare vari stili di caffè. La risposta è sì, possiamo. Questo è chiamato modello di progettazione del metodo di fabbrica.

Da una semplice fabbrica a un metodo di fabbrica

Per risolvere il compito nel modo più efficiente possibile:
  1. Restituiamo il createCoffee(CoffeeType type)metodo alla CoffeeShopclasse.
  2. Renderemo questo metodo astratto.
  3. La CoffeeShopclasse stessa diventerà astratta.
  4. La CoffeeShopclasse avrà classi figlio.
Si Amico. La caffetteria italiana non è altro che un discendente della CoffeeShopclasse, che attua il createCoffee(CoffeeType type)metodo secondo le migliori tradizioni dei baristi italiani. Ora, un passo alla volta. Passaggio 1. Rendere la Coffeeclasse astratta. Abbiamo due intere famiglie di prodotti diversi. Tuttavia, i caffè italiani e americani hanno un antenato comune: la Coffeeclasse. Sarebbe opportuno renderlo astratto:

public abstract class Coffee {
    public void makeCoffee(){
        // Brew the coffee
    }
    public void pourIntoCup(){
        // Pour into a cup
    }
}
Passaggio 2. Rendi CoffeeShopastratto, con un createCoffee(CoffeeType type)metodo astratto

public abstract class CoffeeShop {

    public Coffee orderCoffee(CoffeeType type) {
        Coffee coffee = createCoffee(type);

        coffee.makeCoffee();
        coffee.pourIntoCup();

        System.out.println("Here's your coffee! Thanks! Come again!");
        return coffee;
    }

    protected abstract Coffee createCoffee(CoffeeType type);
}
Passaggio 3. Crea una caffetteria italiana, che è un discendente della caffetteria astratta. Implementiamo il createCoffee(CoffeeType type)metodo in esso, tenendo conto delle specificità delle ricette italiane.

public class ItalianCoffeeShop extends CoffeeShop {

    @Override
    public Coffee createCoffee (CoffeeType type) {
        Coffee coffee = null;
        switch (type) {
            case AMERICANO:
                coffee = new ItalianStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new ItalianStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new ItalianStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new ItalianStyleCaffeLatte();
                break;
        }
        return coffee;
    }
}
Passaggio 4. Facciamo lo stesso per la caffetteria in stile americano

public class AmericanCoffeeShop extends CoffeeShop {
    @Override
    public Coffee createCoffee(CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new AmericanStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new AmericanStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new AmericanStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new AmericanStyleCaffeLatte();
                break;
        }

        return coffee;
    }
}
Passaggio 5. Scopri come appariranno i caffelatte americani e italiani:

public class Main {
    public static void main(String[] args) {
        CoffeeShop italianCoffeeShop = new ItalianCoffeeShop();
        italianCoffeeShop.orderCoffee(CoffeeType.CAFFE_LATTE);

        CoffeeShop americanCoffeeShop = new AmericanCoffeeShop();
        americanCoffeeShop.orderCoffee(CoffeeType.CAFFE_LATTE);
    }
}
Congratulazioni. Abbiamo appena implementato il modello di progettazione del metodo di fabbrica utilizzando la nostra caffetteria come esempio.

Il principio alla base dei metodi di fabbrica

Ora consideriamo più in dettaglio ciò che abbiamo ottenuto. Il diagramma seguente mostra le classi risultanti. I blocchi verdi sono classi di creatori e i blocchi blu sono classi di prodotti. Modelli di progettazione: Metodo di fabbrica - 2Quali conclusioni possiamo trarre?
  1. Tutti i prodotti sono implementazioni della Coffeeclasse astratta.
  2. Tutti i creatori sono implementazioni della CoffeeShopclasse astratta.
  3. Vediamo due gerarchie di classi parallele:
    • Gerarchia dei prodotti. Vediamo discendenti italiani e discendenti americani
    • Gerarchia dei creatori. Vediamo discendenti italiani e discendenti americani
  4. La CoffeeShopsuperclasse non ha informazioni su quale prodotto specifico ( Coffee) verrà creato.
  5. La CoffeeShopsuperclasse delega la creazione di un prodotto specifico ai suoi discendenti.
  6. Ogni discendente della CoffeeShopclasse implementa un createCoffee()metodo factory secondo le proprie caratteristiche specifiche. In altre parole, le implementazioni delle classi producer preparano prodotti specifici basati sulle specifiche della classe producer.
Ora sei pronto per la definizione del pattern del metodo factory . Il pattern del metodo factory definisce un'interfaccia per la creazione di un oggetto, ma consente alle sottoclassi di selezionare la classe dell'oggetto creato. Pertanto, un metodo factory delega la creazione di un'istanza alle sottoclassi. In generale, ricordare la definizione non è importante quanto capire come funziona.

Struttura di un metodo di fabbrica

Modelli di progettazione: Metodo di fabbrica - 3Il diagramma sopra mostra la struttura generale del pattern del metodo factory. Cos'altro è importante qui?
  1. La classe Creator implementa tutti i metodi che interagiscono con i prodotti, ad eccezione del metodo factory.
  2. Il metodo astratto factoryMethod()deve essere implementato da tutti i discendenti della Creatorclasse.
  3. La ConcreteCreatorclasse implementa il factoryMethod()metodo, che crea direttamente il prodotto.
  4. Questa classe è responsabile della creazione di prodotti specifici. Questa è l'unica classe con informazioni sulla creazione di questi prodotti.
  5. Tutti i prodotti devono implementare un'interfaccia comune, ovvero devono essere discendenti di una classe di prodotto comune. Ciò è necessario affinché le classi che utilizzano i prodotti possano operare su di essi come astrazioni, piuttosto che implementazioni specifiche.

Compiti a casa

Oggi abbiamo svolto parecchio lavoro e studiato il modello di progettazione del metodo di fabbrica. È ora di rinforzare il materiale! Esercizio 1. Svolgi il lavoro per aprire un altro bar. Potrebbe essere una caffetteria in stile inglese o in stile spagnolo. O anche in stile astronave. Aggiungi colorante alimentare al caffè per farlo risplendere e il tuo caffè sarà semplicemente fuori dal mondo! Esercizio 2. Nell'ultima lezione hai svolto un esercizio in cui hai creato un sushi bar virtuale o una pizzeria virtuale. Ora il tuo esercizio è non stare fermo. Oggi hai imparato come usare a tuo vantaggio il modello del metodo di fabbrica. È tempo di utilizzare questa conoscenza ed espandere la propria attività;)
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION