JavaRush /Blog Java /Random-PL /Metoda fabryczna i abstrakcyjne wzorce fabryczne

Metoda fabryczna i abstrakcyjne wzorce fabryczne

Opublikowano w grupie Random-PL
W książce „Najpierw głowa. Wzorce projektowe” definiuje te wzorce w następujący sposób: Wzorzec Metoda fabryczna definiuje interfejs do tworzenia obiektu, ale pozwala podklasom wybrać klasę instancji, która ma zostać utworzona. W związku z tym metoda Factory deleguje operację tworzenia instancji do podklas. Wzorzec Fabryki Abstrakcyjnej zapewnia interfejs do tworzenia rodzin powiązanych ze sobą lub współzależnych obiektów bez określania ich konkretnych klas. Spróbujmy zrozumieć to bardziej szczegółowo. Załóżmy, że decydujesz się napisać grę o ludziach, którzy postanawiają zostać... (potrzebujesz tu czegoś oryginalnego i niezwykłego) mnichami. Moglibyśmy zacząć od następujących rzeczy. 1) Utwórz klasę Monk i klasy podrzędne (najpierw utwórzmy jedną):
public abstract class Monk {

    public abstract void description();
}
public class OrthodoxMonk extends Monk {
    @Override
    public void description() {
        System.out.println("Я православный монах");
    }
}
2) I oczywiście utwórz klasę Klasztor, w której będziesz mógł realizować „śluby monastyczne”:
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;
    }
}
Cóż, sprawdźmy wynik:
public class Main {
    public static void main(String[] args) {
        Monastery monastery = new Monastery();
        monastery.createMonk("ORTODOX");
        monastery.getMonk().description();
    }
}
Я православный монах
Teraz, jeśli chcesz stworzyć... mnicha katolickiego, będziesz musiał: A) Utworzyć nową klasę dla mnicha katolickiego:
public class CatholicMonk extends Monk {
    @Override
    public void description() {
        System.out.println("Я католический монах");
    }
}
B) Dokonaj zmian w klasie klasztoru:
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;
    }
}
więc za każdym razem, gdy wprowadzane są nowe typy mnichów, będziesz musiał stworzyć nową klasę i edytować istniejącą. Co w tym przypadku można zrobić, żeby w jakiś sposób „osłonić” naszą klasę klasztorną przed zmianami. Możesz spróbować użyć wzorca metody fabrycznej. Jak to będzie wyglądać A) Zostawmy klasę mnichów bez zmian, może z wyjątkiem dodania mnicha anglikańskiego (monastycyzm wyznają nie tylko katolicy i prawosławni chrześcijanie):
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) Zmieńmy klasę klasztoru w następujący sposób (uczyńmy ją i jej metodę abstrakcyjną). Tutaj po prostu używamy metody Factory:
public abstract class Monastery {
    protected abstract Monk createMonk();
}
i utwórz klasy potomne z implementacją metody:
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) Sprawdźmy kod
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();
    }
}
Я православный монах
Я католический монах
Я англиканский монах
Te. jak widzimy teraz, dodając nowe typy mnichów, nie będzie potrzeby zmieniać istniejących klas, a jedynie w razie potrzeby dodawać nowe (klasę konkretnego klasztoru i mnicha). Być może ktoś już zauważył, że metoda opisu, która od samego początku była w klasie Monka, również była Factory :) Definicja metody fabrycznej mówiła, że ​​nasz wzór definiuje interfejs do tworzenia obiektu, ale my nie stworzyliśmy żadnego interfejsy, chociaż moglibyśmy stworzyć klasę Monastery jako interfejs i zaimplementować ją w konkretnych implementacjach. Odnosi się to do słowa „interfejs” w szerszym znaczeniu. Definicja mówi również, że pozwala podklasom wybrać klasę tworzonej instancji . Tutaj widzimy po prostu, że podklasy (klasy podrzędne) implementują tę metodę (to znaczy, że uprawnienia do tworzenia obiektów mnichów są im delegowane). Teraz rozszerzmy trochę nasz program, wprowadźmy możliwość, że w tym czy innym wyznaniu są różni mnisi. Na przykład w prawosławiu, opierając się na stanowisku Cerkwi prawosławnej w sprawie klasztorów i monastyków (przyjętym na Soborze Biskupów Rosyjskiej Cerkwi Prawosławnej w dniach 29 listopada - 2 grudnia 2017 r.), możemy stwierdzić, że istnieją 2 rodzaje mnichów : - Mniejszy schemat (płaszcz). - Schemat (świetny schemat). Są też „etapy przygotowawcze”, ale za mnichów (Trudnik, Nowicjusz i Ryasofor czyli Mnich) nie uważa się ludzi, bo nie składają ślubów zakonnych. Dlatego nie bierzemy ich pod uwagę. Co otrzymujemy w tym przypadku: A) Klasę Klasztorną (w uproszczeniu skupmy się na razie na monastycyzmie prawosławnym) metodą Fabryczną :
public abstract class Monastery {
    protected abstract Monk createMonk(String type);
}
i konkretny klasztor
public class OrthodoxMonastery extends Monastery {

    @Override
    protected Monk createMonk(String type) {
        return new OrthodoxMonk(type);
    }
}
B) Naprawmy klasę mnicha:
public abstract class Monk {
    String kind;

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

