JavaRush /בלוג Java /Random-HE /שיטת המפעל ותבניות מפעל מופשטות

שיטת המפעל ותבניות מפעל מופשטות

פורסם בקבוצה
בספר "ראש ראשון. Design Patterns" מגדיר תבניות אלו באופן הבא: תבנית שיטת המפעל מגדירה את הממשק ליצירת אובייקט, אך מאפשרת לתתי מחלקות לבחור את המחלקה של המופע שייווצר. לפיכך, שיטת Factory מאצילה את פעולת המופע לתת-מחלקות. דפוס המפעל המופשט מספק ממשק ליצירת משפחות של אובייקטים הקשורים זה לזה או תלויים זה בזה מבלי לציין את המחלקות הקונקרטיות שלהם. בואו ננסה להבין זאת ביתר פירוט. נניח שהחלטתם לכתוב משחק על אנשים שמחליטים להיות... (צריך כאן משהו מקורי ויוצא דופן) נזירים. נוכל להתחיל עם הדברים הבאים. 1) צור כיתת נזיר וכיתות ילדים (בואו ניצור אחת תחילה):
public abstract class Monk {

    public abstract void description();
}
public class OrthodoxMonk extends Monk {
    @Override
    public void description() {
        System.out.println("Я православный монах");
    }
}
2) וכמובן, צור כיתת מנזר, שבה אתה יכול ליישם "נדרים נזיריים":
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;
    }
}
ובכן, בואו נבדוק את התוצאה:
public class Main {
    public static void main(String[] args) {
        Monastery monastery = new Monastery();
        monastery.createMonk("ORTODOX");
        monastery.getMonk().description();
    }
}
Я православный монах
עכשיו אם אתה צריך ליצור... נזיר קתולי, תצטרך א) ליצור מחלקה חדשה לנזיר קתולי:
public class CatholicMonk extends Monk {
    @Override
    public void description() {
        System.out.println("Я католический монах");
    }
}
ב) בצע שינויים במעמד המנזר:
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;
    }
}
ולכן בכל פעם שמציגים סוגים חדשים של נזירים, תצטרך ליצור מחלקה חדשה ולערוך את הקיימת. מה אפשר לעשות במקרה הזה כדי איכשהו "להכיל" את מעמד המנזר שלנו משינויים. אתה יכול לנסות להשתמש בתבנית שיטת המפעל. איך זה יראה א) בואו נשאיר את מעמד הנזירים כפי שהוא, חוץ מלהוסיף אולי נזיר אנגליקני (לא רק לקתולים ונוצרים אורתודוקסים יש נזירות):
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("Я англиканский монах");
    }
}
ב) בואו נשנה את מעמד המנזר באופן הבא (בואו נעשה את זה ואת השיטה שלו למופשטים). כאן אנו פשוט משתמשים בשיטת המפעל:
public abstract class Monastery {
    protected abstract Monk createMonk();
}
וליצור כיתות ילדים עם יישום שיטה:
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();
    }
}
ב) בוא נבדוק את הקוד
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();
    }
}
Я православный монах
Я католический монах
Я англиканский монах
הָהֵן. כפי שאנו רואים כעת, כאשר מוסיפים סוגים חדשים של נזירים, לא יהיה צורך לשנות כיתות קיימות, אלא רק במידת הצורך, להוסיף חדשים (המעמד של מנזר ונזיר ספציפיים). אולי מישהו כבר שם לב ששיטת התיאור, שהייתה מההתחלה במחלקת Monk, הייתה גם Factory :) ההגדרה של שיטת המפעל אמרה שהתבנית שלנו מגדירה את הממשק ליצירת אובייקט, אבל לא יצרנו שום ממשקים, למרות שנוכל ליצור את מחלקת המנזר כממשק וליישם אותה ביישומים ספציפיים. הכוונה היא למילה "ממשק" במובן הרחב יותר. ההגדרה גם אמרה שהיא מאפשרת לתת-מחלקות לבחור את המחלקה של המופע שהם יוצרים . כאן אנו רק רואים שתת-מחלקות (מחלקות ילד) מיישמות את השיטה הזו (כלומר, הסמכויות הללו ליצור אובייקטים נזירים מואצלים להן). עכשיו בואו נרחיב מעט את התוכנית שלנו, נציג את האפשרות שישנם נזירים שונים בעדה כזו או אחרת. לדוגמה, באורתודוקסיה, בהתבסס על עמדת הכנסייה האורתודוקסית בנושא מנזרים ומנזרים (שאומצו במועצת הבישופים של הכנסייה הרוסית האורתודוקסית ב-29 בנובמבר - 2 בדצמבר 2017), אנו יכולים להסיק שישנם 2 סוגים של נזירים : - סכימה פחותה (מעטפת). - סכימה (סכימה מצוינת). ישנם גם "שלבי הכנה", אבל אנשים אינם נחשבים לנזירים (טרודניק, טירון וריאסופור או נזיר), כי הם אינם נודרים נדרים נזיריים. לכן, אנחנו לא לוקחים אותם בחשבון. מה נקבל במקרה הזה: א) כיתת מנזרים (כדי לפשט, בואו נתמקד בנזירות אורתודוקסית לעת עתה) בשיטת המפעל :
public abstract class Monastery {
    protected abstract Monk createMonk(String type);
}
ומנזר ספציפי
public class OrthodoxMonastery extends Monastery {

