תבנית המתודה התבניתית
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Java של Coddy. שיעור 73 מתוך 87.
תבנית שיטת התבנית היא תבנית עיצוב התנהגותית שמגדירה את שלד האלגוריתם במחלקת בסיס, ומאפשרת למחלקות יורשות לשנות שלבים מסוימים בלי לשנות את המבנה הכללי. אפשר לחשוב על זה כמו על מתכון: תהליך הבישול הכללי נשאר זהה, אך אפשר לשנות רכיבים או טכניקות מסוימים.
התבנית משתמשת במחלקה מופשטת עם מתודת תבנית שקוראת לרצף של שלבים. לחלק מהשלבים יש מימושי ברירת מחדל, בעוד שאחרים מופשטים וחייבים להיות ממומשים במחלקות יורשות:
abstract class DataProcessor {
// מתודת תבנית - מגדירה את מבנה האלגוריתם
public final void process() {
readData();
processData();
saveData();
}
abstract void readData();
abstract void processData();
// מימוש ברירת מחדל (מתודת עוגן)
void saveData() {
System.out.println("Saving to default location");
}
}
class CSVProcessor extends DataProcessor {
void readData() {
System.out.println("Reading CSV file");
}
void processData() {
System.out.println("Parsing CSV data");
}
}
class XMLProcessor extends DataProcessor {
void readData() {
System.out.println("Reading XML file");
}
void processData() {
System.out.println("Parsing XML data");
}
}בדרך כלל מסמנים את שיטת התבנית כ־final כדי למנוע ממחלקות משנה לשנות את מבנה האלגוריתם. כשקוראים לה, היא מבצעת את אותו רצף עבור כל מחלקות המשנה:
DataProcessor csv = new CSVProcessor();
csv.process();
// פלט:
// קורא קובץ CSV
// מנתח נתוני CSV
// שומר במיקום ברירת המחדלתבנית Template Method אידיאלית כאשר יש לך אלגוריתמים שחולקים את אותו המבנה אך נבדלים בשלבים מסוימים. היא מעודדת שימוש חוזר בקוד על ידי הצבת הלוגיקה המשותפת במחלקת הבסיס, תוך מתן אפשרות להתאמה אישית באמצעות ירושה.
אתגר
קלבואו נבנה מערכת ליצירת מסמכים באמצעות תבנית השיטה (Template Method Pattern)! תיצרו מסגרת שבה סוגי מסמכים שונים (דוחות וחשבוניות) פועלים לפי אותו תהליך יצירה, אך מתאימים אישית שלבים מסוימים. זה מושלם למצבים שבהם תהליך העבודה הכללי קבוע, אך הפרטים משתנים בהתאם לסוג המסמך.
תארגנו את הקוד בארבעה קבצים:
DocumentGenerator.java: צרו מחלקת בסיס אבסטרקטית שמגדירה את תבנית השיטה. למחלקהDocumentGeneratorצריכים להיות:שיטה
finalבשםgenerateDocument()שמגדירה את שלד האלגוריתם באמצעות קריאה לשלושה שלבים לפי הסדר:createHeader(),createBody()ו-createFooter().שתי שיטות אבסטרקטיות:
createHeader()ו-createBody(), שעל מחלקות הבת לממש.שיטת וו
createFooter()עם מימוש ברירת מחדל שמדפיס את--- End of Document ---.ReportGenerator.java: צרו מחלקת בת קונקרטית ליצירת דוחות. הבנאי שלה יקבל כותרת לדוח (String).השיטה
createHeader()מדפיסה את=== REPORT: [title] ===השיטה
createBody()מדפיסה אתReport content goes here...השתמשו בכותרת התחתונה שעוברת בירושה כברירת מחדל.
InvoiceGenerator.java: צרו מחלקת בת קונקרטית נוספת ליצירת חשבוניות. הבנאי שלה יקבל שם לקוח (String) וסכום (double).השיטה
createHeader()מדפיסה את*** INVOICE ***השיטה
createBody()מדפיסה בשורה אחת אתCustomer: [name], ולאחר מכן בשורה הבאה אתAmount Due: $[amount](עצבו את הסכום כך שיוצגו שתי ספרות אחרי הנקודה העשרונית).דרסו את
createFooter()כדי להדפיס אתThank you for your business!Main.java: חברו יחד את מערכת תבנית השיטה! תקבלו שלוש קלטים: כותרת לדוח (String), שם לקוח (String) וסכום חשבונית (double).צרו
ReportGeneratorעם הכותרת וקראו ל-generateDocument(). הדפיסו שורה ריקה להפרדה. לאחר מכן, צרוInvoiceGeneratorעם שם הלקוח והסכום, וקראו ל-generateDocument().
תקבלו שלוש קלטים לפי הסדר: כותרת לדוח (String), שם לקוח (String) וסכום חשבונית (double).
לדוגמה, עבור הקלטים Q4 Sales, Acme Corp ו-1500.00, הפלט שלכם יהיה:
=== REPORT: Q4 Sales ===
Report content goes here...
--- End of Document ---
*** INVOICE ***
Customer: Acme Corp
Amount Due: $1500.00
Thank you for your business!שימו לב ששני סוגי המסמכים פועלים לפי אותו תהליך בן שלושה שלבים שמוגדר בתבנית השיטה, אך כל אחד מהם מתאים את השלבים באופן שונה. ה-ReportGenerator משתמש בכותרת התחתונה שמוגדרת כברירת מחדל, בעוד שה-InvoiceGenerator מספק כותרת תחתונה משלו. זהו הכוח של שיטות וו בתבנית השיטה!
נסו בעצמכם
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// קראו את נתוני הקלט
String reportTitle = scanner.nextLine();
String customerName = scanner.nextLine();
double invoiceAmount = scanner.nextDouble();
// TODO: צרו ReportGenerator עם הכותרת וקראו ל-generateDocument()
// TODO: הדפיסו שורה ריקה להפרדה
// TODO: צרו InvoiceGenerator עם שם הלקוח והסכום, וקראו ל-generateDocument()
}
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
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+)3מאפייני מחלקה וחברים סטטיים
משתני מופע לעומת משתנים סטטייםמתודות סטטיותבלוקים סטטייםקבועים (static final)סיכום — מונה ומחלקת עזר6ממשקים ומחלקות מופשטות
מבוא לממשקיםמימוש ממשקיםמימוש של כמה ממשקיםמתודות ברירת מחדל וסטטיות בממשקמחלקות מופשטות לעומת ממשקיםממשקים פונקציונלייםסיכום – מערכת תשלומים9גנריות
מבוא לגנריותמחלקות גנריותשיטות גנריותפרמטרי טיפוס מוגבליםתווים כלליים (?, extends, super)סיכום – מכל גנרי12תבניות עיצוב — חלק 2
תבנית הפקודהתבנית המתאםתבנית העיצובתבנית המתודה התבניתיתתבנית המצבתבנית המרוכבתתבנית האיטרטורתרגלו בעצמכם: קומפיילר Java אונליין