תבנית הפקודה
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Java של Coddy. שיעור 70 מתוך 87.
תבנית הפקודה היא תבנית עיצוב התנהגותית שהופכת בקשה לאובייקט עצמאי שמכיל את כל המידע על הבקשה. השינוי הזה מאפשר לך להעביר בקשות כארגומנטים של מתודה, לדחות את ביצוע הבקשה או להכניס אותה לתור, ולתמוך בפעולות שניתן לבטל.
חשוב על זה כמו על מסעדה: במקום לומר לשף ישירות מה לבשל, כותבים את ההזמנה על פתק. המלצר (המפעיל) לוקח את הפתק (הפקודה) למטבח, שם אפשר להכניס אותה לתור, לתת לה עדיפות או אפילו לבטל אותה. התבנית מנתקת בין האובייקט שמפעיל את הפעולה לבין זה שמבצע אותה.
Command שמצהיר על המתודה execute, מחלקות ConcreteCommand שמממשות פעולות ספציפיות, Receiver שמבצע את העבודה בפועל, ו-Invoker שמפעיל פקודות:
interface Command {
void execute();
}class Light { // מקבל
public void turnOn() {
System.out.println("Light is ON");
}
public void turnOff() {
System.out.println("Light is OFF");
}
}class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.turnOn();
}
}class RemoteControl { // מפעיל
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}המפעיל לא יודע איזו פעולה תבוצע. הוא פשוט קורא ל־execute():
Light light = new Light();
Command lightOn = new LightOnCommand(light);
RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton(); // פלט: האור דולקתבנית הפקודה מצטיינת כשצריך לתור פעולות, לממש פונקציונליות של ביטול וביצוע מחדש, או לתזמן משימות לביצוע מאוחר יותר. כל פקודה מכילה את כל מה שנדרש לביצוע פעולה, וכך המערכת שלך נעשית גמישה וניתנת להרחבה יותר.
אתגר
קלבואו נבנה מערכת אוטומציה לבית חכם באמצעות תבנית הפקודה! תיצרו מערכת שבה אפשר לשלוט במכשירים שונים בבית באמצעות פקודות, וכך ניתן להכניס פעולות לתור, לבצע אותן ואפילו לשמור אותן לשימוש מאוחר יותר — ממש כמו שלט אוניברסלי שלא צריך לדעת איך כל מכשיר פועל מבפנים.
תארגנו את הקוד שלכם בארבעה קבצים:
Command.java: הגדירו את הממשקCommand, שכל פקודות המכשירים יממשו. עליו להצהיר על מתודה יחידהexecute(), שמבצעת את פעולת הפקודה.Devices.java: צרו שתי מחלקות מקבלות שמייצגות מכשירים לבית חכם:Television- יש לה מתודותpowerOn()שמדפיסהTV is now ON,powerOff()שמדפיסהTV is now OFF, ו-setChannel(int channel)שמדפיסהTV channel set to [channel].Thermostat- יש לה מתודותsetTemperature(int temp)שמדפיסהThermostat set to [temp] degrees, ו-turnOff()שמדפיסהThermostat turned OFF.Commands.java: צרו מחלקות פקודה קונקרטיות שעוטפות פעולות של מכשירים:TVOnCommand- מקבלתTelevisionבבנאי שלה וקוראת ל-powerOn()בעת ביצוע הפקודה.TVOffCommand- מקבלתTelevisionבבנאי שלה וקוראת ל-powerOff()בעת ביצוע הפקודה.TVChannelCommand- מקבלתTelevisionואתint channelבבנאי שלה, וקוראת ל-setChannel()עם הערוץ הזה בעת ביצוע הפקודה.ThermostatSetCommand- מקבלתThermostatואתint temperatureבבנאי שלה, וקוראת ל-setTemperature()עם הערך הזה בעת ביצוע הפקודה.Main.java: צרו מחלקה בשםRemoteControl(המפעיל), שיש לה מתודהsetCommand(Command cmd)לשמירת פקודה ומתודהpressButton()לביצוע הפקודה השמורה.במתודה הראשית שלכם תקבלו שני קלטים: מספר ערוץ טלוויזיה (מספר שלם) וטמפרטורה לתרמוסטט (מספר שלם).
צרו
Televisionו-Thermostat. צרוRemoteControlוהדגימו את תבנית הפקודה כך:- הגדירו
TVOnCommandולחצו על הכפתור - הגדירו
TVChannelCommandעם ערוץ הטלוויזיה שהתקבל בקלט ולחצו על הכפתור - הגדירו
ThermostatSetCommandעם הטמפרטורה שהתקבלה בקלט ולחצו על הכפתור - הגדירו
TVOffCommandולחצו על הכפתור
- הגדירו
תקבלו שני קלטים לפי הסדר: ערוץ הטלוויזיה (מספר שלם) וטמפרטורת התרמוסטט (מספר שלם).
לדוגמה, עבור הקלטים 5 ו-72, הפלט שלכם יהיה:
TV is now ON
TV channel set to 5
Thermostat set to 72 degrees
TV is now OFFשימו לב ש-RemoteControl אף פעם לא מתקשר ישירות עם המכשירים. הוא מכיר רק את הממשק Command. כל פקודה עוטפת את כל מה שנדרש לביצוע הפעולה שלה, וכך קל להוסיף מכשירים ופקודות חדשים בלי לשנות את המפעיל!
נסו בעצמכם
import java.util.Scanner;
// TODO: צרו את המחלקה RemoteControl (מפעילת הפקודות) עם:
// - מתודה setCommand(Command cmd) לשמירת פקודה
// - מתודה pressButton() לביצוע הפקודה שנשמרה
class RemoteControl {
// TODO: ממשו את RemoteControl
}
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// קראו את הקלט
int channel = scanner.nextInt();
int temperature = scanner.nextInt();
// TODO: צרו Television ו-Thermostat
// TODO: צרו RemoteControl
// TODO: הדגימו את תבנית הפקודה:
// 1. הגדירו TVOnCommand ולחצו על הכפתור
// 2. הגדירו TVChannelCommand עם הערוץ מהקלט ולחצו על הכפתור
// 3. הגדירו ThermostatSetCommand עם הטמפרטורה מהקלט ולחצו על הכפתור
// 4. הגדירו TVOffCommand ולחצו על הכפתור
scanner.close();
}
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
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 אונליין