JavaRush /Java Blog /Random-IT /Metodo di fabbrica e modelli astratti di fabbrica

Metodo di fabbrica e modelli astratti di fabbrica

Pubblicato nel gruppo Random-IT
Nel libro “A testa in giù. Design Patterns” definisce questi pattern come segue: Il pattern Factory Method 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. Il modello Abstract Factory fornisce un'interfaccia per creare famiglie di oggetti correlati o interdipendenti senza specificarne le classi concrete. Proviamo a capirlo più in dettaglio. Diciamo che decidi di scrivere un gioco su persone che decidono di diventare... (hai bisogno di qualcosa di originale e insolito qui) monaci. Potremmo iniziare con quanto segue. 1) Crea una classe Monaco e classi figlie (creiamone prima una):

public abstract class Monk {

    public abstract void description();
}

public class OrthodoxMonk extends Monk {
    @Override
    public void description() {
        System.out.println("Я православный монах");
    }
}
2) E, naturalmente, crea una classe del Monastero, in cui puoi implementare i “voti monastici”:

public class Monastery {
    private Monk monk;

    public void createMonk(String typeName) {
        this.monk = switch (typeName) {
            case "ORTODOX" -> new OrthodoxMonk();
            default -> null;
        };
    }

    public Monk getMonk() {
        return monk;
    }
}
Bene, controlliamo il risultato:

public class Main {
    public static void main(String[] args) {
        Monastery monastery = new Monastery();
        monastery.createMonk("ORTODOX");
        monastery.getMonk().description();
    }
}

Я православный монах
Ora se devi creare... un monaco cattolico, dovrai A) Creare una nuova classe per un monaco cattolico:

public class CatholicMonk extends Monk {
    @Override
    public void description() {
        System.out.println("Я католический монах");
    }
}
B) Apportare modifiche alla classe del monastero:

public class Monastery {
    private Monk monk;

    public void createMonk(String typeName) {
        this.monk = switch (typeName) {
            case "ORTODOX" -> new OrthodoxMonk();
            case "CATHOLIC" -> new CatholicMonk();
            default -> null;
        };
    }

    public Monk getMonk() {
        return monk;
    }
}
e quindi ogni volta che verranno introdotte nuove tipologie di monaci, bisognerà creare una nuova classe e modificare quella esistente. Cosa si può fare in questo caso per “proteggere” in qualche modo la nostra classe monastica dai cambiamenti. Puoi provare a utilizzare il modello del metodo di fabbrica. Come apparirà A) Lasciamo la classe dei monaci così com'è, tranne forse aggiungere un monaco anglicano (non solo i cattolici e i cristiani ortodossi hanno il monachesimo):

public abstract class Monk {

    public abstract void description();
}

public class OrthodoxMonk extends Monk {
    @Override
    public void description() {
        System.out.println("Я православный монах");
    }
}

public class CatholicMonk extends Monk {
    @Override
    public void description() {
        System.out.println("Я католический монах");
    }
}

public class AnglicanMonk extends Monk {
    @Override
    public void description() {
        System.out.println("Я англиканский монах");
    }
}
B) Modifichiamo la classe del monastero come segue (rendiamola astratta e il suo metodo). Qui usiamo semplicemente il metodo Factory:

public abstract class Monastery {
    protected abstract Monk createMonk();
}
e creare classi figlie con l'implementazione del metodo:

public class OrthodoxMonastery extends Monastery {
    @Override
    protected Monk createMonk() {
        return new OrthodoxMonk();
    }
}

public class CatholicMonastery extends Monastery {
    @Override
    protected Monk createMonk() {
        return new CatholicMonk();
    }
}

public class AnglicanMonastery extends Monastery {
    @Override
    protected Monk createMonk() {
        return new AnglicanMonk();
    }
}
B) Controlliamo il codice

public class Main {
    public static void main(String[] args) {
        Monastery monastery;

        monastery = new OrthodoxMonastery();
        monastery.createMonk().description();

        monastery = new CatholicMonastery();
        monastery.createMonk().description();

        monastery = new AnglicanMonastery();
        monastery.createMonk().description();
    }
}

Я православный монах
Я католический монах
Я англиканский монах
Quelli. come vediamo ora, quando si aggiungono nuovi tipi di monaci, non sarà necessario modificare le classi esistenti, ma solo se necessario aggiungerne di nuove (la classe di un monastero e monaco specifico). Forse qualcuno ha già notato che anche il metodo description, presente fin dall'inizio nella classe Monk, era Factory :) La definizione del metodo factory diceva che il nostro pattern definisce l'interfaccia per la creazione di un oggetto, ma noi non ne abbiamo creato alcuno interfacce, anche se potremmo creare la classe Monastero come interfaccia e implementarla in implementazioni specifiche. Questo si riferisce alla parola “interfaccia” in un senso più ampio. La definizione dice anche che permette alle sottoclassi di scegliere la classe dell'istanza che creano . Qui vediamo solo che le sottoclassi (classi figlie) implementano questo metodo (ovvero, questi poteri per creare oggetti monaco sono loro delegati). Ora espandiamo un po' il nostro programma, introduciamo la possibilità che ci siano diversi monaci in una denominazione o nell'altra. Ad esempio, nell'Ortodossia, sulla base della posizione della Chiesa ortodossa sui monasteri e sui monaci (adottata dal Consiglio dei vescovi della Chiesa ortodossa russa dal 29 novembre al 2 dicembre 2017), possiamo concludere che esistono 2 tipi di monaci : - Schema minore (mantello). - Schema (ottimo schema). Esistono anche “fasi preparatorie”, ma le persone non sono considerate monaci (Trudnik, Novizio e Ryasophor o Monaco), perché non prendono i voti monastici. Pertanto non li prendiamo in considerazione. Cosa otteniamo in questo caso: A) Classe in Monastero (per semplificare, concentriamoci per ora sul monachesimo ortodosso) con il metodo Factory :

public abstract class Monastery {
    protected abstract Monk createMonk(String type);
}
e un monastero specifico

public class OrthodoxMonastery extends Monastery {

    @Override
    protected Monk createMonk(String type) {
        return new OrthodoxMonk(type);
    }
}
B) Sistemiamo la classe del monaco:

public abstract class Monk {
    String kind;

    public Monk(String kind) {
        this.kind = kind;
    }

    public abstract void description();
}
e classe bambini:

public class OrthodoxMonk extends Monk {
    public OrthodoxMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я православный монах - " + kind);
    }
}
C) Controlliamo il nostro codice:

public class Main {
    public static void main(String[] args) {
        Monastery monastery = new OrthodoxMonastery();
        monastery.createMonk("Мантийный монах").description();
        monastery.createMonk("Великосхимник").description();
    }
}

Я православный монах - Мантийный монах
Я православный монах — Великосхимник
Pertanto, utilizzando il modello Factory Method, abbiamo ottenuto che non è necessario modificare le classi scritte in precedenza, ma anche quando si espandono le immagini (tipi) dei monaci, sono necessarie modifiche minime al codice. Controlliamo e aggiungiamo tutti gli ordini e le congregazioni dei monaci cattolici :) Ma è meglio concentrarsi sui 3 più famosi, perché ce ne sono più di 100: 1) Benedettino 2) Gesuita 3) Francescano Per fare questo, come prima con il monaco ortodosso, occorre implementare una classe specifica di monaco cattolico:

public class CatholicMonk extends Monk {
    public CatholicMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я католический монах - " + kind);
    }
}
e lezione del monastero:

public class CatholicMonastery extends Monastery {

    @Override
    protected Monk createMonk(String type) {
        return new CatholicMonk(type);
    }
}
e controlla il codice:

public class Main {
    public static void main(String[] args) {
        Monastery monastery;

        monastery = new OrthodoxMonastery();
        monastery.createMonk("Мантийный монах").description();
        monastery.createMonk("Великосхимник").description();

        monastery = new CatholicMonastery();
        monastery.createMonk("Бенедиктинец").description();
        monastery.createMonk("Иезуит").description();
        monastery.createMonk("Францисканец").description();
    }
}

Я православный монах - Мантийный монах
Я православный монах - Великосхимник
Я католический монах - Бенедиктинец
Я католический монах - Иезуит
Я католический монах - Францисканец
Concludiamo con questo modello. Tutte queste tipologie di monaci potrebbero anche essere aggiunte a priori alla classe E-num, ma per semplificare il codice ne faremo a meno. È il momento del modello Abstract Factory. Abbiamo dei monaci, ora potremmo fargli vestiti, rosari, ecc. Cominciamo dall'abbigliamento, cioè se torniamo alla nostra definizione iniziale, l'abbigliamento diventerà una famiglia di oggetti interconnessi o interdipendenti . Cominciamo con il problema che ogni tipo di monaco ha vesti diverse. Se aggiungiamo anche buddista, saranno completamente diversi :) Per fare ciò, possiamo creare un'interfaccia di fabbrica, le cui implementazioni creerebbero gli abiti necessari. Quindi A) Creiamo una fabbrica per realizzare vestiti

public interface MonkFactory {
    Clothing createClothing();
}
e la sua implementazione

public class OrthodoxMonkFactory implements MonkFactory {

        @Override
    public Clothing createClothing() {
        return new OrtodoxClothing();
    }
}

public class CatholicMonkFactory implements MonkFactory {

    @Override
    public Clothing createClothing() {
        return new CatholicClothing();
    }
}

public class AnglicanMonkFactory implements MonkFactory {

    @Override
    public Clothing createClothing() {
        return new AnglicanClothing();
    }
}
Bene, non dimentichiamoci dei monaci buddisti :)

public class BuddhistMonkFactory implements MonkFactory {

    @Override
    public Clothing createClothing() {
        return new BuddhistClothing();
    }
}
B) Creare un corso di abbigliamento (per semplificare prendiamo l’elemento chiave dell’abbigliamento dei monaci, non entreremo nei dettagli):

public abstract class Clothing {
    private String name;

    public Clothing(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}
e classi per bambini

public class OrtodoxClothing extends Clothing {
    public OrtodoxClothing() {
        super("Мантия");
    }
}

public class CatholicClothing extends Clothing {
    public CatholicClothing() {
        super("Ряса с капюшоном");
    }
}

public class AnglicanClothing extends Clothing {
    public AnglicanClothing() {
        super("Ряса");
    }
}

public class BuddhistClothing extends Clothing {
    public BuddhistClothing() {
        super("Кашая");
    }
}
C) Successivamente, cambiamo le classi dei monaci in modo che abbiano vestiti:

public abstract class Monk {
    String kind;
    Clothing clothing;

    public Monk(String kind) {
        this.kind = kind;
    }

    public void setClothing(Clothing clothing) {
        this.clothing = clothing;
    }

    public abstract void description();
}

public class OrthodoxMonk extends Monk {
    public OrthodoxMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я православный монах - " + kind);
        System.out.println("Моя одежда - " + clothing.getName());
    }
}

public class CatholicMonk extends Monk {
    public CatholicMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я католический монах - " + kind);
        System.out.println("Моя одежда - " + clothing.getName());
    }
}

public class AnglicanMonk extends Monk {
    public AnglicanMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я англиканский монах - " + kind);
        System.out.println("Моя одежда - " + clothing.getName());
    }
}

public class BuddhistMonk extends Monk {
    public BuddhistMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я буддийский монах - " + kind);
        System.out.println("Моя одежда - " + clothing.getName());
    }
}
D) La classe del monastero contiene il nostro metodo Factory

public abstract class Monastery {

    public Monk create(MonkFactory monkFactory, String type) {
        Monk monk = createMonk(type);
        monk.setClothing(monkFactory.createClothing());
        return monk;
    }

    protected abstract Monk createMonk(String type);
}
le nostre implementazioni non sono cambiate

public class OrthodoxMonastery extends Monastery {

    @Override
    protected Monk createMonk(String type) {
        return new OrthodoxMonk(type);
    }
}

public class CatholicMonastery extends Monastery {

    @Override
    protected Monk createMonk(String type) {
        return new CatholicMonk(type);
    }
}

public class AnglicanMonastery extends Monastery {

    @Override
    protected Monk createMonk(String type) {
        return new AnglicanMonk(type);
    }
}

public class BuddhistMonastery extends Monastery {

    @Override
    protected Monk createMonk(String type) {
        return new BuddhistMonk(type);
    }
}
D) Controlla il risultato:

public class Main {
    public static void main(String[] args) {
        Monastery monastery;

        monastery = new OrthodoxMonastery();
        monastery.create(new OrthodoxMonkFactory(), "Мантийный монах").description();

        monastery = new CatholicMonastery();
        monastery.create(new CatholicMonkFactory(), "Иезуит").description();

        monastery = new AnglicanMonastery();
        monastery.create(new AnglicanMonkFactory(), "Бенедиктинец").description();

        monastery = new BuddhistMonastery();
        monastery.create(new BuddhistMonkFactory(), "Монах").description();
    }
}

Я православный монах - Мантийный монах
Моя одежда - Мантия
Я католический монах - Иезуит
Моя одежда - Ряса с капюшоном
Я англиканский монах - Бенедиктинец
Моя одежда - Ряса
Я буддийский монах - Монах
Моя одежда - Кашая
La fabbrica che crea vestiti ha iniziato a funzionare bene. Ora puoi aggiungere alla fabbrica la produzione di attrezzature per monaci per la preghiera di successo (rosari, ecc.). Ma la domanda rimane ancora: è possibile usare 2 modelli insieme? Certo che puoi :) Proviamo a realizzare la versione finale del nostro progetto e ad aggiungere un monaco indù: A) Le fabbriche ora creano suoni dei monaci come una "fabbrica di stelle" :

public interface MonkFactory {
    Monk createMonk(String type);
    Clothing createClothing();
}

public class OrthodoxMonkFactory implements MonkFactory {

