سی ام اس نویسسی ام اس نویسیاد بگیر، سیستم خودتو بنویس
مجلهتماس با ما
سی ام اس نویسسی ام اس نویس

CMS نویس مرجعی برای آموزش برنامه نویسی وب و PHP، توسعه افزونه‌های اختصاصی وردپرس، طراحی ورک‌فلو و اتوماسیون با n8n و توسعه CMS های اختصاصی است. هدف ما ارائه آموزش‌های کاربردی و راهکارهای حرفه‌ای برای توسعه‌دهندگان، کسب‌وکارها و علاقه‌مندان به دنیای برنامه‌نویسی است.

پیوندهای سریع
محصولات فیزیکیمحصولات دیجیتالدوره‌های آموزشی
سی ام اس نویس
مجله سی ام اس نویستماس با ماپنل کاربری
سی ام اس نویس — تمامی حقوق برای سی ام اس نویس محفوظ است. © 1406 تماس با ما09307711941
Observer Pattern در PHP چیست؟ آموزش الگوی Observer با مثال عملی

Observer Pattern در PHP چیست؟ آموزش الگوی Observer با مثال عملی

ممدیر کل سیستم ۲۱ شهریور ۱۴۰۵ ۲۰ دقیقه مطالعه ۱ بازدید
Observer Pattern در PHP چیست؟ آموزش الگوی Observer با مثال عملی

در پروژه‌های واقعی PHP معمولاً اتفاقی در سیستم رخ می‌دهد که بعد از آن باید چند عملیات مختلف انجام شود.

برای مثال تصور کنید کاربر جدیدی در سایت ثبت‌نام می‌کند. بعد از ثبت‌نام ممکن است بخواهیم:

  • ایمیل خوش‌آمدگویی ارسال کنیم.
  • اطلاعات کاربر را در سیستم CRM ثبت کنیم.
  • یک لاگ ایجاد کنیم.
  • پیامک ارسال کنیم.
  • کاربر را عضو خبرنامه کنیم.
  • یک اعلان داخل پنل ایجاد کنیم.
  • اطلاعات ثبت‌نام را برای یک سرویس خارجی ارسال کنیم.

یک روش ساده این است که تمام این عملیات را مستقیماً داخل متد ثبت‌نام بنویسیم.

اما با بزرگ‌تر شدن پروژه، این روش باعث افزایش وابستگی بین کلاس‌ها می‌شود.

اینجاست که Observer Pattern در PHP می‌تواند راه‌حل مناسبی باشد.

Observer یک الگوی طراحی رفتاری است که اجازه می‌دهد یک شیء به نام Subject تغییر یا رخداد خاصی را اعلام کند و چند شیء دیگر به نام Observer به آن واکنش نشان دهند. این الگو یک رابطه یک‌به‌چند ایجاد می‌کند و هدف آن کاهش وابستگی مستقیم بین این بخش‌هاست.


Observer Pattern چیست؟

Observer Pattern یا الگوی مشاهده‌گر روشی برای ایجاد ارتباط بین یک شیء اصلی و چند شیء وابسته است.

در این الگو، یک شیء اصلی که معمولاً آن را Subject می‌نامیم، لیستی از Observerها را نگهداری می‌کند.

هر زمان وضعیت Subject تغییر کند یا رویداد مشخصی اتفاق بیفتد، Subject به Observerهای ثبت‌شده اطلاع می‌دهد.

ساختار کلی را می‌توان این‌طور تصور کرد:


                    Subject

                       │

          ┌────────────┼────────────┐

          │            │            │

          ▼            ▼            ▼

       Observer 1   Observer 2   Observer 3

          │            │            │

          ▼            ▼            ▼

        Email         SMS          Log

در این ساختار، Subject لازم نیست بداند هر Observer دقیقاً چه کاری انجام می‌دهد.

فقط می‌داند که Observer می‌تواند به تغییرات واکنش نشان دهد.


یک مثال ساده از دنیای واقعی

فرض کنید در یک فروشگاه اینترنتی، سفارش جدیدی ثبت می‌شود.

بعد از ثبت سفارش باید چند کار انجام شود:

  1. ارسال ایمیل برای مشتری
  2. ارسال پیامک
  3. ثبت لاگ
  4. اطلاع به انبار
  5. اطلاع به سیستم حسابداری

اگر همه این موارد را مستقیماً داخل کلاس OrderService قرار دهیم، کلاس به مرور بزرگ و وابسته می‌شود.

مثلاً:


<?php



final class OrderService

{

    public function create(array $data): void

    {

        // ایجاد سفارش



        $this->sendEmail();

        $this->sendSms();

        $this->updateInventory();

        $this->createAccountingRecord();

        $this->writeLog();

    }

}

در ظاهر کد ساده است، اما مشکل زمانی شروع می‌شود که تعداد عملیات افزایش پیدا کند.

مثلاً چند ماه بعد بخواهیم:

  • اطلاعات سفارش را به CRM بفرستیم.
  • برای مدیر اعلان ایجاد کنیم.
  • اطلاعات را به یک API خارجی ارسال کنیم.
  • سیستم امتیازدهی مشتری را به‌روزرسانی کنیم.

در این حالت OrderService به تعداد زیادی سرویس مختلف وابسته خواهد شد.


# مشکل Coupling چیست؟

یکی از مفاهیم مهم در طراحی نرم‌افزار، Coupling یا وابستگی بین بخش‌های مختلف است.

اگر یک کلاس مستقیماً تعداد زیادی کلاس دیگر را صدا بزند، تغییر یک بخش می‌تواند روی بخش‌های دیگر تأثیر بگذارد.

مثلاً:


<?php



final class RegisterUser

{

    public function register(string $email): void

    {

        // ذخیره کاربر



        $mailer = new Mailer();

        $mailer->sendWelcome($email);



        $sms = new SmsService();

        $sms->sendWelcome($email);



        $crm = new CrmService();

        $crm->createCustomer($email);



        $logger = new Logger();

        $logger->info('User registered');

    }

}

کلاس RegisterUser حالا مسئولیت‌های زیادی دارد.

اگر سرویس SMS تغییر کند، ممکن است مجبور شویم همین کلاس را تغییر دهیم.

اگر CRM تغییر کند، دوباره همین کلاس باید تغییر کند.

اگر یک سرویس جدید اضافه شود، باز هم باید این کلاس را تغییر دهیم.

Observer Pattern تلاش می‌کند این وابستگی را کاهش دهد.


# ساختار Observer Pattern

Observer Pattern معمولاً شامل دو نقش اصلی است:

Subject

Subject همان شیء اصلی است که وضعیت یا رخداد آن برای سایر اشیا اهمیت دارد.

وظایف Subject معمولاً شامل موارد زیر است:

  • نگهداری Observerها
  • اضافه کردن Observer
  • حذف Observer
  • اطلاع دادن به Observerها

Observer

Observer شیئی است که منتظر رخداد یا تغییر در Subject است.

وقتی Subject تغییر می‌کند، Observer متوجه این تغییر شده و عملیات خودش را انجام می‌دهد.


# پیاده‌سازی Observer Pattern در PHP

بیایید ابتدا یک مثال ساده و قابل فهم بسازیم.

فرض کنید می‌خواهیم وقتی سفارش ثبت شد، چند Observer مطلع شوند.

ابتدا Interface مربوط به Observer را ایجاد می‌کنیم:


<?php



declare(strict_types=1);



interface OrderObserver

{

    public function update(Order $order): void;

}

هر Observer باید متد update را داشته باشد.

حالا کلاس سفارش را می‌سازیم:


<?php



declare(strict_types=1);



final class Order

{

    private array $observers = [];



    public function attach(OrderObserver $observer): void

    {

        $this->observers[] = $observer;

    }



    public function detach(OrderObserver $observer): void

    {

        foreach ($this->observers as $key => $item) {

            if ($item === $observer) {

                unset($this->observers[$key]);

            }

        }

    }



    public function create(): void

    {

        echo "Order created\n";



        $this->notify();

    }



    private function notify(): void

    {

        foreach ($this->observers as $observer) {

            $observer->update($this);

        }

    }

}

حالا می‌توانیم Observerهای مختلف ایجاد کنیم.


# ساخت Email Observer


<?php



declare(strict_types=1);



final class EmailOrderObserver implements OrderObserver

{

    public function update(Order $order): void

    {

        echo "Order confirmation email sent.\n";

    }

}

این Observer وظیفه ارسال ایمیل را بر عهده دارد.


# ساخت SMS Observer


<?php



declare(strict_types=1);



final class SmsOrderObserver implements OrderObserver

{

    public function update(Order $order): void

    {

        echo "Order confirmation SMS sent.\n";

    }

}

حالا یک Observer دیگر برای ثبت لاگ ایجاد می‌کنیم.


# ساخت Log Observer


<?php



declare(strict_types=1);



final class LogOrderObserver implements OrderObserver

{

    public function update(Order $order): void

    {

        echo "Order activity logged.\n";

    }

}

اکنون سه Observer داریم:


Order

 │

 ├── EmailOrderObserver

 │

 ├── SmsOrderObserver

 │

 └── LogOrderObserver


# استفاده از Observerها

حالا می‌توانیم Observerها را به سفارش متصل کنیم:


<?php



$order = new Order();



$order->attach(

    new EmailOrderObserver()

);



$order->attach(

    new SmsOrderObserver()

);



$order->attach(

    new LogOrderObserver()

);



$order->create();

وقتی create() اجرا شود، Subject تمام Observerهای ثبت‌شده را مطلع می‌کند.

خروجی می‌تواند چیزی شبیه این باشد:


Order created

Order confirmation email sent.

Order confirmation SMS sent.

Order activity logged.

نکته مهم این است که Order لازم نیست بداند Email Observer دقیقاً چگونه ایمیل ارسال می‌کند.

همین موضوع باعث کاهش وابستگی می‌شود.


# متد attach چیست؟

متد attach برای ثبت یک Observer استفاده می‌شود.

مثلاً:


$order->attach(

    new EmailOrderObserver()

);

با این کار Email Observer در لیست Observerهای Subject قرار می‌گیرد.


# متد detach چیست؟

اگر بخواهیم یک Observer دیگر به تغییرات Subject واکنش نشان ندهد، می‌توانیم آن را حذف کنیم.

مثلاً:


$emailObserver = new EmailOrderObserver();



$order->attach($emailObserver);



$order->detach($emailObserver);

بعد از detach دیگر آن Observer هنگام notify اجرا نمی‌شود.

الگوی رایج Observer نیز معمولاً قابلیت‌های ثبت و حذف Observerها را در Subject در نظر می‌گیرد.


# متد notify چیست؟

notify وظیفه دارد تمام Observerهای ثبت‌شده را اجرا کند.

در ساده‌ترین حالت:


private function notify(): void

{

    foreach ($this->observers as $observer) {

        $observer->update($this);

    }

}

بنابراین جریان اجرا این‌گونه است:


Event happens

     ↓

Subject

     ↓

notify()

     ↓

Observer 1

Observer 2

Observer 3


# استفاده از SplSubject و SplObserver در PHP

یکی از نکات مهم در مورد Observer Pattern این است که PHP خودش Interfaceهای SplSubject و SplObserver را در اختیار ما قرار می‌دهد.

این Interfaceها برای پیاده‌سازی ساختار استاندارد Observer کاربرد دارند.

SplSubject متدهای اصلی زیر را دارد:


attach()

detach()

notify()

و SplObserver متد زیر را تعریف می‌کند:


update()

بنابراین می‌توانیم مثال قبلی را با امکانات داخلی PHP بازنویسی کنیم.


# پیاده‌سازی Observer با SplSubject

ابتدا Subject:


<?php



declare(strict_types=1);



use SplObjectStorage;

use SplObserver;

use SplSubject;



final class Order implements SplSubject

{

    private SplObjectStorage $observers;



    public function __construct()

    {

        $this->observers = new SplObjectStorage();

    }



    public function attach(SplObserver $observer): void

    {

        $this->observers->attach($observer);

    }



    public function detach(SplObserver $observer): void

    {

        $this->observers->detach($observer);

    }



    public function notify(): void

    {

        foreach ($this->observers as $observer) {

            $observer->update($this);

        }

    }



    public function create(): void

    {

        echo "Order created.\n";



        $this->notify();

    }

}

در اینجا SplObjectStorage برای نگهداری مجموعه‌ای از اشیای Observer استفاده شده است.


# ساخت Observer با SplObserver


<?php



declare(strict_types=1);



use SplObserver;

use SplSubject;



final class EmailObserver implements SplObserver

{

    public function update(SplSubject $subject): void

    {

        echo "Email notification sent.\n";

    }

}

Observer دیگری برای لاگ:


<?php



declare(strict_types=1);



use SplObserver;

use SplSubject;



final class LogObserver implements SplObserver

{

    public function update(SplSubject $subject): void

    {

        echo "Order logged.\n";

    }

}

حالا:


<?php



$order = new Order();



$order->attach(

    new EmailObserver()

);



$order->attach(

    new LogObserver()

);



$order->create();

این همان مفهوم اصلی Observer Pattern را با Interfaceهای استاندارد PHP پیاده‌سازی می‌کند.


# Observer Pattern در پروژه‌های واقعی چه کاربردی دارد؟

Observer فقط یک مثال آموزشی نیست و در سیستم‌های واقعی کاربردهای زیادی دارد.

برای مثال:

ثبت‌نام کاربر

بعد از ثبت‌نام:


User Registered

       │

       ├── Send Welcome Email

       ├── Send SMS

       ├── Create CRM Record

       └── Write Log

ثبت سفارش


Order Created

       │

       ├── Send Email

       ├── Update Inventory

       ├── Create Invoice

       ├── Notify Admin

       └── Create Log

پرداخت موفق


Payment Successful

       │

       ├── Activate Service

       ├── Send Receipt

       ├── Update Wallet

       ├── Notify User

       └── Create Transaction Log

تغییر قیمت محصول


Product Price Changed

       │

       ├── Update Cache

       ├── Notify Admin

       ├── Update Search Index

       └── Write Audit Log

در تمام این موارد، یک رخداد می‌تواند چند واکنش مستقل داشته باشد.


# Observer Pattern و Publish/Subscribe

یکی از اصطلاحاتی که در بحث Observer زیاد می‌بینیم، Publish/Subscribe یا Pub/Sub است.

در این مدل:

  • Publisher رخداد را منتشر می‌کند.
  • Subscriber به رخداد گوش می‌دهد.
  • Publisher لازم نیست بداند چه Subscriberهایی وجود دارند.

برای مثال:


Publisher

    │

    │ UserRegistered

    ▼

Event

    │

    ├── Email Listener

    ├── SMS Listener

    ├── CRM Listener

    └── Analytics Listener

این ساختار یکی از مفاهیم مهم معماری Event-Driven است.


# تفاوت Observer Pattern و Event System

Observer Pattern و سیستم Event/Listener بسیار به یکدیگر نزدیک هستند، اما دقیقاً یکی نیستند.

در Observer کلاس Subject معمولاً Observerهای خود را نگهداری می‌کند.

اما در یک Event Dispatcher، معمولاً یک سیستم مرکزی وظیفه مدیریت Eventها و Listenerها را بر عهده دارد.

مثلاً:


Event Dispatcher

       │

       ├── Listener 1

       ├── Listener 2

       └── Listener 3

در نتیجه Event Dispatcher را می‌توان نوعی پیاده‌سازی گسترده‌تر و مرکزی‌تر از ایده Publish/Subscribe دانست.


# Observer Pattern در Laravel

اگر با Laravel کار کرده باشید، احتمالاً با مفاهیمی مانند:

  • Event
  • Listener
  • Model Observer

آشنا هستید.

Laravel از معماری Event و Listener برای جدا کردن رخدادها از واکنش‌های مربوط به آن‌ها استفاده می‌کند.

برای مثال تصور کنید کاربری ثبت‌نام می‌کند.

به‌جای اینکه داخل Controller تمام کارها را انجام دهیم:


<?php



public function register()

{

    // Create user



    // Send email



    // Send SMS



    // Create CRM record



    // Log activity

}

می‌توانیم یک Event ایجاد کنیم:


<?php



final class UserRegistered

{

    public function __construct(

        public readonly int $userId

    ) {

    }

}

سپس Listenerهای مختلفی داشته باشیم:


UserRegistered

       │

       ├── SendWelcomeEmail

       ├── SendWelcomeSms

       ├── CreateCrmCustomer

       └── LogUserRegistration

در این حالت منطق ثبت‌نام از واکنش‌های بعد از ثبت‌نام جدا می‌شود.


# مثال Laravel برای Event و Listener

فرض کنید کاربر ثبت‌نام شده است.

در سرویس ثبت‌نام:


<?php



use App\Events\UserRegistered;



event(

    new UserRegistered($user->id)

);

حالا Listener مربوط به ارسال ایمیل می‌تواند مسئول این کار باشد:


<?php



namespace App\Listeners;



use App\Events\UserRegistered;



final class SendWelcomeEmail

{

    public function handle(

        UserRegistered $event

    ): void {

        // Send welcome email

    }

}

Listener دیگری می‌تواند SMS ارسال کند:


<?php



namespace App\Listeners;



use App\Events\UserRegistered;



final class SendWelcomeSms

{

    public function handle(

        UserRegistered $event

    ): void {

        // Send SMS

    }

}

مزیت اصلی این ساختار این است که کدی که کاربر را ثبت می‌کند، لازم نیست بداند بعد از ثبت‌نام دقیقاً چه اتفاقاتی رخ خواهد داد.


# Laravel Model Observer چیست؟

Laravel همچنین مفهوم Model Observer دارد.

Model Observer برای زمانی بسیار مفید است که بخواهیم نسبت به lifecycle یک مدل واکنش نشان دهیم.

مثلاً برای مدل Product می‌توانیم به رخدادهایی مانند موارد زیر واکنش نشان دهیم:

  • creating
  • created
  • updating
  • updated
  • deleting
  • deleted
  • restoring
  • restored

مثلاً:


<?php



namespace App\Observers;



use App\Models\Product;



final class ProductObserver

{

    public function created(Product $product): void

    {

        // Clear cache

        // Update search index

        // Write log

    }

}

در این حالت تغییرات مربوط به مدل Product می‌توانند واکنش‌های مشخصی را فعال کنند.


# چه زمانی از Laravel Observer استفاده کنیم؟

Model Observer زمانی مناسب است که یک عملیات به lifecycle یک مدل وابسته باشد.

برای مثال:


Product Updated

       ↓

Clear Product Cache

یا:


Order Created

       ↓

Create Invoice

اما اگر یک فرآیند پیچیده کسب‌وکاری دارید که باید در نقاط مختلف سیستم منتشر شود، Event و Listener معمولاً انتخاب مناسب‌تری است.


# مزایای Observer Pattern

۱. کاهش وابستگی

Subject لازم نیست جزئیات Observerها را بداند.

این یکی از مهم‌ترین مزایای این الگو است.

۲. امکان اضافه کردن واکنش جدید

فرض کنید بعد از ثبت سفارش فقط ایمیل و SMS داریم.

بعداً می‌توانیم Observer دیگری اضافه کنیم:


$order->attach(

    new AccountingObserver()

);

بدون اینکه منطق اصلی سفارش را تغییر دهیم.

۳. رعایت اصل Open/Closed

در بسیاری از سناریوها می‌توان رفتار جدید را با اضافه کردن Observer جدید توسعه داد، بدون اینکه Subject اصلی را تغییر دهیم.

۴. جداسازی مسئولیت‌ها

ارسال ایمیل، ارسال SMS، ثبت لاگ و به‌روزرسانی CRM در کلاس‌های جدا قرار می‌گیرند.

۵. قابلیت تست بهتر

هر Observer را می‌توان مستقل تست کرد.

مثلاً:


$emailObserver = new EmailObserver();

و عملکرد آن را جداگانه تست کرد.


# معایب Observer Pattern

Observer نیز مانند هر Design Pattern دیگری هزینه و Trade-off دارد.

۱. پیچیده شدن جریان اجرای برنامه

ممکن است از داخل یک متد فقط این خط را ببینید:


$this->notify();

اما پشت آن پنج Observer اجرا شوند.

برای توسعه‌دهنده‌ای که با پروژه آشنا نیست، پیدا کردن تمام اتفاقات ممکن است دشوار باشد.

۲. وابستگی پنهان

اگر تعداد Observerها زیاد شود، ممکن است رفتار سیستم در نگاه اول مشخص نباشد.

۳. مشکل در مدیریت خطا

اگر یکی از Observerها خطا ایجاد کند، باید مشخص باشد که:

  • آیا Observerهای بعدی اجرا شوند؟
  • آیا کل عملیات شکست بخورد؟
  • آیا خطا Log شود؟
  • آیا عملیات دوباره تلاش شود؟

این موضوع در سیستم‌های حساس اهمیت زیادی دارد.

۴. مشکل عملیات سنگین

اگر Observer مستقیماً عملیات سنگینی مانند ارسال HTTP Request یا پردازش فایل بزرگ انجام دهد، ممکن است درخواست کاربر کند شود.

برای چنین عملیات‌هایی معمولاً Queue انتخاب مناسب‌تری است.


# Observer و Queue چه تفاوتی دارند؟

این دو مفهوم را نباید با هم اشتباه بگیریم.

Observer مشخص می‌کند:

> وقتی اتفاقی افتاد، چه بخش‌هایی باید مطلع شوند؟

Queue مشخص می‌کند:

> این عملیات چه زمانی و در چه فرآیندی اجرا شود؟

مثلاً:


Order Created

     ↓

Observer / Event

     ↓

Queue Job

     ↓

Send Email

در این معماری، Observer یا Event رخداد را اعلام می‌کند و Job عملیات سنگین را به صف منتقل می‌کند.

در یک درخواست عادی PHP، Observerهای معمولی به‌صورت همزمان و در همان فرآیند اجرا می‌شوند؛ بنابراین عملیات سنگین داخل Observer می‌تواند پاسخ را کند کند. برای کارهای زمان‌بر بهتر است Observer یا Listener صرفاً کار را به Queue بسپارد.


# تست Observer با PHPUnit

یکی از مزیت‌های این الگو، امکان تست مستقل Observerهاست.

برای مثال:


<?php



use PHPUnit\Framework\TestCase;



final class EmailObserverTest extends TestCase

{

    public function test_observer_is_called(): void

    {

        $observer = new EmailObserver();



        $order = new Order();



        $order->attach($observer);



        $order->create();



        self::assertTrue(true);

    }

}

در پروژه واقعی بهتر است به‌جای تستی که فقط true را بررسی می‌کند، رفتار واقعی Observer را با Mock، Fake یا Assertion مناسب بررسی کنیم.


# چه زمانی از Observer Pattern استفاده کنیم؟

Observer انتخاب خوبی است وقتی:

  • یک رخداد چند واکنش مستقل دارد.
  • تعداد واکنش‌ها ممکن است در آینده افزایش پیدا کند.
  • نمی‌خواهید Subject به جزئیات واکنش‌ها وابسته باشد.
  • نیاز به Publish/Subscribe دارید.
  • می‌خواهید مسئولیت‌ها از یکدیگر جدا شوند.
  • یک تغییر باید چند بخش مختلف سیستم را مطلع کند.

مثلاً:


PaymentCompleted

      ↓

 ┌────┼────┬────┐

 ↓    ↓    ↓    ↓

Email SMS  Log  Invoice

این سناریو نمونه بسیار خوبی برای Observer است.


# چه زمانی از Observer استفاده نکنیم؟

اگر فقط یک عملیات ساده بعد از یک اتفاق دارید، استفاده از Observer ممکن است غیرضروری باشد.

مثلاً:


<?php



$user->activate();

و فقط یک خط ساده بعد از آن وجود دارد، احتمالاً نیازی نیست برای آن چند Interface و Observer ایجاد کنیم.

همچنین اگر ترتیب دقیق اجرای چند مرحله اهمیت حیاتی دارد، Observer ممکن است انتخاب مناسبی نباشد؛ چون Observer بیشتر برای واکنش‌های مستقل طراحی شده است.


# تفاوت Observer با Strategy

در مقاله قبلی با Strategy Pattern آشنا شدیم.

این دو الگو ممکن است در نگاه اول مشابه به نظر برسند، اما هدفشان متفاوت است.

Strategy

Strategy برای انتخاب بین چند الگوریتم یا رفتار مختلف است.

مثلاً:


Payment

 ├── Zarinpal

 ├── IDPay

 └── Stripe

Observer

Observer برای اطلاع دادن یک رخداد به چند بخش مختلف است.

مثلاً:


Order Created

 ├── Email

 ├── SMS

 ├── Inventory

 └── Log

پس:

> Strategy = انتخاب یک رفتار

> Observer = اطلاع‌رسانی به چند رفتار


# تفاوت Observer با Command

Command معمولاً یک درخواست یا عملیات را به‌صورت یک شیء مستقل کپسوله می‌کند.

Observer اما بیشتر روی اطلاع‌رسانی یک رخداد و واکنش چند Observer تمرکز دارد.

برای مثال:


Command

   ↓

"CreateOrder"

در مقابل:


OrderCreated Event

       ↓

 ┌─────┼─────┐

 ↓     ↓     ↓

Email  SMS   Log

این دو الگو حتی می‌توانند در یک معماری کنار یکدیگر استفاده شوند.


# تفاوت Observer با Mediator

در Observer معمولاً یک Subject به چند Observer اطلاع می‌دهد.

اما Mediator برای مدیریت ارتباط بین چند شیء مختلف از طریق یک واسط مرکزی استفاده می‌شود.

به‌صورت ساده:


Observer:



Subject

 ├── Observer

 ├── Observer

 └── Observer

در حالی که:


Mediator:



Object A ──┐

Object B ──┼── Mediator

Object C ──┘

بنابراین Observer بیشتر روی Broadcast یک رخداد تمرکز دارد.


# یک مثال واقعی‌تر: سیستم فروشگاه اینترنتی

فرض کنید کاربر سفارش خود را پرداخت کرده است.

ما یک Event داریم:


<?php



final class OrderPaid

{

    public function __construct(

        public readonly int $orderId

    ) {

    }

}

بعد از انتشار این Event چند Listener می‌توانند فعال شوند:


OrderPaid

   │

   ├── SendInvoiceEmail

   │

   ├── UpdateInventory

   │

   ├── NotifyAdmin

   │

   ├── UpdateCustomerPoints

   │

   └── SendToAccounting

این ساختار بسیار بهتر از این است که یک کلاس بزرگ داشته باشیم:


<?php



public function pay(): void

{

    // Payment



    // Email



    // SMS



    // Inventory



    // Accounting



    // CRM



    // Analytics



    // ...

}

با افزایش امکانات فروشگاه، روش دوم به‌سرعت پیچیده می‌شود.


# نکات مهم در طراحی Observerهای حرفه‌ای

برای اینکه Observer Pattern باعث پیچیدگی پروژه نشود، چند نکته مهم را رعایت کنید.

Observerها را مستقل نگه دارید

بهتر است یک Observer به اجرای موفق Observer دیگر وابسته نباشد.

Observerها را بیش از حد بزرگ نکنید

اگر Observer خودش صدها خط کد دارد، احتمالاً باید مسئولیت آن را به سرویس‌های کوچک‌تر تقسیم کنید.

عملیات سنگین را Queue کنید

برای ایمیل، HTTP Request، پردازش فایل و عملیات مشابه، در پروژه‌های Laravel بهتر است از Queue استفاده شود.

نام‌گذاری واضح داشته باشید

نام‌هایی مانند:


SendWelcomeEmail

UpdateInventory

CreateInvoice

NotifyAdmin

بسیار بهتر از نام‌هایی مانند:


Observer1

Observer2

Handler

Process

هستند.

مراقب وابستگی‌های پنهان باشید

اگر فهمیدن اینکه یک رخداد دقیقاً چه کارهایی انجام می‌دهد بسیار دشوار شده است، تعداد Eventها و Observerها را بازبینی کنید.


# جمع‌بندی

Observer Pattern در PHP یکی از الگوهای مهم طراحی رفتاری است که برای ایجاد ارتباط یک‌به‌چند بین یک Subject و چند Observer استفاده می‌شود.

در این الگو، Subject یک رخداد یا تغییر را اعلام می‌کند و Observerهای ثبت‌شده به آن واکنش نشان می‌دهند.

مهم‌ترین مزیت Observer این است که می‌توانیم واکنش‌های مختلف را از منطق اصلی برنامه جدا کنیم.

برای مثال در یک فروشگاه:


Order Created

      │

      ├── Send Email

      ├── Send SMS

      ├── Update Inventory

      ├── Create Invoice

      └── Write Log

هرکدام از این عملیات می‌توانند مستقل باشند و اضافه کردن یک واکنش جدید لزوماً نیازی به تغییر منطق اصلی سفارش نداشته باشد.

PHP نیز با SplSubject و SplObserver امکانات پایه لازم برای پیاده‌سازی این الگو را در اختیار برنامه‌نویس قرار می‌دهد.

در Laravel نیز مفاهیم Event، Listener و Model Observer ابزارهای بسیار کاربردی برای پیاده‌سازی همین ایده در پروژه‌های واقعی هستند.

اگر پروژه شما دارای رخدادهایی است که چند بخش مختلف باید به آن‌ها واکنش نشان دهند، Observer Pattern می‌تواند انتخاب مناسبی باشد؛ اما نباید صرفاً به خاطر استفاده از Design Pattern، ساختار ساده پروژه را پیچیده کنیم.

هدف Design Pattern این نیست که کد بیشتری بنویسیم؛ هدف آن این است که کد را در برابر تغییرات آینده قابل نگهداری‌تر کنیم.


سوالات متداول

Observer Pattern در PHP چیست؟

Observer Pattern یک الگوی طراحی رفتاری است که اجازه می‌دهد یک Subject هنگام رخ دادن تغییر یا رویداد، چند Observer را مطلع کند.

Subject در Observer Pattern چیست؟

Subject شیء اصلی است که تغییرات یا رخدادهای آن برای Observerها اهمیت دارد و مسئول اطلاع‌رسانی به Observerهای ثبت‌شده است.

Observer در PHP چیست؟

Observer شیئی است که به Subject متصل می‌شود و هنگام رخ دادن یک تغییر یا رویداد، متد مربوط به واکنش آن اجرا می‌شود.

آیا PHP خودش Observer Pattern را پشتیبانی می‌کند؟

PHP دارای Interfaceهای داخلی SplSubject و SplObserver است که می‌توان از آن‌ها برای پیاده‌سازی ساختار Observer استفاده کرد.

تفاوت Observer و Strategy چیست؟

Strategy برای انتخاب بین چند الگوریتم یا رفتار استفاده می‌شود، در حالی که Observer برای اطلاع دادن یک رخداد به چند Observer کاربرد دارد.

Observer Pattern در Laravel کجا استفاده می‌شود؟

در Laravel می‌توان از Event و Listener برای پیاده‌سازی الگوی Publish/Subscribe و از Model Observer برای واکنش به lifecycle مدل‌ها استفاده کرد.

آیا Observerها باید عملیات سنگین انجام دهند؟

بهتر است عملیات زمان‌بر مانند ارسال درخواست‌های خارجی، پردازش فایل و برخی عملیات ایمیل به Queue منتقل شوند تا اجرای Observer باعث کند شدن درخواست اصلی نشود.

آیا استفاده از Observer همیشه مناسب است؟

خیر. اگر فقط یک واکنش ساده دارید یا ارتباط بین بخش‌ها بسیار ساده است، استفاده از Observer ممکن است پیچیدگی غیرضروری ایجاد کند.

نظرات (0)

نظرات پس از تأیید مدیر نمایش داده می‌شوند

هنوز نظری ثبت نشده است؛ اولین نفر باشید!

مقالات مرتبط

ساخت CMS با Laravel 12؛ آموزش ساخت سیستم مدیریت محتوا از صفر

ساخت CMS با Laravel 12؛ آموزش ساخت سیستم مدیریت محتوا از صفر

۲۴ دقیقه
ساخت فروشگاه اینترنتی با Laravel 12؛ آموزش پروژه‌محور از صفر تا طراحی هسته فروشگاه

ساخت فروشگاه اینترنتی با Laravel 12؛ آموزش پروژه‌محور از صفر تا طراحی هسته فروشگاه

۲۲ دقیقه
ساخت Marketplace با Laravel؛ آموزش طراحی فروشگاه چندفروشندگی

ساخت Marketplace با Laravel؛ آموزش طراحی فروشگاه چندفروشندگی

۲۲ دقیقه
مدیریت Queueهای سنگین در Laravel؛ آموزش پردازش کارهای طولانی

مدیریت Queueهای سنگین در Laravel؛ آموزش پردازش کارهای طولانی

۲۲ دقیقه