    @Override
    protected Monk createMonk(String type) {
        return new OrthodoxMonk(type);
    }
}
ב) בואו נתקן את כיתת הנזירים:
public abstract class Monk {
    String kind;

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

    public abstract void description();
}
וכיתת ילדים:
public class OrthodoxMonk extends Monk {
    public OrthodoxMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я православный монах - " + kind);
    }
}
ג) בואו נבדוק את הקוד שלנו:
public class Main {
    public static void main(String[] args) {
        Monastery monastery = new OrthodoxMonastery();
        monastery.createMonk("Мантийный монах").description();
        monastery.createMonk("Великосхимник").description();
    }
}
Я православный монах - Мантийный монах
Я православный монах — Великосхимник
לפיכך, על ידי שימוש בתבנית שיטת המפעל, השגנו שלא נצטרך לשנות מחלקות שנכתבו בעבר, אך כאשר מרחיבים את התמונות (הסוגים) של נזירים, נדרש מינימום של שינויים בקוד. בואו נבדוק ונוסיף את כל המסדרים והקהילות של נזירים קתולים :) אבל עדיף להתמקד ב-3 המפורסמים ביותר, כי יש יותר מ-100 מהם: 1) בנדיקטיני 2) ישועי 3) פרנציסקני לעשות את זה, כמו קודם עם הנזיר האורתודוקסי, עלינו ליישם מעמד ספציפי של נזיר קתולי:
public class CatholicMonk extends Monk {
    public CatholicMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я католический монах - " + kind);
    }
}
וכיתת מנזר:
public class CatholicMonastery extends Monastery {

    @Override
    protected Monk createMonk(String type) {
        return new CatholicMonk(type);
    }
}
ותבדוק את הקוד:
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();
    }
}
Я православный монах - Мантийный монах
Я православный монах - Великосхимник
Я католический монах - Бенедиктинец
Я католический монах - Иезуит
Я католический монах - Францисканец
בואו נסיים עם הדפוס הזה. ניתן היה להוסיף מראש את כל סוגי הנזירים הללו למחלקה E-num, אבל כדי לפשט את הקוד נסתדר בלעדיו. הגיע הזמן לדפוס המפעל המופשט. יש לנו נזירים, עכשיו נוכל להכין להם בגדים, מחרוזות וכו'. נתחיל בלבוש, כלומר, אם נחזור להגדרה שלנו בהתחלה, הלבוש יהפוך למשפחה של אובייקטים מחוברים או תלויים זה בזה . נתחיל מהבעיה שלכל סוג של נזיר יש גלימות שונות. אם נוסיף בודהיסטיות, אז הם יהיו שונים לחלוטין :) לשם כך, נוכל ליצור ממשק מפעל, שהיישום שלו ייצור את הבגדים הדרושים. לכן א) אנו יוצרים מפעל לייצור בגדים
public interface MonkFactory {
    Clothing createClothing();
}
ויישומה
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();
    }
}
ובכן, בואו לא נשכח את הנזירים הבודהיסטים :)
public class BuddhistMonkFactory implements MonkFactory {