    @Override
    public Monk createMonk(String type){
        return new OrthodoxMonk(type);
    }

    @Override
    public Clothing createClothing() {
        return new OrtodoxClothing();
    }
}

public class CatholicMonkFactory implements MonkFactory {

    @Override
    public Monk createMonk(String type){
        return new CatholicMonk(type);
    }

    @Override
    public Clothing createClothing() {
        return new CatholicClothing();
    }
}

public class AnglicanMonkFactory implements MonkFactory {

    @Override
    public Monk createMonk(String type){
        return new AnglicanMonk(type);
    }

    @Override
    public Clothing createClothing() {
        return new AnglicanClothing();
    }
}

public class BuddhistMonkFactory implements MonkFactory {

    @Override
    public Monk createMonk(String type){
        return new BuddhistMonk(type);
    }

    @Override
    public Clothing createClothing() {
        return new BuddhistClothing();
    }
}

public class HinduMonkFactory implements MonkFactory {

    @Override
    public Monk createMonk(String type){
        return new HinduMonk(type);
    }

    @Override
    public Clothing createClothing() {
        return new HinduClothing();
    }
}
B) La classe monastero + le implementazioni concrete della classe Monastero non servono, vengono implementate dalla fabbrica (anzi potremmo lasciarle e togliere le fabbriche, ma in sostanza poi sarebbero semplicemente al posto delle fabbriche, solo in in questo caso il Monastero dovrebbe essere reso un'interfaccia e non una classe astratta). E aggiungi la classe dell'applicazione:

public class Application {

    public Monk create(MonkFactory monkFactory, String type) {
        Monk monk = monkFactory.createMonk(type);
        monk.prepare(monkFactory);
        return monk;
    }
}
B) I monaci ora contengono

public abstract class Monk {
    String kind;
    Clothing clothing;

    public Monk(String kind) {
        this.kind = kind;
    }

    public void setClothing(Clothing clothing) {
        this.clothing = clothing;
    }

    public abstract void description();

    public abstract void prepare(MonkFactory monkFactory);
}
contiene un metodo factory nelle implementazioni, che viene implementato utilizzando una factory:

public class OrthodoxMonk extends Monk {

    public OrthodoxMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я православный монах - " + kind);
        System.out.println("Моя одежда - " + clothing.getName());
    }

    @Override
    public void prepare(MonkFactory monkFactory) {
        setClothing(monkFactory.createClothing());
    }
}

public class CatholicMonk extends Monk {
    public CatholicMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я католический монах - " + kind);
        System.out.println("Моя одежда - " + clothing.getName());
    }

    @Override
    public void prepare(MonkFactory monkFactory) {
        setClothing(monkFactory.createClothing());
    }
}

public class AnglicanMonk extends Monk {
    public AnglicanMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я англиканский монах - " + kind);
        System.out.println("Моя одежда - " + clothing.getName());
    }

    @Override
    public void prepare(MonkFactory monkFactory) {
        setClothing(monkFactory.createClothing());
    }
}

public class BuddhistMonk extends Monk {
    public BuddhistMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я буддийский монах - " + kind);
        System.out.println("Моя одежда - " + clothing.getName());
    }

    @Override
    public void prepare(MonkFactory monkFactory) {
        setClothing(monkFactory.createClothing());
    }
}

public class HinduMonk extends Monk {
    public HinduMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я индуистский монах - " + kind);
        System.out.println("Моя одежда - " + clothing.getName());
    }

    @Override
    public void prepare(MonkFactory monkFactory) {
        setClothing(monkFactory.createClothing());
    }
}
D) E controlliamo:

public class Main {
    public static void main(String[] args) {
        Application application = new Application();

        application.create(new OrthodoxMonkFactory(), "Мантийный монах").description();
        application.create(new CatholicMonkFactory(), "Иезуит").description();
        application.create(new AnglicanMonkFactory(), "Бенедиктинец").description();
        application.create(new BuddhistMonkFactory(), "Монах").description();
        application.create(new HinduMonkFactory(), "Саньяси").description();
    }
}

Я православный монах - Мантийный монах
Моя одежда - Мантия
Я католический монах - Иезуит
Моя одежда - Ряса с капюшоном
Я англиканский монах - Бенедиктинец
Моя одежда - Ряса
Я буддийский монах - Монах
Моя одежда - Кашая
Я индуистский монах - Саньяси
Моя одежда - Почти ничего, тепло же :)
In conclusione, puoi notare che il metodo Factory utilizzava una classe astratta con un metodo non implementato, che era implementato in sottoclassi, e la Factory astratta utilizzava un'interfaccia, dove l'implementazione (nel nostro caso, la creazione di un monaco) avveniva in classi che implementavano questa interfaccia.
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION