תבנית המצב
חלק מהיחידה תכנות מונחה עצמים במסלול ה-PHP של Coddy. שיעור 79 מתוך 91.
תבנית State היא תבנית עיצוב התנהגותית המאפשרת לאובייקט לשנות את ההתנהגות שלו כאשר המצב הפנימי שלו משתנה. במקום להשתמש בתנאים מורכבים כדי לטפל במצבים שונים, עוטפים כל מצב במחלקה משלו ומעבירים את ההתנהגות לאובייקט המצב הנוכחי.
חשבו על מכונת ממכר אוטומטית. היא מתנהגת באופן שונה בהתאם לשאלה אם היא ממתינה לכסף, קיבלה תשלום או מוציאה מוצר.
לכל מצב יש כללים משלו לגבי הפעולות המותרות. תבנית המצב ממדלת זאת באמצעות יצירת מחלקה לכל מצב.
<?php
interface OrderState {
public function proceed(Order $order): string;
public function getStatus(): string;
}
class PendingState implements OrderState {
public function proceed(Order $order): string {
$order->setState(new ProcessingState());
return "Order moved to processing";
}
public function getStatus(): string {
return "Pending";
}
}
class ProcessingState implements OrderState {
public function proceed(Order $order): string {
$order->setState(new ShippedState());
return "Order has been shipped";
}
public function getStatus(): string {
return "Processing";
}
}
class ShippedState implements OrderState {
public function proceed(Order $order): string {
return "Order already shipped";
}
public function getStatus(): string {
return "Shipped";
}
}
מחלקת ההקשר מחזיקה הפניה למצב הנוכחי ומעבירה אליו קריאות:
<?php
class Order {
private OrderState $state;
public function __construct() {
$this->state = new PendingState();
}
public function setState(OrderState $state): void {
$this->state = $state;
}
public function proceed(): string {
return $this->state->proceed($this);
}
public function getStatus(): string {
return $this->state->getStatus();
}
}
$order = new Order();
echo $order->getStatus() . "\n";
echo $order->proceed() . "\n";
echo $order->getStatus() . "\n";
echo $order->proceed() . "\n";
echo $order->getStatus();
פלט:
Pending
Order moved to processing
Processing
Order has been shipped
Shippedתבנית State מבטלת הצהרות switch מסורבלות ומקלה על הוספת מצבים חדשים. כל מחלקת מצב אחראית לדעת איזה מצב מגיע אחריה, וכך הלוגיקה נשארת מאורגנת וניתנת לתחזוקה.
אתגר
קלבוא נבנה מערכת לניהול תהליך עבודה של מסמכים באמצעות תבנית State. מסמכים במערכת לניהול תוכן עוברים שלבים שונים — טיוטה, סקירה ופרסום — ולכל שלב יש כללים משלו לגבי הפעולות המותרות ומה קורה כשמנסים לקדם את המסמך.
תארגן את הקוד שלך בארבעה קבצים:
DocumentState.php: הגדר ממשקDocumentStateשמגדיר את החוזה לכל מצבי המסמך. צריכות להיות בו שתי פונקציות:publish(Document $document): stringלניסיון לקדם את המסמך, ו-getStatus(): stringלהחזרת שם המצב הנוכחי.States.php: כלול את הממשק DocumentState וצור שלוש מחלקות מצבים שמממשות אותו:DraftState: כאשר קוראים ל-publish(), המסמך עובר למצבReviewStateומוחזר"Document sent for review". הסטטוס שלו הוא"Draft".ReviewState: כאשר קוראים ל-publish(), המסמך עובר למצבPublishedStateומוחזר"Document published". הסטטוס שלו הוא"Under Review".PublishedState: כאשר קוראים ל-publish(), מוחזר"Document is already published"בלי לשנות את המצב. הסטטוס שלו הוא"Published".
Document.php: כלול את הקובץ States וצור מחלקהDocumentשמתפקדת כהקשר. המסמך שלך צריך:- לקבל כותרת בבנאי ולהתחיל במצב
DraftState - להכיל פונקציה
setState(DocumentState $state): voidלשינוי המצב הנוכחי - להכיל פונקציה
publish(): stringשמעבירה את הקריאה לפונקציהpublish()של המצב הנוכחי - להכיל פונקציה
getStatus(): stringשמחזירה את הסטטוס של המצב הנוכחי
- לקבל כותרת בבנאי ולהתחיל במצב
main.php: כלול את קובץ Document. יתקבל קלט אחד: מספר הפעמים לקרוא ל-publish()עבור המסמך.צור
Documentעם הכותרת"My Article". תחילה, הדפס את הסטטוס ההתחלתי. לאחר מכן, קרא ל-publish()את מספר הפעמים שצוין, והדפס את התוצאה של כל קריאה בשורה חדשה. לבסוף, הדפס את הסטטוס הסופי.פורמט הפלט:
Status: [initial status] [publish result 1] [publish result 2] ... Status: [final status]
כל מחלקת מצב יודעת מהו המצב הבא בתהליך העבודה, וכך לוגיקת המעבר נשארת מסודרת וקלה לשינוי. הוספת מצב חדש כמו "Archived" תצריך רק יצירת מחלקה חדשה ועדכון המעברים הרלוונטיים.
נסו בעצמכם
<?php
require_once 'Document.php';
// קרא את הקלט - מספר הפעמים שיש לקרוא ל-publish()
$n = intval(trim(fgets(STDIN)));
// TODO: צור Document עם הכותרת "My Article"
// TODO: הדפס את המצב ההתחלתי בפורמט: "Status: [status]"
// TODO: קרא ל-publish() n פעמים, והדפס כל תוצאה בשורה חדשה
// TODO: הדפס את המצב הסופי בפורמט: "Status: [status]"
?>השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים (OOP)
קבצים חיצונייםמבוא ל-OOPמחלקות לעומת אובייקטיםמילת המפתח $thisמתודותמאפייניםבנאי (__construct)הורס (__destruct)חזרה: מחשבון פשוט4ירושה
ירושה בסיסיתמילת המפתח parent::דריסת מתודותמילת המפתח finalמחלקות מופשטותסיכום – היררכיית עובדים7כימוס
ציבורי, מוגן, פרטימפרטי גישה לעומקמתודות גישה ועדכוןהסתרת מידעקידום פרמטרים בבנאי (8.0)סיכום – מערכת רישומי תלמידים10מושגי OOP מתקדמים
הרכבה לעומת ירושההזרקת תלויותמחלקות אנונימיותטיפוסי Enum (PHP 8.1)סיבים (PHP 8.1)העמקה בשכפול אובייקטיםמחוללים ואיטרטורים13תבניות עיצוב חלק 2
תבנית הפקודהתבנית המתאםתבנית העיצובןתבנית שיטת התבניתתבנית המצבתבנית המורכבתבנית המאגר2מרחבי שמות וטעינה אוטומטית
מבוא למרחבי שמותמילת המפתח useתקן הטעינה האוטומטית PSR-4הטוען האוטומטי של Composerסיכום – פרויקט מאורגן5ממשקים וחוזים
מבוא לממשקיםמימוש ממשקיםמימוש של כמה ממשקיםממשק לעומת מחלקה מופשטתרמזי טיפוסים עם ממשקיםסיכום - מחשבון צורות8מתודות קסם
מבוא למתודות קסם__toString ו־__debugInfo__get, __set, __isset, __unset__call ו־__callStatic__clone ושיבוט אובייקטים__serialize ו־__unserializeסיכום – אוסף מותאם אישית3מאפייני מחלקה
מאפייני מופע לעומת מאפיינים סטטייםקבועים במחלקותמתודות ומאפיינים סטטייםמאפיינים פרטיים ומוגניםמאפיינים לקריאה בלבד (PHP 8.1)סיכום – מנהל חשבון בנק6פולימורפיזם
בחינה מחדש של דריסת מתודותפולימורפיזם באמצעות ממשקיםרמזי טיפוסים וטיפוסי איחודקישור סטטי מאוחרסיכום — מעבד תשלומיםתרגלו בעצמכם: קומפיילר PHP אונליין