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

نمط Observer

جزء من قسم Object Oriented Programming في رحلة PHP على Coddy — الدرس 73 من 91.

يُعد نمط المراقب (Observer Pattern) نمط تصميم سلوكي ينشئ علاقة واحد إلى متعدد بين الكائنات. فعندما تتغير حالة كائن واحد (الـ subject)، يتم إخطار جميع التابعين له (الـ observers) وتحديثهم تلقائياً.

فكر في الاشتراك في نشرة إخبارية. النشرة الإخبارية هي الموضوع (subject)، والمشتركون هم المراقبون (observers). عندما يتم نشر إصدار جديد، يتلقاه جميع المشتركين تلقائيًا دون حاجة النشرة الإخبارية لمعرفة تفاصيل كل مشترك.

إليك تنفيذ أساسي بلغة PHP:

<?php
interface Observer {
    public function update(string $message): void;
}

class Newsletter {
    private array $subscribers = [];
    
    public function subscribe(Observer $observer): void {
        $this->subscribers[] = $observer;
    }
    
    public function publish(string $content): void {
        foreach ($this->subscribers as $subscriber) {
            $subscriber->update($content);
        }
    }
}

class EmailSubscriber implements Observer {
    public function __construct(private string $email) {}
    
    public function update(string $message): void {
        echo "Sending to {$this->email}: $message\n";
    }
}

$newsletter = new Newsletter();
$newsletter->subscribe(new EmailSubscriber("alice@example.com"));
$newsletter->subscribe(new EmailSubscriber("bob@example.com"));
$newsletter->publish("New article released!");

المخرجات:

Sending to alice@example.com: New article released!
Sending to bob@example.com: New article released!

يفصل هذا النمط الموضوع عن مراقبيه. لا يحتاج صنف Newsletter إلى معرفة ما إذا كان يقوم بإخطار مشتركي البريد الإلكتروني، أو مشتركي الرسائل القصيرة (SMS)، أو خدمات إشعارات الدفع. إنه ببساطة يستدعي update() على أي شيء ينفذ واجهة Observer. وهذا يجعل إضافة أنواع مراقبين جديدة أمراً سهلاً دون تعديل الكود الحالي.

challenge icon

التحدي

سهل

لنقم ببناء نظام تنبيه لأسعار الأسهم باستخدام نمط المراقب (Observer Pattern). عندما يتغير سعر السهم، يجب إخطار جميع المستثمرين المسجلين تلقائيًا — وهو تطبيق مثالي لهذا النمط في العالم الحقيقي.

ستقوم بتنظيم الكود الخاص بك عبر أربعة ملفات:

  • Observer.php — قم بتعريف واجهة Observer مع طريقة واحدة update(string $stockName, float $price): void. هذا هو العقد الذي يجب على جميع المراقبين اتباعه لتلقي الإشعارات.
  • Investors.php — قم بتضمين ملف Observer وإنشاء فئتين (classes) تنفذان هذه الواجهة:
    • EmailInvestor — يأخذ عنوان بريد إلكتروني في منشئه (استخدم constructor promotion). تقوم طريقة update() الخاصة به بطباعة "Email to {$this->email}: {$stockName} is now \$" . $price . "\n"
    • SmsInvestor — يأخذ رقم هاتف في منشئه. تقوم طريقة update() الخاصة به بطباعة "SMS to {$this->phone}: {$stockName} is now \$" . $price . "\n"
  • Stock.php — قم بتضمين ملف Observer وأنشئ فئة Stock التي تعمل كموضوع (subject). يجب أن يقوم السهم الخاص بك بـ:
    • تخزين اسم السهم والسعر الحالي (استخدم constructor promotion للاسم، وقم بتهيئة السعر إلى 0.0)
    • الاحتفاظ بمصفوفة خاصة (private array) من المراقبين
    • امتلاك طريقة subscribe(Observer $observer): void لإضافة المراقبين
    • امتلاك طريقة setPrice(float $price): void تقوم بتحديث السعر وإخطار جميع المراقبين
    عند إخطار المراقبين، قم باستدعاء طريقة update() لكل مراقب مع اسم السهم والسعر الجديد.
  • main.php — قم بتضمين ملفات Investors و Stock. ستتلقى ثلاثة مدخلات: اسم السهم، وعنوان بريد إلكتروني، ورقم هاتف.

    أنشئ Stock بالاسم المعطى. قم بتسجيل EmailInvestor بعنوان البريد الإلكتروني و SmsInvestor برقم الهاتف.

    قم بتعيين سعر السهم إلى 150.50 لتفعيل الإشعارات لجميع المشتركين.

يحافظ نمط المراقب (Observer Pattern) على فصل فئة Stock الخاصة بك عن طرق الإخطار المحددة. يمكنك بسهولة إضافة PushInvestor أو SlackInvestor لاحقًا دون تعديل فئة Stock على الإطلاق.

ورقة مرجعية

يُنشئ نمط المراقب (Observer Pattern) علاقة واحد إلى متعدد حيث يقوم الموضوع (subject) بإخطار جميع المراقبين تلقائيًا عند تغير حالته.

المكونات الرئيسية:

  • واجهة المراقب (Observer Interface) — تحدد العقد للمراقبين باستخدام طريقة update()
  • الموضوع (Subject) — يحتفظ بقائمة من المراقبين ويقوم بإخطارهم بتغيرات الحالة
  • المراقبون الملموسون (Concrete Observers) — يقومون بتنفيذ واجهة المراقب لتلقي الإشعارات

التنفيذ الأساسي:

<?php
interface Observer {
    public function update(string $message): void;
}

class Subject {
    private array $observers = [];
    
    public function subscribe(Observer $observer): void {
        $this->observers[] = $observer;
    }
    
    public function notify(string $data): void {
        foreach ($this->observers as $observer) {
            $observer->update($data);
        }
    }
}

class ConcreteObserver implements Observer {
    public function __construct(private string $name) {}
    
    public function update(string $message): void {
        echo "Notifying {$this->name}: $message\n";
    }
}

$subject = new Subject();
$subject->subscribe(new ConcreteObserver("Observer1"));
$subject->subscribe(new ConcreteObserver("Observer2"));
$subject->notify("State changed!");

الفوائد:

  • يفصل الموضوع عن مراقبيه (Decoupling)
  • من السهل إضافة أنواع مراقبين جديدة دون تعديل الكود الحالي
  • يدعم الاشتراك وإلغاء الاشتراك الديناميكي

جرّب بنفسك

<?php
require_once 'Investors.php';
require_once 'Stock.php';

// قراءة المدخلات
$stockName = trim(fgets(STDIN));
$email = trim(fgets(STDIN));
$phone = trim(fgets(STDIN));

// TODO: قم بإنشاء سهم (Stock) بالاسم المعطى

// TODO: قم بالاشتراك كمستثمر عبر البريد الإلكتروني (EmailInvestor) باستخدام عنوان البريد الإلكتروني

// TODO: قم بالاشتراك كمستثمر عبر الرسائل النصية (SmsInvestor) باستخدام رقم الهاتف

// TODO: قم بتعيين سعر السهم إلى 150.50 لتفعيل الإشعارات
?>
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming