نمط الحالة
جزء من قسم البرمجة كائنية التوجه في رحلة PHP على Coddy. الدرس 79 من 91.
إن نمط الحالة هو نمط تصميم سلوكي يتيح لكائن تغيير سلوكه عندما تتغير حالته الداخلية. وبدلاً من استخدام شروط معقدة للتعامل مع الحالات المختلفة، فإنك تغلّف كل حالة في فئتها الخاصة وتفوّض السلوك إلى كائن الحالة الحالية.
فكّر في آلة بيع آلية. فهي تتصرّف بشكل مختلف اعتمادًا على ما إذا كانت تنتظر المال، أو قد تلقّت الدفع، أو تقوم بصرف منتج.
each حالة لها قواعدها الخاصة بشأن الإجراءات المسموح بها. يحاكي نمط الحالة ذلك من خلال إنشاء class لكل حالة.
<?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يلغي نمط الحالة عبارات switch المتشعبة، ويجعل إضافة حالات جديدة أمرًا مباشرًا. تكون كل فئة حالة مسؤولة عن معرفة الحالة التي تأتي بعدها، مما يحافظ على تنظيم المنطق وسهولة صيانته.
التحدي
سهللنبنِ نظامًا لسير عمل المستندات باستخدام نمط الحالة. تمر المستندات في نظام إدارة المحتوى بمراحل مختلفة: المسودة والمراجعة والنشر، ولكل مرحلة قواعدها الخاصة بشأن الإجراءات المسموح بها وما يحدث عند محاولة تقديم المستند إلى المرحلة التالية.
ستنظم الشيفرة عبر أربعة ملفات:
DocumentState.php: Define واجهةDocumentStateالتي تحدد العقد لجميع حالات المستند. يجب أن تحتوي على طريقتين:publish(Document $document): stringلمحاولة تقديم المستند إلى المرحلة التالية، وgetStatus(): stringلإرجاع اسم الحالة الحالية.States.php: تضمين واجهة DocumentState وإنشاء ثلاث فئات للحالات Implement لها: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تعمل بوصفها السياق. يجب أن يقوم المستند بما يلي:- Accept عنوانًا في الباني الخاص به ويبدأ في
DraftState - Have طريقة
setState(DocumentState $state): voidلتغيير الحالة الحالية - Have طريقة
publish(): stringDelegate إلى طريقةpublish()الخاصة بالحالة الحالية - Have طريقة
getStatus(): stringتُرجع حالة الحالة الحالية
- Accept عنوانًا في الباني الخاص به ويبدأ في
main.php: تضمين ملف Document. ستتلقى مُدخلًا واحدًا: عدد مرات استدعاءpublish()على المستند.Create كائن
Documentبالعنوان"My Article". أولًا، Print الحالة الأولية. ثم استدعِ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
الملفات الخارجيةمقدمة في OOPClasses مقابل Objectsالكلمة المفتاحية $thisالـ Methodsالـ Propertiesالمنشئ (__construct)الهادم (__destruct)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسيةالكلمة المفتاحية parent::إعادة تعريف الدوال (Method Overriding)الكلمة المفتاحية finalالأصناف المجردة (Abstract Classes)مراجعة - هيكلية الموظفين7التغليف (Encapsulation)
العام، المحمي، الخاص (Public, Protected, Private)نظرة متعمقة على معدلات الوصولدوال Getters و Settersإخفاء المعلوماتميزة Constructor Promotion (8.0)مراجعة - نظام سجلات الطلاب10مفاهيم البرمجة كائنية التوجه المتقدمة
التركيب مقابل الوراثةحقن التبعيةالأصناف المجهولةالتعدادات (PHP 8.1)الألياف (PHP 8.1)تعمق في استنساخ الكائناتالمولدات والمكررات13أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المستودع2مساحات الأسماء والتحميل التلقائي
مقدمة إلى مساحات الأسماءالكلمة المفتاحية useمعيار التحميل التلقائي PSR-4أداة التحميل التلقائي Composerملخص - مشروع منظم5الواجهات والعقود
مقدمة إلى الواجهاتتنفيذ الواجهاتتنفيذ واجهات متعددةالواجهة مقابل الـ Abstract Classالـ Type Hinting مع الواجهاتملخص - حاسبة الأشكال8الميثودات السحرية (Magic Methods)
مقدمة في الميثودات السحرية__toString و __debugInfo__get, __set, __isset, __unset__call و __callStatic__clone واستنساخ الكائنات__serialize و __unserializeمراجعة - Custom Collection3خصائص الكلاس
خصائص الـ Instance مقابل الخصائص الساكنة (Static)الثوابت في الكلاساتالدوال والخصائص الساكنة (Static)الخصائص الخاصة (Private) والمحمية (Protected)خصائص للقراءة فقط (Readonly) في PHP 8.1مراجعة - مدير الحساب البنكي6تعدد الأشكال (Polymorphism)
مراجعة إعادة تعريف الدوال (Method Overriding)تعدد الأشكال عبر الـ Interfacesتلميح النوع (Type Hinting) وأنواع الاتحاد (Union Types)الربط الاستاتيكي المتأخر (Late Static Binding)ملخص - معالج الدفع (Payment Processor)تدرّب بنفسك: مترجم PHP عبر الإنترنت