    public abstract void description();
}
i klasa dziecka:
public class OrthodoxMonk extends Monk {
    public OrthodoxMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я православный монах - " + kind);
    }
}
C) Sprawdźmy nasz kod:
public class Main {
    public static void main(String[] args) {
        Monastery monastery = new OrthodoxMonastery();
        monastery.createMonk("Мантийный монах").description();
        monastery.createMonk("Великосхимник").description();
    }
}
Я православный монах - Мантийный монах
Я православный монах — Великосхимник
Tym samym stosując wzorzec Factory Method osiągnęliśmy to, że nie musimy zmieniać wcześniej napisanych klas, ale także przy rozwijaniu obrazów (typów) mnichów wymagane jest minimum zmian w kodzie. Sprawdźmy i dodajmy wszystkie zakony i zgromadzenia mnichów katolickich :) Ale lepiej skupić się na 3 najsłynniejszych, bo jest ich ponad 100: 1) Benedyktyni 2) Jezuici 3) Franciszkanie Aby to zrobić, jak poprzednio z mnicha prawosławnego, musimy wdrożyć konkretną klasę mnicha katolickiego:
public class CatholicMonk extends Monk {
    public CatholicMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я католический монах - " + kind);
    }
}
i klasa klasztorna:
public class CatholicMonastery extends Monastery {

    @Override
    protected Monk createMonk(String type) {
        return new CatholicMonk(type);
    }
}
i sprawdź kod:
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();
    }
}
Я православный монах - Мантийный монах
Я православный монах - Великосхимник
Я католический монах - Бенедиктинец
Я католический монах - Иезуит
Я католический монах - Францисканец
Skończmy z tym wzorem. Wszystkie te typy mnichów można również wcześniej dodać do klasy E-num, ale dla uproszczenia kodu obejdziemy się bez tego. Czas na wzór Abstract Factory. Mamy mnichów, teraz moglibyśmy uszyć im ubrania, różańce itp. Zacznijmy od odzieży, czyli jeśli wrócimy do naszej definicji na początku, odzież stanie się rodziną powiązanych ze sobą lub współzależnych przedmiotów . Zacznijmy od problemu, że każdy typ mnicha ma inne szaty. Jeśli dodamy też buddyjskie, to będą one zupełnie inne :) W tym celu możemy stworzyć interfejs fabryczny, którego implementacje stworzyłyby niezbędne ubrania. Dlatego A) Tworzymy fabrykę do produkcji ubrań
public interface MonkFactory {
    Clothing createClothing();
}
i jego wdrożenie
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();
    }
}
No cóż, nie zapominajmy o mnichach buddyjskich :)
public class BuddhistMonkFactory implements MonkFactory {

    @Override
    public Clothing createClothing() {
        return new BuddhistClothing();
    }
}
B) Utwórz klasę ubioru (dla uproszczenia weźmy kluczowy element ubioru mnichów, nie będziemy się wdawać w szczegóły):
public abstract class Clothing {
    private String name;

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

    public String getName() {
        return name;
    }
}
i zajęcia dla dzieci
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) Następnie zmieniamy klasy mnichów tak, aby mieli ubrania:
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) Klasa klasztoru zawiera naszą metodę 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);
}
nasze wdrożenia się nie zmieniły
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) Sprawdź wynik:
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();
    }
}
Я православный монах - Мантийный монах
Моя одежда - Мантия
Я католический монах - Иезуит
Моя одежда - Ряса с капюшоном
Я англиканский монах - Бенедиктинец
Моя одежда - Ряса
Я буддийский монах - Монах
Моя одежда - Кашая
Fabryka produkująca ubrania zaczęła dobrze działać. Teraz możesz dodać do fabryki produkcję sprzętu dla mnichów do skutecznej modlitwy (różańce itp.). Pozostaje jednak pytanie, czy możliwe jest użycie 2 wzorów jednocześnie? Oczywiście, że możesz :) Spróbujmy stworzyć ostateczną wersję naszego projektu i dodać hinduskiego mnicha: A) Fabryki tworzą teraz mnichów, co brzmi jak „fabryka gwiazd” :
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) Klasa klasztorna + konkretne implementacje klasy klasztornej nie są potrzebne, są one wdrażane przez fabrykę (wręcz przeciwnie, moglibyśmy je opuścić i usunąć fabryki, ale w zasadzie byłyby wtedy po prostu zamiast fabryk, tylko w w tym przypadku klasztor musiałby być interfejsem, a nie klasą abstrakcyjną). I dodaj klasę aplikacji:
public class Application {

    public Monk create(MonkFactory monkFactory, String type) {
        Monk monk = monkFactory.createMonk(type);
        monk.prepare(monkFactory);
        return monk;
    }
}
B) Mnisi teraz zawierają
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);
}
zawiera w implementacjach metodę fabryczną, która jest implementowana przy użyciu fabryki:
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) I sprawdźmy:
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();
    }
}
Я православный монах - Мантийный монах
Моя одежда - Мантия
Я католический монах - Иезуит
Моя одежда - Ряса с капюшоном
Я англиканский монах - Бенедиктинец
Моя одежда - Ряса
Я буддийский монах - Монах
Моя одежда - Кашая
Я индуистский монах - Саньяси
Моя одежда - Почти ничего, тепло же :)
Podsumowując, można zauważyć, że metoda Factory wykorzystywała klasę abstrakcyjną z niezaimplementowaną metodą, która została zaimplementowana w podklasach, zaś Fabryka Abstrakcyjna korzystała z interfejsu, gdzie implementacja (w naszym przypadku utworzenie mnicha) nastąpiła w klasach, które zaimplementowały ten interfejs.
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION