תבנית המצב
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Dart של Coddy. שיעור 98 מתוך 110.
תבנית State מאפשרת לאובייקט לשנות את התנהגותו כאשר מצבו הפנימי משתנה, כך שנראה כאילו האובייקט שינה את המחלקה שלו. במקום להשתמש בתנאים מורכבים כדי לטפל במצבים שונים, עוטפים כל מצב במחלקה משלו ומאצילים את ההתנהגות לאובייקט המצב הנוכחי.
התבנית מורכבת מ־הקשר שמחזיק הפניה למצב הנוכחי, וממחלקות מצב שמגדירות התנהגות לכל מצב אפשרי. כאשר המצב של ההקשר משתנה, ההתנהגות שלו משתנה אוטומטית:
abstract class DocumentState {
void publish(Document doc);
String getStatus();
}
class DraftState implements DocumentState {
@override
void publish(Document doc) {
print('Moving to review...');
doc.setState(ReviewState());
}
@override
String getStatus() => 'Draft';
}
class ReviewState implements DocumentState {
@override
void publish(Document doc) {
print('Document published!');
doc.setState(PublishedState());
}
@override
String getStatus() => 'Under Review';
}
class PublishedState implements DocumentState {
@override
void publish(Document doc) {
print('Already published.');
}
@override
String getStatus() => 'Published';
}
class Document {
DocumentState _state = DraftState();
void setState(DocumentState state) => _state = state;
void publish() => _state.publish(this);
String getStatus() => _state.getStatus();
}
void main() {
var doc = Document();
print(doc.getStatus()); // טיוטה
doc.publish(); // מועבר לבדיקה...
print(doc.getStatus()); // בבדיקה
doc.publish(); // המסמך פורסם!
print(doc.getStatus()); // פורסם
}כל מחלקת מצב מטפלת בפעולה publish() באופן שונה, ויכולה להעביר את המסמך למצב הבא. מחלקת Document אינה צריכה תנאים כדי לבדוק מהו המצב הנוכחי שלה — היא פשוט מעבירה את הטיפול לאובייקט המצב הפעיל כרגע. התבנית הזאת אידיאלית לאובייקטים בעלי מצבי פעולה מובחנים, כמו עיבוד הזמנות, נגני מדיה או מערכות זרימת עבודה.
אתגר
קלבוא נבנה מערכת רמזורים באמצעות תבנית המצב! ניצור רמזור שעובר בין מצבים שונים, כאשר כל מצב קובע את צבע האור וכיצד הוא עובר למצב הבא.
נארגן את הקוד בשני קבצים:
traffic_light.dart: קובץ זה מכיל את מחלקות המצבים ואת ההקשר של הרמזור. יש ליצור מחלקה מופשטתTrafficLightStateעם שתי מתודות:String getColor()שמחזירה את צבע האור הנוכחי, ו-void next(TrafficLight light)שמעבירה את הרמזור למצב הבא. יש לממש שלוש מחלקות מצב קונקרטיות:RedState- מחזירה אתREDכצבע שלה ומדפיסה אתStop! Light is red.כאשר קוראים ל-next(), ואז עוברת ל-GreenStateGreenState- מחזירה אתGREENכצבע שלה ומדפיסה אתGo! Light is green.כאשר קוראים ל-next(), ואז עוברת ל-YellowStateYellowState- מחזירה אתYELLOWכצבע שלה ומדפיסה אתCaution! Light is yellow.כאשר קוראים ל-next(), ואז עוברת ל-RedState
TrafficLight(ההקשר) שמתחילה במצבRedState. צריכה להיות לה מתודתsetState()לשינוי המצב הנוכחי, מתודתgetColor()שמעבירה את הקריאה למצב הנוכחי, ומתודתchange()שקוראת ל-next()במצב הנוכחי.main.dart: יש לייבא את קובץ הרמזור ולהדגים את תבנית המצב בפעולה. יש ליצורTrafficLightולהדפיס את צבעו ההתחלתי. לאחר מכן יש לקרוא ל-change()שלוש פעמים, ולהדפיס את הצבע אחרי כל שינוי. כך ניתן לראות את הרמזור עובר בין כל המצבים וחוזר להתחלה.
שימו לב שמחלקת TrafficLight אינה זקוקה לתנאים כדי לקבוע את אופן הפעולה — כל מצב יודע מה צבעו ואיזה מצב מגיע אחריו. הרמזור פשוט מעביר את הקריאה למצב הפעיל כרגע!
הפלט הצפוי:
RED
Stop! Light is red.
GREEN
Go! Light is green.
YELLOW
Caution! Light is yellow.
REDנסו בעצמכם
import 'traffic_light.dart';
void main() {
// TODO: צרו מופע של TrafficLight
// TODO: הדפיסו את הצבע ההתחלתי
// TODO: קראו ל־change() והדפיסו את הצבע (3 פעמים כדי לעבור על כל המצבים)
// שינוי ראשון: RED -> GREEN
// שינוי שני: GREEN -> YELLOW
// שינוי שלישי: YELLOW -> RED
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים
קבצים חיצונייםספריות וייבואמבוא לתכנות מונחה עצמיםמחלקות לעומת אובייקטיםמילת המפתח thisמתודותמשתני מופעיסודות הבנאיחזרה: מחשבון פשוט4בטיחות מפני ערכי null
מבוא לבטיחות מפני ערכי nullניתן להקצאה ל-null לעומת לא ניתן להקצאה ל-nullהאופרטורים ? ו-!מילת המפתח Late ובטיחות מפני ערכי nullאופרטורים מודעי nullבטיחות מפני ערכי null במחלקותסיכום – מערכת פרופילי משתמשים7מחלקות מופשטות וממשקים
מחלקות מופשטותמתודות מופשטותממשקים ב-Dartממשקים משתמעיםמימוש לעומת הורשהממשקים מרוביםחזרה – מחשבון צורות10אוספים וגנריות
סקירה כללית של List, Set ו-Mapאוספים בטוחים מבחינת טיפוסיםמחלקות גנריותמתודות גנריותאילוצים גנרייםIterable ו-Iteratorסיכום – אחסון גנרי13מושגי OOP מתקדמים
הרכבה לעומת הורשהמתודות הרחבהמחלקות קריאותמחלקות אטומות (Dart 3)רשומות (Dart 3)תבניות והתאמה (3.0)טיפוסים מונים עם מתודות16פרויקט: ניהול ספרייה
סקירת הפרויקטמחלקות ספר ומשתמש2בנאים ב-Dart
בנאי ברירת מחדלבנאים בעלי שםרשימות אתחולבנאים קבועיםבנאי Factoryבנאים מפניםסיכום - בונה צורות5כימוס
חברים ציבוריים לעומת פרטייםמוסכמת הקידומת _פרטיות ברמת הספרייההעמקה ב-getters וב-settersהסתרת מידעסיכום – רשומות תלמידים8מיקסינים
מבוא למיקסיניםיצירת מיקסיניםשימוש במספר מיקסיניםמילת המפתח on במיקסיניםמיקסינים לעומת ירושהמיקסינים לעומת ממשקסיכום – מערכת בעלי החיים3מאפייני מחלקה
איברי מופע לעומת איברים סטטייםשדות final ו-constמשתני lateמתודות ושדות סטטייםGetters ו-Settersסיכום – מנהל חשבונות בנק6ירושה
ירושה בסיסיתמילת המפתח superדריסת מתודותהאנוטציה @overrideמילת המפתח final למחלקהבנאים וירושהסיכום – היררכיית עובדים9פולימורפיזם
יסודות הפולימורפיזםפולימורפיזם באמצעות ממשקיםבדיקת טיפוסים בזמן ריצההאופרטורים is ו-asמילת המפתח קווריאנטיסיכום — מעבד תשלומים12תכנות מונחה עצמים אסינכרוני
Future-ים ו-async/awaitיסודות ה-Streamsבקרי Streamsבנאים אסינכרונייםאסינכרוניות במתודות של מחלקהסיכום - אחזור נתונים15תבניות עיצוב חלק 2
תבנית הפקודהתבנית המתאםתבנית העיצובןתבנית שיטת התבניתתבנית המצבתבנית המרוכבתבנית המאגרתרגלו בעצמכם: קומפיילר Dart אונליין