JavaRush /בלוג Java /Random-HE /דפוסי עיצוב: FactoryMethod

דפוסי עיצוב: FactoryMethod

פורסם בקבוצה
שלום! היום נמשיך ללמוד דפוסי עיצוב ונדבר על שיטת המפעל. דפוסי עיצוב: FactoryMethod - 1תוכלו לגלות במה מדובר ולאילו משימות מתאימה התבנית הזו. נסתכל על דפוס עיצוב זה בפועל ונחקור את המבנה שלו. כדי להבהיר לך את כל האמור לעיל, עליך להבין את הנושאים הבאים:
  1. ירושה בג'אווה.
  2. שיטות ושיעורים מופשטים ב-Java.

איזו בעיה פותרת שיטת המפעל?

בכל דפוסי עיצוב המפעל קיימות שתי קבוצות של משתתפים - יוצרים (המפעלים עצמם) ומוצרים (החפצים שיצרו המפעלים). תארו לעצמכם את המצב: יש לנו מפעל שמייצר מכוניות תחת המותג AutoRush. היא יודעת ליצור דגמי מכוניות עם סוגים שונים של גוף:
  • מכוניות סדאן
  • מכוניות סטיישן
  • דוּ מוֹשָׁבִית סְגוּרָה
הדברים הלכו לנו כל כך טוב שיום בהיר אחד ספגנו את קונצרן OneAuto. כמנהלים הגיוניים, אנחנו לא רוצים לאבד לקוחות של OneAuto, והמשימה שלנו היא לבנות מחדש את הייצור בצורה כזו שנוכל לייצר:
  • מכוניות AutoRush
  • AutoRush סטיישן
  • קופה AutoRush
  • מכוניות סדאן של OneAuto
  • וואן אוטו סטיישן
  • OneAuto קופה
כפי שאתה יכול לראות, במקום קבוצה אחת של מוצרים נגזרים, הופיעו שניים, שנבדלים זה מזה בפרטים מסוימים. דפוס העיצוב של שיטת המפעל פותר את הבעיה של יצירת קבוצות שונות של מוצרים, שלכל אחת מהן יש ספציפיות מסוימת. נשקול את העיקרון של תבנית זו בפועל, ונעבור בהדרגה מפשוט למורכב, תוך שימוש בדוגמה של בית הקפה שלנו, שיצרנו באחת ההרצאות הקודמות .

קצת על תבנית המפעל

תן לי להזכיר לך: בנינו איתך בית קפה וירטואלי קטן. בו, בעזרת מפעל פשוט, למדנו איך ליצור סוגים שונים של קפה. היום נחדד את הדוגמה הזו. בואו נזכור איך נראה בית הקפה שלנו עם מפעל פשוט. היה לנו שיעור קפה:
public class Coffee {
    public void grindCoffee(){
        // перемалываем кофе
    }
    public void makeCoffee(){
        // делаем кофе
    }
    public void pourIntoCup(){
        // наливаем в чашку
    }
}
וגם כמה מיורשיו - סוגי קפה ספציפיים שהמפעל שלנו יכול לייצר:
public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
לנוחות קבלת הזמנות, הצגנו העברות:
public enum CoffeeType {
    ESPRESSO,
    AMERICANO,
    CAFFE_LATTE,
    CAPPUCCINO
}
מפעל הקפה עצמו נראה כך:
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;
    }
}
ולבסוף, בית הקפה עצמו:
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;
    }
}

מודרניזציה של מפעל פשוט

בית הקפה שלנו מצליח. עד כדי כך שאנחנו חושבים להרחיב. אנחנו רוצים לפתוח כמה נקודות חדשות. בתור חבר'ה יוזמים, לא נוציא בתי קפה מונוטוניים. אני רוצה שלכל אחד יהיה טוויסט משלו. לכן, מלכתחילה, נפתח שתי נקודות: בסגנון איטלקי ואמריקאי. השינויים ישפיעו לא רק על הפנים, אלא גם על המשקאות:
  • בבית קפה איטלקי נשתמש אך ורק במותגי קפה איטלקיים, עם טחינה וקלייה מיוחדים.
  • המנה האמריקאית תהיה קצת יותר גדולה, ובכל הזמנה נגיש מרשמלו מומס - מרשמלו.
הדבר היחיד שיישאר ללא שינוי הוא המודל העסקי שלנו, שהוכיח את עצמו היטב. אם אנחנו מדברים בשפת קוד, זה מה שקורה. היו לנו 4 מחלקות של מוצרים:
public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
וזה הופך ל-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 {}
מכיוון שאנו רוצים לשמור על המודל העסקי הנוכחי ללא שינוי, אנו רוצים שהשיטה orderCoffee(CoffeeType type)תעבור מספר מינימלי של שינויים. בואו נסתכל על זה:
public Coffee orderCoffee(CoffeeType type) {
    Coffee coffee = coffeeFactory.createCoffee(type);
    coffee.grindCoffee();
    coffee.makeCoffee();
    coffee.pourIntoCup();

    System.out.println("Вот ваш кофе! Спасибо, приходите еще!");
    return coffee;
}
אילו אפשרויות עומדות בפנינו? אנחנו כבר יודעים איך לכתוב מפעל, נכון? הדבר הפשוט ביותר שעולה מיד בראש הוא לכתוב שני מפעלים דומים, ולאחר מכן להעביר את היישום הנדרש לבית הקפה שלנו בקונסטרוקטור. אז המעמד של בית הקפה לא ישתנה. ראשית, עלינו ליצור מחלקת מפעל חדשה, לרשת מהמפעל הפשוט שלנו ולעקוף את ה- createCoffee (CoffeeType type). בואו נכתוב מפעלים ליצירת קפה בסגנון איטלקי ואמריקאי:
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;
    }

}
כעת נוכל להעביר את ההטמעה הנדרשת במפעל ל-CoffeeShop. בואו נראה איך ייראה הקוד להזמנת קפה מבתי קפה שונים. לדוגמה, קפוצ'ינו בסגנון איטלקי ואמריקאי:
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);
    }
}
יצרנו שני בתי קפה שונים, והעברנו כל אחד מהם למפעל הנדרש. מצד אחד השגנו את המטרה שלנו, אבל מצד שני... משהו שורט את נשמתו הבלתי ניתנת לריסון של היזם... בואו נבין מה לא בסדר. ראשית, שפע המפעלים. האם ניתן ליצור בכל פעם מפעל משלך לנקודה חדשה ובנוסף לוודא שבעת יצירת בית קפה המפעל הדרוש מועבר לקונסטרוקטור? שנית, זה עדיין מפעל פשוט. רק קצת מודרניזציה. אנחנו עדיין לומדים כאן דפוס חדש. שלישית, האם לא ניתן לעשות זאת אחרת? זה יהיה מגניב אם נוכל למקם את כל השאלות על הכנת קפה בתוך הכיתה CoffeeShop, לקשר בין תהליכי יצירת הקפה ושירות ההזמנה, אך במקביל לשמור על גמישות מספקת להכנת קפה בסגנונות שונים. התשובה היא כן, אתה יכול. זה נקרא דפוס עיצוב שיטת המפעל.

ממפעל פשוט לשיטת מפעל

כדי לפתור את הבעיה בצורה יעילה ככל האפשר, אנו:
  1. בואו נחזיר את השיטה createCoffee(CoffeeType type)למחלקה CoffeeShop.
  2. בואו נעשה את השיטה הזו למופשטת.
  3. השיעור עצמו CoffeeShopיהפוך למופשט.
  4. לכיתה CoffeeShopיהיו יורשים.
כן חבר. בית קפה איטלקי הוא לא יותר מאשר יורש של הכיתה CoffeeShop, מיישם שיטה createCoffee(CoffeeType type)בהתאם למיטב המסורת של הבריסטות האיטלקיות. אז לפי הסדר. שלב 1. בואו נעשה את הכיתה Coffeeלמופשטת. יש לנו כעת שתי משפחות של מוצרים שונים. משקאות קפה איטלקיים ואמריקאים עדיין חולקים אב קדמון משותף: ה Coffee. נכון יהיה לעשות את זה מופשט:
public abstract class Coffee {
    public void makeCoffee(){
        // делаем кофе
    }
    public void pourIntoCup(){
        // наливаем в чашку
    }
}
שלב 2. הפוך אותו CoffeeShopלמופשט, בשיטה מופשטתcreateCoffee(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);
}
שלב 3. צור בית קפה איטלקי, כיתת צאצאים של בית הקפה המופשט. בו אנו מיישמים את השיטה createCoffee(CoffeeType type)תוך התחשבות בפרטים האיטלקיים.
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;
    }
}
שלב 4. בואו נעשה את אותו הדבר עבור בית קפה בסגנון אמריקאי.
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;
    }
}
שלב 5. בואו נסתכל איך תיראה הזמנת לאטה בסגנון אמריקאי ואיטלקי:
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);
    }
}
מזל טוב. זה עתה יישמנו את דפוס העיצוב של שיטת המפעל בבית הקפה שלנו.

איך עובדת שיטת המפעל

עכשיו בואו נסתכל מקרוב על מה שקיבלנו. התרשים שלהלן מציג את הכיתות שהתקבלו. בלוקים ירוקים הם כיתות יוצרים, בלוקים כחולים הם כיתות מוצרים. דפוסי עיצוב: FactoryMethod - 2אילו מסקנות ניתן להסיק?
  1. כל המוצרים הם יישומים של המחלקה המופשטת Coffee.
  2. כל היוצרים הם מימושים של המחלקה המופשטת CoffeeShop.
  3. אנו רואים שתי היררכיות מחלקות מקבילות:
    • היררכיה של מוצרים. אנו רואים צאצאים איטלקיים וצאצאים אמריקאים
    • היררכיה של יוצרים. אנו רואים צאצאים איטלקיים וצאצאים אמריקאים
  4. ל- superclass CoffeeShopאין מידע לגבי מימוש מוצר ספציפי ( Coffee) שייווצר.
  5. כיתת-על CoffeeShopמאצילה את יצירתו של מוצר ספציפי לצאצאיו.
  6. כל כיתת צאצאים CoffeeShopמיישמת שיטת מפעל createCoffee()בהתאם לפרטים שלה. במילים אחרות, במסגרת ההטמעות של כיתות יוצרים, מתקבלת החלטה להכין מוצר ספציפי על סמך הפרטים של כיתת היוצר.
כעת אתה מוכן להגדיר את דפוס שיטת המפעל . תבנית שיטת המפעל מגדירה את הממשק ליצירת אובייקט, אך מאפשרת לתת-מחלקות לבחור את המחלקה של המופע ליצירה. לפיכך, שיטת Factory מאצילה את פעולת המופע לתת-מחלקות. באופן כללי, זכירת ההגדרה אינה חשובה כמו להבין כיצד דברים עובדים.

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

דפוסי עיצוב: FactoryMethod - 3התרשים לעיל מציג את המבנה הכללי של דפוס שיטת המפעל. מה עוד חשוב כאן?
  1. המחלקה Creator מכילה יישומים של כל השיטות המקיימות אינטראקציה עם מוצרים, למעט שיטת המפעל.
  2. שיטה מופשטת factoryMethod()חייבת להיות מיושמת על ידי כל צאצאי המחלקה Creator.
  3. הכיתה ConcreteCreatorמיישמת שיטה factoryMethod()המייצרת מוצר ישירות.
  4. שיעור זה אחראי ליצירת מוצרים ספציפיים. זהו השיעור היחיד עם מידע על יצירת מוצרים אלה.
  5. כל המוצרים חייבים ליישם ממשק משותף - להיות צאצאים של מחלקת מוצרים משותפת. זה הכרחי כדי ששיעורים המשתמשים במוצרים יוכלו לפעול עליהם ברמת הפשטות ולא מימושים קונקרטיים.

שיעורי בית

אז היום עשינו הרבה עבודה ולמדנו את דפוס העיצוב של שיטת המפעל. הגיע הזמן לאחד את החומר שסיפית! משימה 1. עבודה על פתיחת בית קפה נוסף. זה יכול להתבצע בסגנון אנגלי או ספרדית. או אפילו בסגנון של חללית. בואו נוסיף לקפה צבעי מאכל כדי להבריק, ובכלל, הקפה יהיה רק ​​מקום! משימה 2. בהרצאה האחרונה הייתה לכם משימה ליצור סושי בר וירטואלי או פיצריה וירטואלית. המשימה שלך היא לא לעמוד במקום. היום למדת איך אתה יכול להשתמש בתבנית שיטת המפעל כדי להגיע להצלחה. זה הזמן לנצל את הידע הזה ולהרחיב את העסק שלך ;)
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION