תבנית הפקודה
חלק מהיחידה תכנות מונחה עצמים במסלול ה-C# של Coddy. שיעור 56 מתוך 70.
תבנית Command היא תבנית התנהגותית שהופכת בקשה לאובייקט עצמאי שמכיל את כל המידע על הבקשה. ההמרה הזו מאפשרת להעביר בקשות כארגומנטים של מתודות, להכניס אותן לתור, לתעד אותן או לתמוך בפעולות שניתן לבטל.
התבנית מפרידה בין האובייקט שמפעיל פעולה לבין האובייקט שיודע כיצד לבצע אותה. היא מורכבת מארבעה רכיבים מרכזיים: ממשק פקודה, פקודות קונקרטיות, מקבל (האובייקט שמבצע את העבודה בפועל) ומפעיל (האובייקט שמפעיל פקודות).
public interface ICommand
{
void Execute();
}
public class Light // מקבל
{
public void TurnOn() => Console.WriteLine("Light is ON");
public void TurnOff() => Console.WriteLine("Light is OFF");
}
public class TurnOnCommand : ICommand
{
private Light _light;
public TurnOnCommand(Light light) => _light = light;
public void Execute() => _light.TurnOn();
}
public class TurnOffCommand : ICommand
{
private Light _light;
public TurnOffCommand(Light light) => _light = light;
public void Execute() => _light.TurnOff();
}המפעיל מאחסן ומבצע פקודות בלי לדעת מה הן עושות:
public class RemoteControl // מפעיל
{
private ICommand _command;
public void SetCommand(ICommand command) => _command = command;
public void PressButton() => _command.Execute();
}
// שימוש
var light = new Light();
var remote = new RemoteControl();
remote.SetCommand(new TurnOnCommand(light));
remote.PressButton(); // Light דולק (ON)
remote.SetCommand(new TurnOffCommand(light));
remote.PressButton(); // Light כבוי (OFF)תבנית הפקודה יעילה במיוחד למימוש פונקציונליות של ביטול, הקלטת מאקרו או הוספת פעולות לתור לביצוע מאוחר יותר. כל פקודה מכילה את כל הדרוש לביצוע פעולה, ובכך הופכת את המערכת שלך לגמישה יותר וניתנת להרחבה.
אתגר
קלבואו נבנה מערכת פקודות לעורך טקסט באמצעות תבנית Command. תיצרו מערכת שבה פעולות עריכה כמו הוספה ומחיקה של טקסט מקופלות כאובייקטים של פקודה, כך שהעורך יוכל לבצע אותן דרך ממשק אחיד.
תארגנו את הקוד שלכם בשלושה קבצים:
Command.cs: הגדירו ממשקICommandבמרחב השמותTextEditorעם שיטה אחת,Execute(). לאחר מכן צרו שתי מחלקות פקודה קונקרטיות:InsertCommand- מקבלתDocumentומחרוזתtextבבנאי שלה. בעת ביצוע הפקודה, היא מוסיפה את הטקסט למסמך ומדפיסהInserted: {text}DeleteCommand- מקבלתDocumentו-int countבבנאי שלה. בעת ביצוע הפקודה, היא מסירה אתcountהתווים האחרונים מהמסמך ומדפיסהDeleted: {count} characters
Document.cs: צרו מחלקתDocumentבאותו מרחב שמות. זהו המקבל שלכם — האובייקט שמבצע בפועל את פעולות הטקסט. צריכות להיות לה:- שדה מחרוזת פרטי לאחסון התוכן (אתחלו אותו למחרוזת ריקה)
- שיטה
Append(string text)שמוסיפה טקסט לתוכן - שיטה
RemoveLast(int count)שמסירה אתcountהתווים האחרונים (אם count גדול מאורך התוכן, נקו את כל התוכן) - שיטה
GetContent()שמחזירה את התוכן הנוכחי
Program.cs: צרו מחלקתEditorשמשמשת כמפעיל. צריכה להיות לה שיטהSetCommand(ICommand command)ושיטהExecuteCommand()שמריצה את הפקודה הנוכחית. בקוד הראשי שלכם, הדגימו את תבנית הפקודות באמצעות עיבוד סדרה של פעולות עריכה.
תקבלו את הקלטים הבאים:
- מספר הפקודות לביצוע
- עבור כל פקודה: סוג הפקודה (
insertאוdelete) ואחריו הטקסט שיש להוסיף או מספר התווים שיש למחוק
לאחר ביצוע כל הפקודות, הדפיסו את תוכן המסמך הסופי בשורה חדשה בפורמט Content: {content}.
לדוגמה, אם הקלטים הם:
4
insert
Hello
insert
World
delete
3
insert
!הפלט צריך להיות:
Inserted: Hello
Inserted: World
Deleted: 3 characters
Inserted: !
Content: Hello Wo!שימו לב שה-Editor (המפעיל) אינו יודע דבר על עיבוד טקסט — הוא פשוט מבצע כל פקודה שניתנת לו. הפקודות כוללות בתוכן גם את הפעולה וגם את המקבל, וכך קל להוסיף פעולות חדשות כמו ReplaceCommand בלי לשנות את העורך!
נסו בעצמכם
using System;
using TextEditor;
// TODO: צרו את המחלקה Editor (מפעילת הפקודות)
public class Editor
{
// TODO: הוסיפו שדה פרטי לשמירת הפקודה הנוכחית
private ICommand _command;
// TODO: ממשו את המתודה SetCommand
public void SetCommand(ICommand command)
{
// TODO: שמרו את הפקודה
}
// TODO: ממשו את המתודה ExecuteCommand
public void ExecuteCommand()
{
// TODO: בצעו את הפקודה הנוכחית
}
}
class Program
{
public static void Main(string[] args)
{
// קראו את מספר הפקודות
int n = Convert.ToInt32(Console.ReadLine());
// צרו את המסמך (מקבל הפקודות)
Document document = new Document();
// צרו את העורך (מפעיל הפקודות)
Editor editor = new Editor();
// TODO: עבדו כל פקודה
for (int i = 0; i < n; i++)
{
string commandType = Console.ReadLine();
// TODO: על סמך commandType, צרו את הפקודה המתאימה
// - אם "insert": קראו את הטקסט וצרו InsertCommand
// - אם "delete": קראו את הכמות וצרו DeleteCommand
// לאחר מכן הגדירו את הפקודה ב-editor והפעילו אותה
}
// TODO: הדפיסו את התוכן הסופי בפורמט "Content: {content}"
}
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים
קבצים חיצונייםמרחבי שמות והנחיותמבוא למחלקות ולאובייקטיםמילת המפתח 'this'מתודות ופרמטריםשדות לעומת מאפייניםבנאיםאתחול אובייקטיםסיכום – מחשבון פשוט4ירושה
תחביר ירושה בסיסית (:)מילת המפתח 'base'מילות המפתח Virtual ו-Overrideמחלקות Sealedמחלקת הבסיס 'object'סיכום – היררכיית עובדים7תכונות מתקדמות
העמסת אופרטוריםאינדקסרים (this[])דריסת ToString()מתודות הרחבהסיכום – רשימה מותאמת אישית2מאפיינים וחברים סטטיים
מאפיינים במימוש אוטומטימאפיינים לקריאה בלבד ולכתיבה בלבדשדות ומתודות סטטייםמחלקות סטטיותחברים עם גוף ביטוי5פולימורפיזם וממשקים
פולימורפיזם בזמן קומפילציה לעומת בזמן ריצהממשק לעומת מחלקה מופשטתממשקים מרוביםממשקים מפורשיםהמרה כלפי מעלה והמרה כלפי מטהחזרה: מחשבון צורות8מושגים מתקדמים בתכנות מונחה עצמים
קומפוזיציה במקום ירושהGenerics (מחלקות ומתודות)נציגים ואירועיםמאפיינים ורפלקציהIDisposable והצהרת usingיסודות הזרקת תלויותתרגלו בעצמכם: קומפיילר C# אונליין