Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

نمط الحالة

جزء من قسم البرمجة كائنية التوجه في رحلة 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 المتشعبة، ويجعل إضافة حالات جديدة أمرًا مباشرًا. تكون كل فئة حالة مسؤولة عن معرفة الحالة التي تأتي بعدها، مما يحافظ على تنظيم المنطق وسهولة صيانته.

challenge icon

التحدي

سهل

لنبنِ نظامًا لسير عمل المستندات باستخدام نمط الحالة. تمر المستندات في نظام إدارة المحتوى بمراحل مختلفة: المسودة والمراجعة والنشر، ولكل مرحلة قواعدها الخاصة بشأن الإجراءات المسموح بها وما يحدث عند محاولة تقديم المستند إلى المرحلة التالية.

ستنظم الشيفرة عبر أربعة ملفات:

  • 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(): string Delegate إلى طريقة publish() الخاصة بالحالة الحالية
    • Have طريقة getStatus(): string تُرجع حالة الحالة الحالية
  • 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]"

?>
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم PHP عبر الإنترنت