نمط Observer
جزء من قسم البرمجة كائنية التوجه في رحلة PHP على Coddy. الدرس 73 من 91.
إن نمط المراقب هو نمط تصميم سلوكي ينشئ علاقة من واحد إلى متعدد بين الكائنات. عندما يغيّر كائن واحد (أي الموضوع) حالته، يتم تلقائيًا إخطار جميع توابعه (أي observers) وتحديثها.
فكّر في الاشتراك في newsletter. newsletter هو الموضوع، والمشتركون هم observers. عند نشر إصدار جديد، يتلقّى جميع المشتركون هذا الإصدار تلقائيًا من دون أن تحتاج newsletter إلى معرفة تفاصيل كل مشترك.
إليك تطبيقًا أساسيًا بلغة 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. وهذا يجعل إضافة أنواع جديدة من المراقبين أمرًا سهلًا دون تعديل الشيفرة الموجودة.
التحدي
سهللنَبْنِ نظامًا للتنبيه عند تغيّر سعر السهم باستخدام نمط Observer. عندما يتغير سعر سهم، ينبغي إخطار جميع المستثمرين المسجلين تلقائيًا: وهذا تطبيق مثالي واقعي لهذا النمط.
ستنظّم شيفرتك عبر أربعة ملفات:
Observer.php: عرّف واجهةObserverذات method واحدة هيupdate(string $stockName, float $price): void. وهذا هو العقد الذي يجب على جميع observers اتباعه لتلقي notifications.Investors.php: ضمّن ملف Observer وأنشئ classين ينفّذان الواجهة:EmailInvestor: يستقبل عنوان email في constructor الخاص به (استخدم constructor promotion). تطبع methodupdate()الخاصة به"Email to [email]: [stockName] is now $[price]"SmsInvestor: يستقبل رقم phone في constructor الخاص به. تطبع methodupdate()الخاصة به"SMS to [phone]: [stockName] is now $[price]"
Stock.php: ضمّن ملف Observer وأنشئ class باسمStockيتصرف بصفته subject. يجب أن يقوم سهمك بما يلي:- تخزين اسم السهم والسعر الحالي (استخدم constructor promotion للاسم، وInitialize السعر إلى
0.0) - Maintain مصفوفة خاصة من observers
- امتلاك method باسم
subscribe(Observer $observer): voidلإضافة observers - امتلاك method باسم
setPrice(float $price): voidتحدّث السعر وتُخطِر all observers
update()لكل observer مع اسم السهم والسعر الجديد.- تخزين اسم السهم والسعر الحالي (استخدم constructor promotion للاسم، وInitialize السعر إلى
main.php: ضمّن ملفَي Investors وStock. ستتلقى ثلاثة inputs: اسم سهم، وعنوان email، ورقم phone.أنشئ
Stockبالاسم given. سجّلEmailInvestorباستخدام عنوان email وSmsInvestorباستخدام رقم phone.اضبط سعر السهم على
150.50لإطلاق notifications إلى جميع المشتركين.
يحافظ نمط Observer على فصل class Stock عن methods الإخطار المحددة. ويمكنك بسهولة إضافة PushInvestor أو SlackInvestor لاحقًا من دون تعديل class Stock على الإطلاق.
جرّب بنفسك
<?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 لتفعيل الإشعارات
?>يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
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 عبر الإنترنت