    @Override
    public Clothing createClothing() {
        return new BuddhistClothing();
    }
}
ב) צור כיתת בגדים (כדי לפשט, ניקח את המרכיב המרכזי של בגדי נזירים, לא נפרט):
public abstract class Clothing {
    private String name;

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

    public String getName() {
        return name;
    }
}
וחוגי ילדים
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("Кашая");
    }
}
ג) לאחר מכן, אנו משנים את כיתות הנזירים כך שיהיו להם בגדים:
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());
    }
}
ד) כיתת המנזר מכילה את שיטת המפעל שלנו
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);
}
היישום שלנו לא השתנה
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);
    }
}
ד) בדוק את התוצאה:
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();
    }
}
Я православный монах - Мантийный монах
Моя одежда - Мантия
Я католический монах - Иезуит
Моя одежда - Ряса с капюшоном
Я англиканский монах - Бенедиктинец
Моя одежда - Ряса
Я буддийский монах - Монах
Моя одежда - Кашая
המפעל שיוצר בגדים התחיל לעבוד היטב. כעת ניתן להוסיף למפעל ייצור ציוד לנזירים לתפילה מוצלחת (חרוזי ורדים וכו'). אבל עדיין נותרה השאלה, האם אפשר להשתמש ב-2 תבניות ביחד? כמובן שאתה יכול :) בואו ננסה ליצור את הגרסה הסופית של הפרויקט שלנו ולהוסיף נזיר הינדי: א) מפעלים יוצרים כעת נזירים נשמעים כמו "מפעל כוכבים" :
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();
    }
}
ב) אין צורך במעמד המנזר + מימושים קונקרטיים של מעמד המנזר, הם מיושמים על ידי המפעל (להפך, יכולנו לעזוב אותם ולסלק את המפעלים, אבל בעצם הם יהיו אז פשוט במקום מפעלים, רק ב במקרה זה, המנזר יצטרך להיות ממשק, ולא מחלקה מופשטת). והוסיפו את מחלקת היישום:
public class Application {

    public Monk create(MonkFactory monkFactory, String type) {
        Monk monk = monkFactory.createMonk(type);
        monk.prepare(monkFactory);
        return monk;
    }
}
ב) נזירים מכילים כעת
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);
}
מכיל שיטת מפעל ביישומים, המיושמת באמצעות מפעל:
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());
    }
}
ד) ונבדוק:
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();
    }
}
Я православный монах - Мантийный монах
Моя одежда - Мантия
Я католический монах - Иезуит
Моя одежда - Ряса с капюшоном
Я англиканский монах - Бенедиктинец
Моя одежда - Ряса
Я буддийский монах - Монах
Моя одежда - Кашая
Я индуистский монах - Саньяси
Моя одежда - Почти ничего, тепло же :)
לסיכום, ניתן לציין ששיטת המפעל השתמשה במחלקה אבסטרקטית עם מתודה לא מיושמת שהוטמעה בתתי מחלקות, והמפעל המופשט השתמש בממשק, שבו היישום (במקרה שלנו, יצירת נזיר) התרחש במחלקות שהטמיעו זאת מִמְשָׁק.
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION