JavaRush /Java Blog /Random-IT /Modelli di progettazione: metodo Factory

Modelli di progettazione: metodo Factory

Pubblicato nel gruppo Random-IT
Ciao! Oggi continueremo a studiare i design pattern e parleremo del metodo di fabbrica (FactoryMethod). Modelli di progettazione: Metodo Factory - 1Scoprirai di cosa si tratta e per quali attività è adatto questo modello. Esamineremo questo modello di progettazione nella pratica ed esploreremo la sua struttura. Per rendere chiaro tutto quanto sopra, è necessario comprendere i seguenti argomenti:
  1. Eredità in Java.
  2. Metodi e classi astratti in Java.

Quale problema risolve il metodo di fabbrica?

In tutti i modelli di progettazione di fabbrica, ci sono due gruppi di partecipanti: creatori (le fabbriche stesse) e prodotti (gli oggetti creati dalle fabbriche). Immagina la situazione: abbiamo una fabbrica che produce automobili con il marchio AutoRush. Sa realizzare modelli di auto con diversi tipi di carrozzeria:
  • berline
  • station wagon
  • coupé
Le cose andavano così bene per noi che un bel giorno abbiamo assorbito la preoccupazione di OneAuto. In quanto manager sensati, non vogliamo perdere i clienti di OneAuto e il nostro compito è ristrutturare la produzione in modo tale da poter produrre:
  • Berline AutoRush
  • Station wagon AutoRush
  • coupé AutoRush
  • Berline OneAuto
  • Station wagon OneAuto
  • Coupé OneAuto
Come puoi vedere, invece di un gruppo di prodotti derivati, ne sono comparsi due, che differiscono in alcuni dettagli. Il design pattern del metodo factory risolve il problema della creazione di diversi gruppi di prodotti, ciascuno con una certa specificità. Considereremo nella pratica il principio 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 te lo ricordi: abbiamo costruito insieme a te una piccola caffetteria virtuale. In esso abbiamo imparato come creare diversi tipi di caffè utilizzando una semplice fabbrica. Oggi perfezioneremo questo esempio. Ricordiamo come appariva la nostra caffetteria con una semplice fabbrica. Abbiamo fatto una lezione sul caffè:
public class Coffee {
    public void grindCoffee(){
        // перемалываем кофе
    }
    public void makeCoffee(){
        // делаем кофе
    }
    public void pourIntoCup(){
        // наливаем в чашку
    }
}
E anche molti dei suoi eredi: tipi specifici di caffè che la nostra fabbrica poteva produrre:
public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Per comodità di accettare gli ordini, abbiamo introdotto i trasferimenti:
public enum CoffeeType {
    ESPRESSO,
    AMERICANO,
    CAFFE_LATTE,
    CAPPUCCINO
}
La stessa fabbrica del caffè assomigliava a questa:
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:
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("Вот ваш кофе! Спасибо, приходите еще!");
        return coffee;
    }
}

Modernizzazione di una semplice fabbrica

La nostra caffetteria sta andando bene. Tanto che stiamo pensando di espanderci. Vogliamo aprire diversi nuovi punti. Da ragazzi intraprendenti, non sforneremo monotoni bar. Voglio che ognuno abbia la sua svolta. Pertanto, per cominciare, apriremo due punti: in stile italiano e americano. Le modifiche riguarderanno non solo gli interni, ma anche le bevande:
  • in una caffetteria italiana utilizzeremo esclusivamente caffè di marche italiane, con macinature e tostature particolari.
  • La porzione americana sarà un po' più grande e con ogni ordine serviremo marshmallow fusi: marshmallow.
L’unica cosa che rimarrà invariata è il nostro modello di business, che si è dimostrato efficace. Se parliamo in linguaggio in codice, questo è ciò che accade. Avevamo 4 classi di prodotti:
public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
E diventano 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 {}
Poiché vogliamo mantenere invariato l’attuale modello di business, vogliamo che il metodo orderCoffee(CoffeeType type)subisca un numero minimo di modifiche. Diamo un'occhiata a questo:
public Coffee orderCoffee(CoffeeType type) {
    Coffee coffee = coffeeFactory.createCoffee(type);
    coffee.grindCoffee();
    coffee.makeCoffee();
    coffee.pourIntoCup();

    System.out.println("Вот ваш кофе! Спасибо, приходите еще!");
    return coffee;
}
Quali opzioni abbiamo? Sappiamo già come scrivere una fabbrica, giusto? La cosa più semplice che mi viene subito in mente è scrivere due fabbriche simili e poi passare l'implementazione richiesta al nostro coffee shop nel costruttore. Quindi la classe della caffetteria non cambierà. Per prima cosa dobbiamo creare una nuova classe factory, ereditarla dalla nostra factory semplice e sovrascrivere la classe createCoffee (CoffeeType type). Scriviamo fabbriche per la creazione di caffè in stile italiano e americano:
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 richiesta a CoffeeShop. Vediamo come sarebbe il codice per ordinare il caffè da diversi bar. Ad esempio, cappuccino in stile italiano e americano:
public class Main {
    public static void main(String[] args) {
        /*
            Закажем капучино в итальянском стиле:
            1. Создадим фабрику для приготовления итальянского кофе
            2. Создадим новую кофейню, передав ей в конструкторе фабрику итальянского кофе
            3. Закажем наш кофе
         */
        SimpleItalianCoffeeFactory italianCoffeeFactory = new SimpleItalianCoffeeFactory();
        CoffeeShop italianCoffeeShop = new CoffeeShop(italianCoffeeFactory);
        italianCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);


         /*
            Закажем капучино в американском стиле
            1. Создадим фабрику для приготовления американского кофе
            2. Создадим новую кофейню, передав ей в конструкторе фабрику американского кофе
            3. Закажем наш кофе
         */
        SimpleAmericanCoffeeFactory americanCoffeeFactory = new SimpleAmericanCoffeeFactory();
        CoffeeShop americanCoffeeShop = new CoffeeShop(americanCoffeeFactory);
        americanCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);
    }
}
Abbiamo creato due diverse caffetterie, trasferendole ciascuna nella fabbrica richiesta. Da un lato abbiamo raggiunto il nostro obiettivo, ma dall'altro... Qualcosa scalfisce l'animo irrefrenabile dell'imprenditore... Scopriamo cosa c'è che non va. Innanzitutto l’abbondanza di fabbriche. È possibile creare ogni volta la propria fabbrica per un nuovo punto e, inoltre, assicurarsi che quando si crea una caffetteria, la fabbrica richiesta venga trasferita al costruttore? In secondo luogo, è pur sempre una fabbrica semplice. Solo un po' modernizzato. Stiamo ancora studiando un nuovo modello qui. In terzo luogo, non è possibile farlo diversamente? Sarebbe bello se potessimo localizzare tutte le domande sulla preparazione del caffè all'interno della classe CoffeeShop, collegando i processi di creazione del caffè e di servizio all'ordine, ma allo stesso tempo mantenendo una flessibilità sufficiente per preparare il caffè in stili diversi. La risposta è sì, puoi. Questo è chiamato modello di progettazione del metodo di fabbrica.

Da una semplice fabbrica ad un metodo di fabbrica

Per risolvere il problema nel modo più efficiente possibile, noi:
  1. Restituiamo il metodo createCoffee(CoffeeType type)alla classe CoffeeShop.
  2. Rendiamo astratto questo metodo.
  3. La classe stessa CoffeeShopdiventerà astratta.
  4. La classe CoffeeShopavrà eredi.
Si Amico. Una caffetteria italiana non è altro che un erede della classe CoffeeShop, implementando un metodo createCoffee(CoffeeType type)nel rispetto delle migliori tradizioni dei baristi italiani. Quindi, in ordine. Passaggio 1. Rendiamo Coffeeastratta la classe. Ora abbiamo due famiglie di prodotti diversi. Le bevande al caffè italiane e americane condividono ancora un antenato comune: il Coffee. Sarebbe corretto renderlo astratto:
public abstract class Coffee {
    public void makeCoffee(){
        // делаем кофе
    }
    public void pourIntoCup(){
        // наливаем в чашку
    }
}
Passaggio 2. Rendilo CoffeeShopastratto, con un metodo astrattocreateCoffee(CoffeeType type)
public abstract class CoffeeShop {

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

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

        System.out.println("Вот ваш кофе! Спасибо, приходите еще!");
        return coffee;
    }

    protected abstract Coffee createCoffee(CoffeeType type);
}
Passaggio 3. Creare una caffetteria italiana, una classe discendente della caffetteria astratta. In esso implementiamo il metodo createCoffee(CoffeeType type)tenendo conto delle specificità 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 una 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. Diamo un'occhiata a come sarebbe ordinare un latte in stile americano e italiano:
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 nella nostra caffetteria.

Come funziona il metodo di fabbrica

Ora diamo uno sguardo più da vicino a ciò che abbiamo ottenuto. Il diagramma seguente mostra le classi risultanti. I blocchi verdi sono classi di creatori, i blocchi blu sono classi di prodotto. Modelli di progettazione: Metodo Factory - 2Quali conclusioni si possono trarre?
  1. Tutti i prodotti sono implementazioni della classe astratta Coffee.
  2. Tutti i creatori sono implementazioni della classe astratta CoffeeShop.
  3. Osserviamo 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 superclasse CoffeeShopnon dispone di informazioni su quale specifica implementazione del prodotto ( Coffee) verrà creata.
  5. Una superclasse CoffeeShopdelega la creazione di un prodotto specifico ai suoi discendenti.
  6. Ogni classe discendente CoffeeShopimplementa un metodo factory createCoffee()in conformità con le sue specifiche. In altre parole, nell'ambito delle implementazioni delle classi creatore, viene presa la decisione di preparare un prodotto specifico in base alle specificità della classe creatore.
Ora sei pronto per definire il modello del metodo factory . Il pattern del metodo factory definisce l'interfaccia per la creazione di un oggetto, ma consente alle sottoclassi di scegliere la classe dell'istanza da creare. Pertanto, il metodo Factory delega l'operazione di istanziazione alle sottoclassi. In generale, ricordare la definizione non è importante quanto capire come funzionano le cose.

Struttura del metodo di fabbrica

Modelli di progettazione: Metodo Factory - 3Il diagramma sopra mostra la struttura generale del modello del metodo factory. Cos'altro è importante qui?
  1. La classe Creator contiene implementazioni di tutti i metodi che interagiscono con i prodotti, ad eccezione del metodo factory.
  2. Un metodo astratto factoryMethod()deve essere implementato da tutti i discendenti della classe Creator.
  3. La classe ConcreteCreatorimplementa un metodo factoryMethod()che produce direttamente un 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: essere discendenti di una classe di prodotto comune. Ciò è necessario affinché le classi che utilizzano i prodotti possano operare su di essi a livello di astrazioni piuttosto che di implementazioni concrete.

Compiti a casa

Quindi, oggi abbiamo lavorato parecchio e studiato il modello di progettazione del metodo di fabbrica. È tempo di consolidare il materiale che hai trattato! Compito 1. Lavora all'apertura di un altro bar. Può essere realizzato in stile inglese o spagnolo. O anche nello stile di un'astronave. Aggiungiamo colorante alimentare al caffè per farlo brillare e, in generale, il caffè sarà solo spazio! Compito 2. Nell'ultima lezione, avevi il compito di creare un sushi bar virtuale o una pizzeria virtuale. Il tuo compito non è restare fermo. Oggi hai imparato come utilizzare il modello del metodo factory per raggiungere il successo. È ora di sfruttare questa conoscenza ed espandere la tua attività ;)
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION