תבנית המצב
חלק מהיחידה תכנות מונחה עצמים במסלול ה-C++ של Coddy. שיעור 100 מתוך 104.
תבנית State מאפשרת לאובייקט לשנות את התנהגותו כאשר מצבו הפנימי משתנה, כך שנראה כאילו האובייקט שינה את המחלקה שלו. במקום להשתמש במשפטי תנאי מורכבים כדי לטפל במצבים שונים, עוטפים כל מצב במחלקה נפרדת.
התבנית מורכבת מהקשר שמחזיק הפניה למצב הנוכחי, ממשק מצב שמגדיר התנהגות ספציפית למצב, וממצבים קונקרטיים שמממשים התנהגות עבור כל מצב:
#include <iostream>
#include <memory>
class Document; // הצהרה מוקדמת
// ממשק מצב
class DocumentState {
public:
virtual void publish(Document& doc) = 0;
virtual std::string getName() const = 0;
virtual ~DocumentState() = default;
};
// הקשר
class Document {
std::unique_ptr<DocumentState> state;
public:
Document();
void setState(std::unique_ptr<DocumentState> newState) {
state = std::move(newState);
}
void publish() { state->publish(*this); }
std::string getStateName() const { return state->getName(); }
};
// מצבים קונקרטיים
class Draft : public DocumentState {
public:
void publish(Document& doc) override;
std::string getName() const override { return "Draft"; }
};
class Review : public DocumentState {
public:
void publish(Document& doc) override;
std::string getName() const override { return "Review"; }
};
class Published : public DocumentState {
public:
void publish(Document& doc) override {
std::cout << "Already published\n";
}
std::string getName() const override { return "Published"; }
};
void Draft::publish(Document& doc) {
std::cout << "Moving to review\n";
doc.setState(std::make_unique<Review>());
}
void Review::publish(Document& doc) {
std::cout << "Publishing document\n";
doc.setState(std::make_unique<Published>());
}
Document::Document() : state(std::make_unique<Draft>()) {}כל מצב מטפל בפעולה publish() באופן שונה ואחראי למעבר למצב הבא. ה־Document לא צריך להכיר את לוגיקת המעבר — הוא פשוט מעביר את האחריות למצב הנוכחי. כך נמנעים ממשפטי switch גדולים, והוספת מצבים חדשים נעשית פשוטה.
השתמשו ב-State כאשר ההתנהגות של אובייקט תלויה במידה רבה במצב שלו, ויש לכם הרבה משפטים מותנים שבוחרים פעולה בהתאם למצב האובייקט.
אתגר
קלבואו נבנה בקר רמזור באמצעות תבנית State. תיצור מערכת שבה הרמזור עובר במחזוריות בין מצבים שונים (Red, Yellow, Green), כאשר כל מצב קובע מה קורה כשהרמזור משתנה ואיזו הודעה מוצגת. זוהי דוגמה קלאסית לתבנית State: ההתנהגות של הרמזור תלויה לחלוטין במצב הנוכחי שלו.
תארגן את הקוד שלך בשלושה קבצים:
TrafficLightState.h: הגדר את הממשק של המצבים ואת כל מחלקות המצבים הקונקרטיות.צור מחלקה אבסטרקטית בשם
TrafficLightStateעם:- מתודה וירטואלית טהורה
change(TrafficLight& light)שמטפלת במעבר למצב הבא - מתודה וירטואלית טהורה
getColor()שמחזירה את הצבע הנוכחי כמחרוזת - מתודה וירטואלית טהורה
getAction()שמחזירה את הפעולה שעל הנהגים לבצע (למשל, "Stop", "Caution", "Go") - הורס וירטואלי
ממש שלושה מצבים קונקרטיים:
RedState: הצבע הוא"Red", הפעולה היא"Stop", ועובר ל-GreenYellowState: הצבע הוא"Yellow", הפעולה היא"Caution", ועובר ל-RedGreenState: הצבע הוא"Green", הפעולה היא"Go", ועובר ל-Yellow
תצטרך הצהרה מקדימה עבור
TrafficLight, מכיוון שהמצבים מפנים אליו.- מתודה וירטואלית טהורה
TrafficLight.h: צור את מחלקת ההקשר שמחזיקה את המצב הנוכחי.מחלקת
TrafficLightשלך צריכה להכילstd::unique_ptr<TrafficLightState>ולהתחיל כברירת מחדל במצב Red. ממש את המתודות הבאות:setState(std::unique_ptr<TrafficLightState> newState): משנה את המצב הנוכחיchange(): מעבירה את הטיפול למתודת change של המצב הנוכחיdisplay(): מדפיסה את מצב הרמזור בתבנית:[Color]: [Action]
main.cpp: הדגם את המעבר המחזורי של הרמזור בין המצבים.קרא קלט אחד: מספר שינויי המצב שיש לבצע (מספר שלם).
צור
TrafficLightוהצג את מצבו הראשוני. לאחר מכן בצע את מספר השינויים שצוין, והצג את המצב אחרי כל שינוי.
לדוגמה, עבור הקלט 3:
Red: Stop
Green: Go
Yellow: Caution
Red: Stopעבור הקלט 6:
Red: Stop
Green: Go
Yellow: Caution
Red: Stop
Green: Go
Yellow: Caution
Red: Stopשים לב כיצד הרמזור עובר בין המצבים שלו במחזוריות צפויה: Red → Green → Yellow → Red. כל מצב יודע איזה מצב מגיע אחריו ומטפל במעבר שלו בעצמו. מחלקת TrafficLight אינה זקוקה ללוגיקה מותנית כדי לקבוע מה יקרה. היא פשוט מעבירה את הטיפול למצב שבו היא נמצאת כרגע.
נסו בעצמכם
#include <iostream>
#include "TrafficLight.h"
using namespace std;
int main() {
// קרא את מספר שינויי המצב
int numChanges;
cin >> numChanges;
// TODO: צור אובייקט TrafficLight
// TODO: הצג את המצב ההתחלתי
// TODO: בצע את מספר השינויים שצוין
// לאחר כל שינוי, הצג את המצב הנוכחי
return 0;
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות OOP
קבצים חיצונייםבנייה וקומפילציה של C++קובצי Header וקובצי מקורמרחבי שמות והיקףמבוא ל-OOP ב-C++מחלקות לעומת אובייקטיםמצביע thisמתודות (פונקציות איבר)תכונות (חברי נתונים)יסודות הבנאים וההורסיםסיכום – מחשבון פשוט4מאפייני מחלקה
חברי מופע לעומת חברים סטטייםגטרים וסטריםפונקציות איבר קבועותמילת המפתח mutableמתודות ומשתנים סטטייםפונקציות ומחלקות ידידותסיכום – מנהל חשבונות בנק7ירושה
ירושה בסיסיתרמות גישה בירושהסדר הקריאות לבנאי ולמפרקדריסת מתודותפונקציות וירטואליות ו־VTableירושה מרובהירושה וירטואליתסיכום – היררכיית עובדים2ניהול זיכרון
זיכרון Stack לעומת Heapמצביעים והפניותזיכרון דינמי (new/delete)מצביעים חכמים ב-C++RAII ב-C++סיכום – מנהל מערכים דינמיים5כימוס
מצייני גישה ב-C++מצייני גישה לעומקהסתרת מידעמבנה לעומת מחלקהמחלקות מקוננות ופנימיותסיכום – מערכת רשומות תלמידים8רב־צורתיות
רב־צורתיות בזמן קומפילציה לעומת בזמן ריצההעמסת פונקציותפונקציות וירטואליות — מבט נוסףפונקציות וירטואליות טהורותמחלקות מופשטותתכנון ממשקים ב־C++המרה דינמית ו־RTTIסיכום — מחשבון צורות11מושגי OOP מתקדמים
הרכבה לעומת הורשהMixins באמצעות CRTPאידיום Pimplמחיקת טיפוסיםמחלקות Enum וטיפוסיות חזקהטיפול בחריגות ב-OOPהיררכיות חריגות מותאמות אישית14תבניות עיצוב חלק 2
תבנית הפקודהתבנית המתאםתבנית העיצובתבנית השיטה התבניתיתתבנית המצבתבנית המורכבRAII כתבנית3בנאים והורסים
בנאי ברירת מחדלבנאי עם פרמטריםבנאי העתקהבנאי העברהרשימות אתחול בבנאיםבנאים מאציליםמעמיקים בנושא הורסיםכלל השלושה / החמישה / האפססיכום – מחלקת String6העמסת אופרטורים
מבוא להעמסת אופרטוריםהעמסת אופרטורים אריתמטייםהעמסת אופרטורי השוואהאופרטורי זרםהעמסת אופרטור השמההעמסת האופרטורים [] ו־()אופרטורים להמרת טיפוסיםסיכום – מחלקת Matrix9תבניות
תבניות פונקציהתבניות מחלקההתמחות תבניותתבניות ארגומנטים משתנותיסודות SFINAE ותכונות טיפוסיםסיכום – מכל גנריתרגלו בעצמכם: קומפיילר C++ אונליין