CodeGym /בלוג Java /Random-HE /דפוסי עיצוב: מפעל מופשט
John Squirrels
רָמָה
San Francisco

דפוסי עיצוב: מפעל מופשט

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

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

מפעל מופשט, כמו כל דפוסי המפעל, עוזר לנו להבטיח שאובייקטים חדשים ייווצרו בצורה נכונה. אנו משתמשים בו כדי לנהל את ה"ייצור" של משפחות שונות של אובייקטים הקשורים זה בזה. משפחות שונות של חפצים מחוברים... מה זה אומר? אל דאגה: בפועל הכל פשוט יותר ממה שזה נראה. מלכתחילה, מה יכולה להיות משפחה של עצמים מחוברים? נניח שאנו מפתחים אסטרטגיה צבאית הכוללת מספר סוגים של יחידות:
  • חֵיל הַרַגלִים
  • חֵיל הַפָּרָשִׁים
  • קשתים
יחידות מסוג זה קשורות זו בזו, כי הן משרתות באותו צבא. נוכל לומר שהקטגוריות המפורטות לעיל הן משפחה של עצמים מחוברים זה לזה. אנחנו מבינים את זה. אבל תבנית המפעל המופשטת משמשת כדי לארגן את היצירה של משפחות שונות של חפצים מחוברים זה לזה. גם כאן אין שום דבר מסובך. נמשיך בדוגמה של האסטרטגיה הצבאית. באופן כללי, יחידות צבאיות שייכות לכמה צדדים לוחמים שונים. תלוי בצד של מי הם נמצאים, יחידות צבאיות יכולות להשתנות משמעותית במראה החיצוני. החיילים הרגליים, הפרשים והקשתים של הצבא הרומי אינם זהים לחיילים רגליים, פרשים וקשתים ויקינגים. באסטרטגיה הצבאית, חיילים של צבאות שונים הם משפחות שונות של עצמים מחוברים זה לזה. זה יהיה מצחיק אם טעות של מתכנת תגרום לכך שחייל במדים צרפתיים מתקופת נפוליאון, מוסקט מוכן, יימצא מסתובב בין שורות חיל הרגלים הרומי. תבנית עיצוב המפעל המופשטת נחוצה בדיוק לפתרון בעיה זו. לא, לא בעיית המבוכה שיכולה לבוא ממסע בזמן, אלא הבעיה של יצירת קבוצות שונות של אובייקטים הקשורים זה בזה. מפעל אבסטרקט מספק ממשק ליצירת כל המוצרים הזמינים (משפחת אובייקטים). למפעל מופשט יש בדרך כלל יישומים מרובים. כל אחד מהם אחראי על יצירת מוצרים של אחת המשפחות. האסטרטגיה הצבאית שלנו תכלול מפעל מופשט שיוצר חיילים רגליים מופשטים, קשתים ופרשים, כמו גם יישומים של מפעל זה. למשל, מפעל שיוצר לגיונרים רומיים ומפעל שיוצר חיילים קרתגים. הפשטה היא העיקרון המנחה החשוב ביותר של דפוס זה. לקוחות המפעל עובדים עם המפעל ומוצריו רק באמצעות ממשקים מופשטים. כתוצאה מכך, אתה לא צריך לחשוב על אילו חיילים נוצרים כרגע. במקום זאת, אתה מעביר את האחריות הזו ליישום קונקרטי כלשהו של המפעל המופשט.

בואו נמשיך להפוך את בית הקפה שלנו לאוטומטי

בשיעור האחרון , למדנו את דפוס שיטת המפעל. השתמשנו בו כדי להרחיב את עסקי הקפה שלנו ולפתוח מספר מיקומים חדשים. היום נמשיך לחדש את העסק שלנו. באמצעות תבנית המפעל המופשטת, נניח את הבסיס ליישום שולחן עבודה חדש להזמנת קפה באינטרנט. בעת כתיבת יישום שולחני, עלינו תמיד לחשוב על תמיכה חוצת פלטפורמות. האפליקציה שלנו חייבת לעבוד גם ב-macOS וגם ב-Windows (ספוילר: תמיכה בלינוקס נשארת לך ליישם כשיעורי בית). איך תיראה האפליקציה שלנו? די פשוט: זה יהיה טופס המורכב משדה טקסט, שדה בחירה וכפתור. אם יש לך ניסיון בשימוש במערכות הפעלה שונות, בהחלט שמת לב שכפתורים ב-Windows מוצגים בצורה שונה מאשר ב-Mac. כמו כל השאר... ובכן, בואו נתחיל. כפי שבטח כבר הבנתם, משפחות המוצרים יהיו מורכבות מאלמנטים של הממשק הגרפי:
  • כפתורים
  • שדות טקסט
  • שדות בחירה
כתב ויתור: בכל ממשק, נוכל להגדיר שיטות כמו onClick, onValueChanged, או onInputChanged. במילים אחרות, נוכל להגדיר שיטות שיאפשרו לנו לטפל באירועים שונים (לחיצה על כפתור, הזנת טקסט, בחירת ערך בתיבת בחירה). כל זה מושמט כאן בכוונה כדי לא להעמיס על הדוגמה ולהבהיר אותה ככל שאנו לומדים את דפוס המפעל. בואו נגדיר ממשקים מופשטים עבור המוצרים שלנו:
public interface Button {}
public interface Select {}
public interface TextField {}
עבור כל מערכת הפעלה, עלינו ליצור רכיבי ממשק בסגנון מערכת ההפעלה. אנו נכתוב קוד עבור Windows ו- MacOS. בואו ניצור יישומים עבור Windows:
public class WindowsButton implements Button {
}

public class WindowsSelect implements Select {
}

public class WindowsTextField implements TextField {
}
כעת אנו עושים את אותו הדבר עבור MacOS:
public class MacButton implements Button {
}

public class MacSelect implements Select {
}

public class MacTextField implements TextField {
}
מְעוּלֶה. כעת נוכל להמשיך למפעל המופשט שלנו, שייצור את כל סוגי המוצרים המופשטים הזמינים:
public interface GUIFactory {

    Button createButton();
    TextField createTextField();
    Select createSelect();

}
מְצוּיָן. כפי שאתה יכול לראות, עדיין לא עשינו שום דבר מסובך. כל מה שאחריו גם פשוט. באנלוגיה למוצרים, אנו יוצרים יישומי מפעל שונים עבור כל מערכת הפעלה. נתחיל עם Windows:
public class WindowsGUIFactory implements GUIFactory {
    public WindowsGUIFactory() {
        System.out.println("Creating GUIFactory for Windows OS");
    }

    public Button createButton() {
        System.out.println("Creating Button for Windows OS");
        return new WindowsButton();
    }

    public TextField createTextField() {
        System.out.println("Creating TextField for Windows OS");
        return new WindowsTextField();
    }

    public Select createSelect() {
        System.out.println("Creating Select for Windows OS");
        return new WindowsSelect();
    }
}
הוספנו פלט מסוף בתוך המתודות והקונסטרוקטור על מנת להמחיש עוד יותר מה קורה. עכשיו עבור macOS:
public class MacGUIFactory implements GUIFactory {
    public MacGUIFactory() {
        System.out.println("Creating GUIFactory for macOS");
    }

    @Override
    public Button createButton() {
        System.out.println("Creating Button for macOS");
        return new MacButton();
    }

    @Override
    public TextField createTextField() {
        System.out.println("Creating TextField for macOS");
        return new MacTextField();
    }

    @Override
    public Select createSelect() {
        System.out.println("Creating Select for macOS");
        return new MacSelect();
    }
}
שימו לב שכל חתימת שיטה מציינת שהשיטה מחזירה טיפוס מופשט. אבל בתוך השיטות, אנחנו יוצרים יישומים ספציפיים של המוצרים. זהו המקום היחיד בו אנו שולטים ביצירת מופעים ספציפיים. עכשיו הגיע הזמן לכתוב שיעור לטופס. זוהי מחלקת Java שהשדות שלה הם רכיבי ממשק:
public class CoffeeOrderForm {
    private final TextField customerNameTextField;
    private final Select coffeeTypeSelect;
    private final Button orderButton;

    public CoffeeOrderForm(GUIFactory factory) {
        System.out.println("Creating coffee order form");
        customerNameTextField = factory.createTextField();
        coffeeTypeSelect = factory.createSelect();
        orderButton = factory.createButton();
    }
}
מפעל אבסטרקטי היוצר רכיבי ממשק מועבר לבנאי הטופס. נעביר את ההטמעה הנחוצה במפעל לבנאי על מנת ליצור רכיבי ממשק עבור מערכת הפעלה מסוימת.
public class Application {
    private CoffeeOrderForm coffeeOrderForm;

    public void drawCoffeeOrderForm() {
        // Determine the name of the operating system through System.getProperty()
        String osName = System.getProperty("os.name").toLowerCase();
        GUIFactory guiFactory;

        if (osName.startsWith("win")) { // For Windows
            guiFactory = new WindowsGUIFactory();
        } else if (osName.startsWith("mac")) { // For Mac
            guiFactory = new MacGUIFactory();
        } else {
            System.out.println("Unknown OS. Unable to draw form :(");
            return;
        }
        coffeeOrderForm = new CoffeeOrderForm(guiFactory);
    }

    public static void main(String[] args) {
        Application application = new Application();
        application.drawCoffeeOrderForm();
    }
}
אם נריץ את האפליקציה ב-Windows, נקבל את הפלט הבא:

Creating GUIFactory for Windows OS
Creating coffee order form
Creating TextField for Windows OS
Creating Select for Windows OS
Creating Button for Windows OS
ב-Mac, הפלט יהיה כדלקמן:

Creating GUIFactory for macOS
Creating coffee order form
Creating TextField for macOS
Creating Select for macOS
Creating Button for macOS
על לינוקס:

Unknown OS. Unable to draw form :( 
ועכשיו אנחנו מסכמים. כתבנו את השלד של אפליקציה מבוססת GUI שבה רכיבי הממשק נוצרים במיוחד עבור מערכת ההפעלה הרלוונטית. נחזור בתמציתיות על מה שיצרנו:
  • משפחת מוצרים המורכבת משדה קלט, שדה בחירה וכפתור.
  • הטמעות שונות של משפחת המוצרים עבור Windows ו-macOS.
  • מפעל מופשט המגדיר ממשק ליצירת המוצרים שלנו.
  • שני יישומים של המפעל שלנו, כל אחד אחראי ליצירת משפחה ספציפית של מוצרים.
  • טופס (מחלקה של Java) שהשדות שלו הם רכיבי ממשק מופשטים שמאוחלים עם הערכים הדרושים בקונסטרוקטור באמצעות מפעל מופשט.
  • מחלקת יישומים בתוך מחלקה זו, אנו יוצרים טופס, ומעבירים את היישום הרצוי במפעל לבנאי שלו.
התוצאה היא שיישמנו את תבנית המפעל המופשטת.

מפעל מופשט: כיצד להשתמש

מפעל מופשט הוא דפוס עיצובי לניהול יצירת משפחות מוצרים שונות מבלי להיות קשור לכיתות מוצרים קונקרטיים. בעת שימוש בדפוס זה, עליך:
  1. הגדר משפחות מוצרים. נניח שיש לנו שניים מהם:
    • SpecificProductA1,SpecificProductB1
    • SpecificProductA2,SpecificProductB2
  2. לכל מוצר בתוך המשפחה, הגדירו מחלקה מופשטת (ממשק). במקרה שלנו, יש לנו:
    • ProductA
    • ProductB
  3. בתוך כל משפחת מוצרים, כל מוצר חייב ליישם את הממשק שהוגדר בשלב 2.
  4. צור מפעל מופשט, עם שיטות ליצירת כל מוצר שהוגדרו בשלב 2. במקרה שלנו, שיטות אלו יהיו:
    • ProductA createProductA();
    • ProductB createProductB();
  5. צור יישומי מפעל מופשטים כך שכל מימוש שולט ביצירת מוצרים של משפחה אחת. לשם כך, בתוך כל יישום של המפעל המופשט, עליך ליישם את כל שיטות היצירה כך שייצרו ויחזירו יישומי מוצר ספציפיים.
דיאגרמת UML הבאה ממחישה את ההוראות המפורטות לעיל: דפוסי עיצוב: מפעל מופשט - 3כעת נכתוב קוד בהתאם להוראות אלו:
// Define common product interfaces
public interface ProductA {}
public interface ProductB {}

// Create various implementations (families) of our products
public class SpecificProductA1 implements ProductA {}
public class SpecificProductB1 implements ProductB {}

public class SpecificProductA2 implements ProductA {}
public class SpecificProductB2 implements ProductB {}

// Create an abstract factory
public interface AbstractFactory {
    ProductA createProductA();
    ProductB createProductB();
}

// Implement the abstract factory in order to create products in family 1
public class SpecificFactory1 implements AbstractFactory {

    @Override
    public ProductA createProductA() {
        return new SpecificProductA1();
    }

    @Override
    public ProductB createProductB() {
        return new SpecificProductB1();
    }
}

// Implement the abstract factory in order to create products in family 2
public class SpecificFactory2 implements AbstractFactory {

    @Override
    public ProductA createProductA() {
        return new SpecificProductA2();
    }

    @Override
    public ProductB createProductB() {
        return new SpecificProductB2();
    }
}

שיעורי בית

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