תבנית Strategy
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Java של Coddy. שיעור 69 מתוך 87.
תבנית האסטרטגיה היא תבנית עיצוב התנהגותית שמאפשרת לך להגדיר משפחה של אלגוריתמים, לארוז כל אחד מהם במחלקה משלו ולהפוך אותם לניתנים להחלפה. במקום לקבע התנהגות מסוימת בקוד, תוכל להחליף אלגוריתמים בזמן ריצה בלי לשנות את הקוד שמשתמש בהם.
דמיינו מערכת תשלומים שתומכת בכמה שיטות תשלום. ללא תבנית האסטרטגיה, תיאלצו להתמודד עם לוגיקה מותנית מבולגנת. בעזרתה, כל שיטת תשלום הופכת לאסטרטגיה בפני עצמה:
interface PaymentStrategy {
void pay(int amount);
}
class CreditCardPayment implements PaymentStrategy {
public void pay(int amount) {
System.out.println("Paid " + amount + " via Credit Card");
}
}
class PayPalPayment implements PaymentStrategy {
public void pay(int amount) {
System.out.println("Paid " + amount + " via PayPal");
}
}המחלקה ההקשר מחזיקה הפניה לאסטרטגיה ומעבירה אליה את ביצוע העבודה. העיקר הוא שההקשר אינו יודע באיזו אסטרטגיה קונקרטית הוא משתמש. הוא מכיר רק את הממשק:
class ShoppingCart {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy strategy) {
this.paymentStrategy = strategy;
}
public void checkout(int amount) {
paymentStrategy.pay(amount);
}
}עכשיו אפשר לשנות את התנהגות התשלום באופן דינמי:
ShoppingCart cart = new ShoppingCart();
cart.setPaymentStrategy(new CreditCardPayment());
cart.checkout(100); // שולמו 100 באמצעות כרטיס אשראי
cart.setPaymentStrategy(new PayPalPayment());
cart.checkout(50); // שולמו 50 באמצעות PayPalתבנית האסטרטגיה בולטת כשיש לך כמה דרכים לבצע פעולה ואתה רוצה לעבור ביניהן בקלות. הוספת אמצעי תשלום חדש פירושה פשוט יצירת מחלקה חדשה שמממשת את PaymentStrategy: אין צורך בשינויים בקוד הקיים.
אתגר
קלבואו נבנה מערכת לעיצוב טקסט באמצעות תבנית האסטרטגיה! תיצרו מערכת שבה אפשר להחליף אסטרטגיות עיצוב שונות בזמן ריצה כדי לשנות טקסט בדרכים מגוונות, והיא מתאימה במיוחד ליישום שצריך לתמוך בכמה פורמטים של פלט.
תארגנו את הקוד בארבעה קבצים:
TextFormatter.java: הגדירו את הממשקTextFormatterשכל אסטרטגיות העיצוב יממשו. עליו להצהיר על מתודה יחידהString format(String text)שמקבלת מחרוזת ומחזירה את הגרסה המעוצבת.Formatters.java: צרו שלוש אסטרטגיות קונקרטיות שמממשות את הממשקTextFormatterשלכם:UpperCaseFormatter- משנה את הטקסט לאותיות גדולות.LowerCaseFormatter- משנה את הטקסט לאותיות קטנות.TitleCaseFormatter- הופכת את האות הראשונה בכל מילה לאות גדולה ואת שאר האותיות לאותיות קטנות. מילים מופרדות ברווחים.TextEditor.java: צרו את מחלקת ההקשר שמשתמשת באסטרטגיית עיצוב. במחלקהTextEditorשלכם צריכים להיות:שדה פרטי לשמירת אסטרטגיית
TextFormatterהנוכחית.מתודה
setFormatter(TextFormatter formatter)שמשנה את אסטרטגיית העיצוב הפעילה.מתודה
publishText(String text)שמחילה את המעצב הנוכחי על הטקסט ומדפיסהPublished: [formatted text]. אם לא הוגדר מעצב, עליה להדפיס אתPublished: [original text]ללא שינוי.Main.java: חברו יחד את מערכת האסטרטגיות שלכם! תקבלו שתי קלטים: הטקסט לעיצוב (String) וסגנון העיצוב (String:"upper","lower"או"title").צרו
TextEditorוקראו תחילה ל-publishTextעם טקסט הקלט, לפני הגדרת מעצב כלשהו (כדי להדגים את התנהגות ברירת המחדל).לאחר מכן, בהתאם לקלט של הסגנון, הגדירו את אסטרטגיית העיצוב המתאימה וקראו שוב ל-
publishTextעם אותו טקסט כדי להציג את התוצאה המעוצבת.
תקבלו שני קלטים לפי הסדר: הטקסט לעיצוב (String) וסגנון העיצוב (String).
לדוגמה, עבור הקלטים hello world ו-title, הפלט יהיה:
Published: hello world
Published: Hello Worldשימו לב שמחלקת TextEditor אינה יודעת דבר על לוגיקת העיצוב הספציפית. היא פשוט מעבירה את המשימה לאסטרטגיה שהוגדרה כרגע. כך קל מאוד להוסיף סגנונות עיצוב חדשים בלי לגעת בקוד של העורך!
נסו בעצמכם
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String text = scanner.nextLine();
String style = scanner.nextLine();
// TODO: צור מופע של TextEditor
// TODO: קרא ל-publishText עם טקסט הקלט (לפני הגדרת מעצב כלשהו)
// TODO: בהתאם לקלט הסגנון ("upper", "lower" או "title"),
// הגדר את המעצב המתאים וקרא שוב ל-publishText
}
}השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
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 אונליין