תבנית Factory
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Java של Coddy. שיעור 66 מתוך 87.
תבנית המפעל היא תבנית עיצוב ליצירה, שמעבירה את יצירת האובייקטים לשיטה או למחלקה נפרדת. במקום להשתמש ב־new ישירות בקוד שלך, מבקשים ממפעל ליצור עבורך אובייקטים. הדבר שימושי במיוחד כשיש לך כמה מחלקות קשורות שחולקות ממשק או מחלקת אב משותפים.
נבחן מערכת התראות שיכולה לשלוח הודעות דוא״ל, הודעות SMS או התראות בדחיפה. ללא מפעל, הקוד שלך יצטרך להכיר כל מחלקה קונקרטית:
// ללא Factory - הלקוח חייב להכיר את כל המחלקות הקונקרטיות
Notification notification;
if (type.equals("email")) {
notification = new EmailNotification();
} else if (type.equals("sms")) {
notification = new SMSNotification();
}באמצעות תבנית המפעל, מרכזים את לוגיקת היצירה הזו:
public interface Notification {
void send(String message);
}
public class EmailNotification implements Notification {
public void send(String message) {
System.out.println("Email: " + message);
}
}
public class SMSNotification implements Notification {
public void send(String message) {
System.out.println("SMS: " + message);
}
}
public class NotificationFactory {
public static Notification create(String type) {
if (type.equals("email")) {
return new EmailNotification();
} else if (type.equals("sms")) {
return new SMSNotification();
}
return null;
}
}כעת קוד הלקוח נקי יותר ואינו תלוי במחלקות קונקרטיות:
Notification notification = NotificationFactory.create("email");
notification.send("Hello!"); // פלט: Email: Hello!היתרון המרכזי הוא שהוספת סוגי התראות חדשים דורשת עדכון של המפעל בלבד: קוד הלקוח נשאר ללא שינוי. כך פועלים לפי העיקרון של תכנות מול ממשק, שהופך את המערכת שלך לגמישה יותר וקלה יותר להרחבה.
אתגר
קלבואו נבנה מערכת לציור צורות באמצעות תבנית המפעל! במקום ליצור אובייקטים של צורות ישירות ברחבי הקוד, תרכזו את לוגיקת היצירה במפעל שמפיק סוגים שונים של צורות על סמך מזהה מחרוזת פשוט.
תארגנו את הקוד בארבעה קבצים:
Shape.java: הגדירו ממשק בשםShapeשישמש כחוזה המשותף לכל הצורות. הממשק שלכם צריך להצהיר על מתודה יחידהdraw(), שלא מחזירה דבר אלא מדפיסה מידע על הצורה שמצוירת.Shapes.java: צרו שלוש מחלקות שמממשות את הממשקShape:Circle- המתודהdraw()שלה צריכה להדפיסDrawing a CircleRectangle- המתודהdraw()שלה צריכה להדפיסDrawing a RectangleTriangle- המתודהdraw()שלה צריכה להדפיסDrawing a TriangleShapeFactory.java: צרו את מחלקת המפעל שאחראית ליצירת צורות. למחלקהShapeFactoryצריכה להיות מתודה סטטיתcreateShape(String type)שמחזירהShape. בהתאם לפרמטר type:- אם type שווה ל-
"circle", החזירוCircleחדש - אם type שווה ל-
"rectangle", החזירוRectangleחדש - אם type שווה ל-
"triangle", החזירוTriangleחדש - עבור כל ערך אחר, החזירו
null
- אם type שווה ל-
Main.java: חברו את מערכת המפעל שלכם! תקבלו שתי קלטים: שני סוגי צורות (שניהם מסוג String).עבור כל קלט, השתמשו ב-
ShapeFactoryכדי ליצור את הצורה ולאחסן אותה במשתנה מסוגShape. אם המפעל מחזיר צורה תקינה (לא null), קראו למתודהdraw()שלה. אם הוא מחזיר null, הדפיסוUnknown shape: [type], כאשר [type] הוא הקלט שסופק.עבדו על שני הקלטים לפי הסדר, וכל אחד מהם בשורה נפרדת בפלט.
תקבלו שני קלטים לפי הסדר: סוג הצורה הראשון (String) וסוג הצורה השני (String).
שימו לב שמחלקת Main שלכם לעולם אינה משתמשת ישירות ב-new Circle() או ב-new Rectangle(). היא מכירה רק את הממשק Shape ומבקשת מהמפעל ליצור אובייקטים. זהו כוחה של תבנית המפעל: קוד הלקוח אינו תלוי במחלקות הקונקרטיות!
נסו בעצמכם
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// קראו שני סוגי צורות
String type1 = scanner.nextLine();
String type2 = scanner.nextLine();
// TODO: השתמשו ב-ShapeFactory כדי ליצור את הצורה הראשונה
// אם הצורה אינה null, קראו ל-draw()
// אם הצורה היא null, הדפיסו "Unknown shape: [type]"
// TODO: עשו את אותו הדבר עבור הצורה השנייה
}
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים
קבצים חיצונייםמבוא לתכנות מונחה עצמיםמחלקות לעומת אובייקטיםמילת המפתח thisמתודותשדות (מאפיינים)מתודת הבנאיהעמסת בנאיםחזרה – מחשבון פשוט4ירושה
ירושה בסיסית (extends)מילת המפתח superדריסת מתודות (@Override)שרשור בנאיםהמחלקה Objectירושה יחידה ורב־רמתיתלמה אין ירושה מרובה של מחלקותסיכום – היררכיית עובדים7מתודות מיוחדות ומחלקת Object
מתודת toString()equals() ו-hashCode()מתודת clone()compareTo() ו-Comparableהממשק Comparatorסיכום – מיון מותאם אישית2מצייני גישה ואנקפסולציה
סקירה של רמות הגישהמתודות Getter ו-Setterהסתרת מידעמילת המפתח finalסיכום – מנהל חשבון בנק5פולימורפיזם
יסודות העמסת מתודותדריסת מתודות (בזמן ריצה)המרה כלפי מעלה וכלפי מטההאופרטור instanceofמחלקות ומתודות מופשטותסיכום – מחשבון צורות8מושגי OOP מתקדמים
הרכבה מול ירושהאגרגציה מול הרכבהמחלקות פנימיות, מקוננות ואנונימיותEnums ומתודות של EnumRecords (Java 16+)מחלקות חתומות (Java 17+)11תבניות עיצוב חלק 1
מבוא לתבניות עיצובתבנית Singletonתבנית Factoryתבנית Builderתבנית Observerתבנית Strategy3מאפייני מחלקה וחברים סטטיים
משתני מופע לעומת משתנים סטטייםמתודות סטטיותבלוקים סטטייםקבועים (static final)סיכום — מונה ומחלקת עזר6ממשקים ומחלקות מופשטות
מבוא לממשקיםמימוש ממשקיםמימוש של כמה ממשקיםמתודות ברירת מחדל וסטטיות בממשקמחלקות מופשטות לעומת ממשקיםממשקים פונקציונלייםסיכום – מערכת תשלומיםתרגלו בעצמכם: קומפיילר Java אונליין