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

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

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

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

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

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

برای مثال، یک سرویس ارسال پیامک در ابتدا فقط وظیفه ارسال پیام را دارد؛ اما بعد از مدتی به قابلیت‌های زیر نیاز پیدا می‌کنیم:

  • ثبت لاگ درخواست‌ها
  • شمارش تعداد پیامک‌های ارسال‌شده
  • ذخیره نتیجه در Cache
  • محدود کردن تعداد درخواست‌ها
  • بررسی اعتبار شماره موبایل
  • مدیریت خطا
  • ارسال اعلان بعد از موفقیت

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

راه‌حل مناسب در بسیاری از این شرایط، استفاده از Decorator Pattern در PHP است.

Decorator Pattern به ما اجازه می‌دهد بدون تغییر کد اصلی یک Object، رفتار یا قابلیت جدیدی به آن اضافه کنیم. این قابلیت‌ها می‌توانند به‌صورت جداگانه در کلاس‌های کوچک‌تر پیاده‌سازی شوند و حتی چند Decorator روی یکدیگر قرار بگیرند.

Decorator Pattern در PHP چیست؟

Decorator Pattern یکی از الگوهای طراحی ساختاری یا Structural Design Patterns است.

در این الگو، یک Object داخل Object دیگری قرار می‌گیرد و Object بیرونی، علاوه بر حفظ Interface اصلی، رفتار جدیدی به آن اضافه می‌کند.

به زبان ساده:


Object اصلی

    ↓

Decorator اول

    ↓

Decorator دوم

    ↓

Decorator سوم

تمام این Objectها از یک قرارداد مشترک استفاده می‌کنند؛ بنابراین برنامه می‌تواند با Decorator نهایی دقیقاً مانند Object اصلی کار کند.

مثال واقعی برای درک Decorator

فرض کنید یک فنجان قهوه داریم. ابتدا قهوه ساده است، سپس می‌توانیم به آن موارد زیر را اضافه کنیم:

  • شیر
  • شکر
  • خامه
  • شکلات

هر افزودنی بدون تغییر خود قهوه، ویژگی جدیدی به آن اضافه می‌کند.

در برنامه‌نویسی نیز یک سرویس اصلی داریم و Decoratorها می‌توانند قابلیت‌هایی مانند لاگ‌گیری، کش یا اعتبارسنجی را به آن اضافه کنند.

مشکل افزودن همه قابلیت‌ها در یک کلاس

فرض کنید سرویس ارسال پیامک ما به شکل زیر است:


class SmsService

{

    public function send(

        string $phone,

        string $message

    ): bool {

        // ارسال پیامک



        return true;

    }

}

در ابتدا این کلاس ساده و قابل فهم است. اما اگر بخواهیم لاگ‌گیری، اعتبارسنجی و محدودسازی درخواست را هم به آن اضافه کنیم، ممکن است به چنین کدی برسیم:


class SmsService

{

    public function send(

        string $phone,

        string $message

    ): bool {

        // اعتبارسنجی شماره موبایل



        // بررسی محدودیت ارسال



        // ثبت لاگ



        // ارسال پیامک



        // ذخیره آمار



        return true;

    }

}

مشکل این ساختار این است که کلاس SmsService مسئولیت‌های زیادی پیدا می‌کند.

این وضعیت می‌تواند باعث شود:

  • کلاس بیش از حد بزرگ شود.
  • تست کردن هر قابلیت دشوار شود.
  • تغییر یک قابلیت روی قابلیت‌های دیگر اثر بگذارد.
  • استفاده مجدد از قابلیت‌ها سخت شود.
  • منطق ارسال پیامک با منطق لاگ‌گیری و اعتبارسنجی ترکیب شود.

بهتر است سرویس اصلی فقط وظیفه ارسال پیامک را انجام دهد و قابلیت‌های دیگر در Decoratorهای جداگانه قرار بگیرند.

تعریف Interface مشترک

برای استفاده از Decorator Pattern ابتدا یک Interface تعریف می‌کنیم:


interface NotificationSender

{

    public function send(

        string $recipient,

        string $message

    ): bool;

}

تمام کلاس‌هایی که می‌توانند پیامک ارسال کنند، باید این قرارداد را پیاده‌سازی کنند.

ساخت سرویس اصلی

اکنون سرویس اصلی را می‌سازیم:


class SmsService implements NotificationSender

{

    public function send(

        string $recipient,

        string $message

    ): bool {

        echo "Sending SMS to {$recipient}";



        return true;

    }

}

این کلاس فقط وظیفه ارسال پیامک را دارد و نباید مسئولیت‌های جانبی مانند لاگ‌گیری یا کش را بر عهده بگیرد.

ساخت Decorator پایه

برای اینکه Decoratorهای مختلف ساختار مشترکی داشته باشند، یک Decorator پایه ایجاد می‌کنیم:


abstract class NotificationDecorator

    implements NotificationSender

{

    public function __construct(

        protected NotificationSender $sender

    ) {

    }

}

این کلاس:

  • Interface اصلی را پیاده‌سازی می‌کند.
  • یک نمونه از NotificationSender را دریافت می‌کند.
  • امکان قرار گرفتن روی سرویس اصلی یا Decorator دیگر را فراهم می‌کند.

از آنجا که کلاس پایه Abstract است، متد send() را در Decoratorهای فرزند پیاده‌سازی خواهیم کرد.

ساخت Decorator برای ثبت لاگ

اکنون یک Decorator می‌سازیم که قبل و بعد از ارسال پیامک لاگ ثبت کند:


class LoggingNotificationDecorator

    extends NotificationDecorator

{

    public function send(

        string $recipient,

        string $message

    ): bool {

        echo "Before sending notification" . PHP_EOL;



        $result = $this->sender->send(

            $recipient,

            $message

        );



        echo "After sending notification" . PHP_EOL;



        return $result;

    }

}

این Decorator خودش پیامک را ارسال نمی‌کند؛ بلکه درخواست را به Object داخلی منتقل می‌کند.

به این ترتیب، مسئولیت‌ها از هم جدا می‌شوند:

  • SmsService مسئول ارسال پیامک است.
  • LoggingNotificationDecorator مسئول لاگ‌گیری است.

استفاده از Decorator


$smsService = new SmsService();



$sender = new LoggingNotificationDecorator(

    $smsService

);



$sender->send(

    '09120000000',

    'سلام از سایت سی ام اس نویس'

);

از دید برنامه، متغیر $sender همچنان از نوع NotificationSender است. بنابراین برنامه لازم نیست بداند در پشت آن یک Decorator قرار گرفته است.

ساخت Decorator برای اعتبارسنجی

می‌توانیم یک Decorator دیگر برای بررسی شماره موبایل ایجاد کنیم:


class ValidationNotificationDecorator

    extends NotificationDecorator

{

    public function send(

        string $recipient,

        string $message

    ): bool {

        if (!preg_match('/^09\d{9}$/', $recipient)) {

            throw new InvalidArgumentException(

                'شماره موبایل معتبر نیست.'

            );

        }



        if (trim($message) === '') {

            throw new InvalidArgumentException(

                'متن پیام نمی‌تواند خالی باشد.'

            );

        }



        return $this->sender->send(

            $recipient,

            $message

        );

    }

}

در این کلاس، قبل از ارسال پیامک، اطلاعات ورودی بررسی می‌شود.

ترکیب چند Decorator

قدرت اصلی Decorator Pattern زمانی مشخص می‌شود که چند Decorator را با هم ترکیب کنیم:


$smsService = new SmsService();



$sender = new ValidationNotificationDecorator(

    new LoggingNotificationDecorator(

        $smsService

    )

);



$sender->send(

    '09120000000',

    'پیام تستی'

);

ترتیب اجرای کد به شکل زیر است:


Validation Decorator

    ↓

Logging Decorator

    ↓

SmsService

در این حالت:

  1. شماره موبایل بررسی می‌شود.
  2. لاگ قبل از ارسال ثبت می‌شود.
  3. سرویس اصلی پیامک را ارسال می‌کند.
  4. لاگ بعد از ارسال ثبت می‌شود.
  5. نتیجه به Decorator قبلی برمی‌گردد.

ساخت Decorator برای Cache

فرض کنید نتیجه یک سرویس را می‌خواهیم برای مدتی ذخیره کنیم تا درخواست‌های تکراری دوباره به سرویس اصلی ارسال نشوند.

یک نمونه ساده از Decorator کش:


class CacheNotificationDecorator

    extends NotificationDecorator

{

    private array $cache = [];



    public function send(

        string $recipient,

        string $message

    ): bool {

        $key = md5($recipient . '|' . $message);



        if (isset($this->cache[$key])) {

            return $this->cache[$key];

        }



        $result = $this->sender->send(

            $recipient,

            $message

        );



        $this->cache[$key] = $result;



        return $result;

    }

}

این مثال فقط برای درک ساختار Decorator است. در پروژه واقعی Laravel بهتر است برای کش از سیستم Cache خود Laravel استفاده شود.

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

Decorator برای ثبت آمار

می‌توانیم تعداد درخواست‌ها را نیز با Decorator ثبت کنیم:


class MetricsNotificationDecorator

    extends NotificationDecorator

{

    private int $count = 0;



    public function send(

        string $recipient,

        string $message

    ): bool {

        $this->count++;



        return $this->sender->send(

            $recipient,

            $message

        );

    }



    public function getCount(): int

    {

        return $this->count;

    }

}

استفاده:


$smsService = new SmsService();



$metrics = new MetricsNotificationDecorator(

    $smsService

);



$metrics->send(

    '09120000000',

    'پیام اول'

);



$metrics->send(

    '09121111111',

    'پیام دوم'

);



echo $metrics->getCount();

Decorator برای محدودسازی درخواست‌ها

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


class RateLimitNotificationDecorator

    extends NotificationDecorator

{

    private int $requests = 0;



    public function __construct(

        NotificationSender $sender,

        private int $limit

    ) {

        parent::__construct($sender);

    }



    public function send(

        string $recipient,

        string $message

    ): bool {

        if ($this->requests >= $this->limit) {

            throw new RuntimeException(

                'تعداد درخواست‌ها از حد مجاز بیشتر شده است.'

            );

        }



        $this->requests++;



        return $this->sender->send(

            $recipient,

            $message

        );

    }

}

استفاده:


$sender = new RateLimitNotificationDecorator(

    new SmsService(),

    3

);

در این مثال فقط سه درخواست اجازه عبور دارند.

در پروژه واقعی، محدودسازی درخواست باید معمولاً با Cache یا Redis انجام شود تا محدودیت بین چند Worker یا چند سرور نیز درست عمل کند.

ساختار کامل چند Decorator

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


$sender = new RateLimitNotificationDecorator(

    new ValidationNotificationDecorator(

        new LoggingNotificationDecorator(

            new SmsService()

        )

    ),

    10

);

ساختار اجرایی:


Rate Limit

    ↓

Validation

    ↓

Logging

    ↓

SmsService

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

Decorator Pattern و Composition

Decorator Pattern به جای استفاده گسترده از Inheritance، از Composition استفاده می‌کند.

در Inheritance، قابلیت‌ها معمولاً با ساخت کلاس‌های فرزند اضافه می‌شوند:


class LoggedSmsService extends SmsService

{

}

اما در Composition، یک Object داخل Object دیگر قرار می‌گیرد:


$sender = new LoggingNotificationDecorator(

    new SmsService()

);

Composition معمولاً انعطاف بیشتری دارد؛ زیرا می‌توانیم Decoratorها را در زمان اجرا ترکیب یا جابه‌جا کنیم.

تفاوت Decorator و Inheritance

ویژگیDecoratorInheritance
روش توسعهCompositionارث‌بری
تغییر در زمان اجراامکان‌پذیرمعمولاً محدود
ترکیب چند قابلیتساده‌ترممکن است باعث ایجاد کلاس‌های زیاد شود
وابستگیکمتربیشتر
رعایت Single Responsibilityمعمولاً بهتربه طراحی وابسته است
امکان تغییر ترتیب قابلیت‌هابلهمعمولاً دشوارتر

فرض کنید سه قابلیت داریم:

  • لاگ‌گیری
  • کش
  • اعتبارسنجی

با Inheritance ممکن است به کلاس‌هایی مانند زیر نیاز پیدا کنیم:


LoggedSmsService

CachedSmsService

ValidatedSmsService

LoggedCachedSmsService

LoggedValidatedSmsService

CachedValidatedSmsService

LoggedCachedValidatedSmsService

اما با Decorator می‌توانیم آن‌ها را به شکل زنجیره‌ای ترکیب کنیم:


$sender = new LoggingNotificationDecorator(

    new CacheNotificationDecorator(

        new ValidationNotificationDecorator(

            new SmsService()

        )

    )

);

تفاوت Decorator و Adapter

Adapter و Decorator هر دو می‌توانند یک Object را داخل خود نگه دارند، اما هدفشان متفاوت است.

Adapter

Adapter زمانی استفاده می‌شود که Interface یک کلاس با Interface مورد انتظار برنامه سازگار نیست.

مثال:


send()

    ↓

sendMessage()

Adapter Interface را تبدیل می‌کند.

Decorator

Decorator زمانی استفاده می‌شود که Interface از قبل سازگار است، اما می‌خواهیم رفتار جدیدی به آن اضافه کنیم.

مثال:


send()

    ↓

ثبت لاگ

    ↓

send()

خلاصه:

  • Adapter، Interface را تغییر می‌دهد.
  • Decorator، رفتار را توسعه می‌دهد و Interface را حفظ می‌کند.

تفاوت Decorator و Proxy

Proxy نیز معمولاً یک Object دیگر را کنترل می‌کند، اما هدف آن با Decorator متفاوت است.

Proxy ممکن است برای موارد زیر استفاده شود:

  • دسترسی کنترل‌شده
  • Lazy Loading
  • کنترل سطح دسترسی
  • ارتباط با Object راه دور
  • ایجاد نماینده برای Object اصلی

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

البته در بعضی پروژه‌ها مرز بین Proxy و Decorator می‌تواند نزدیک باشد و تشخیص آن به هدف طراحی بستگی دارد.

تفاوت Decorator و Facade

Facade یک Interface ساده برای کار با چند کلاس پیچیده فراهم می‌کند.

برای مثال:


$orderFacade->completeOrder();

در پشت این متد ممکن است چند عملیات مانند بررسی موجودی، پرداخت، ارسال و اعلان انجام شود.

اما Decorator معمولاً همان Interface موجود را حفظ می‌کند و قابلیت جدیدی به آن اضافه می‌کند.

استفاده از Decorator در Laravel

در Laravel می‌توان از Decorator برای توسعه سرویس‌های مختلف استفاده کرد، مانند:

  • سرویس ارسال پیامک
  • سرویس ارسال ایمیل
  • درگاه پرداخت
  • سرویس ذخیره فایل
  • سرویس ثبت لاگ
  • سرویس محاسبه قیمت
  • سرویس ارسال اعلان
  • سرویس اتصال به API خارجی

فرض کنید Interface زیر را داریم:


interface PaymentGateway

{

    public function pay(int $amount): PaymentResult;

}

درگاه اصلی:


class ZarinpalGateway implements PaymentGateway

{

    public function pay(int $amount): PaymentResult

    {

        // ارسال درخواست به درگاه



        return new PaymentResult(

            'https://payment.example.com'

        );

    }

}

Decorator ثبت لاگ:


class LoggingPaymentDecorator

    implements PaymentGateway

{

    public function __construct(

        private PaymentGateway $gateway

    ) {

    }



    public function pay(int $amount): PaymentResult

    {

        logger()->info(

            'Payment started.',

            [

                'amount' => $amount,

            ]

        );



        $result = $this->gateway->pay($amount);



        logger()->info(

            'Payment completed.',

            [

                'amount' => $amount,

            ]

        );



        return $result;

    }

}

استفاده:


$gateway = new LoggingPaymentDecorator(

    new ZarinpalGateway()

);



$result = $gateway->pay(500000);

اکنون منطق پرداخت در ZarinpalGateway باقی می‌ماند و لاگ‌گیری در Decorator انجام می‌شود.

اتصال Decorator به Service Container

در Laravel می‌توان Decorator را در Service Container ثبت کرد:


$this->app->bind(

    PaymentGateway::class,

    function ($app) {

        $gateway = new ZarinpalGateway();



        return new LoggingPaymentDecorator(

            $gateway

        );

    }

);

از این به بعد هر کلاسی که PaymentGateway را تزریق کند، نسخه Decoratorشده را دریافت خواهد کرد.

نمونه سرویس سفارش:


class OrderService

{

    public function __construct(

        private PaymentGateway $gateway

    ) {

    }



    public function pay(int $amount): PaymentResult

    {

        return $this->gateway->pay($amount);

    }

}

OrderService لازم نیست بداند لاگ‌گیری چگونه انجام می‌شود. این قابلیت در لایه Decorator قرار گرفته است.

نکته مهم درباره ترتیب Decoratorها

ترتیب Decoratorها همیشه بی‌اهمیت نیست.

برای مثال، این دو ساختار می‌توانند نتیجه متفاوتی داشته باشند:


new LoggingDecorator(

    new ValidationDecorator(

        $service

    )

);

و:


new ValidationDecorator(

    new LoggingDecorator(

        $service

    )

);

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

بنابراین هنگام استفاده از چند Decorator باید ترتیب آن‌ها را آگاهانه انتخاب کنیم.

مدیریت خطا در Decorator

Decorator می‌تواند خطا را ثبت، تبدیل یا دوباره پرتاب کند؛ اما نباید بدون دلیل خطاها را پنهان کند.

نمونه:


class ErrorLoggingDecorator

    extends NotificationDecorator

{

    public function send(

        string $recipient,

        string $message

    ): bool {

        try {

            return $this->sender->send(

                $recipient,

                $message

            );

        } catch (Throwable $exception) {

            logger()->error(

                'Notification failed.',

                [

                    'recipient' => $recipient,

                    'error' => $exception->getMessage(),

                ]

            );



            throw $exception;

        }

    }

}

در این مثال:

  • خطا ثبت می‌شود.
  • Exception اصلی از بین نمی‌رود.
  • لایه بالاتر می‌تواند تصمیم مناسب را بگیرد.

تست کردن Decorator

Decoratorها به دلیل داشتن مسئولیت مشخص، معمولاً به‌راحتی قابل تست هستند.

فرض کنید یک سرویس جعلی داریم:


class FakeNotificationSender

    implements NotificationSender

{

    public bool $called = false;



    public function send(

        string $recipient,

        string $message

    ): bool {

        $this->called = true;



        return true;

    }

}

تست ساده:


$fakeSender = new FakeNotificationSender();



$decorator = new LoggingNotificationDecorator(

    $fakeSender

);



$result = $decorator->send(

    '09120000000',

    'Test'

);



assert($result === true);

assert($fakeSender->called === true);

در تست‌های حرفه‌ای‌تر می‌توان با PHPUnit بررسی کرد که:

  • سرویس داخلی دقیقاً یک بار فراخوانی شده است.
  • پارامترها بدون تغییر منتقل شده‌اند.
  • در شرایط خطا رفتار Decorator درست است.
  • قابلیت موردنظر قبل یا بعد از فراخوانی سرویس اجرا می‌شود.

اشتباهات رایج در استفاده از Decorator

۱. تغییر دادن قرارداد اصلی

Decorator باید Interface اصلی را حفظ کند. اگر قرارداد کاملاً تغییر کند، احتمالاً به Adapter نیاز داریم.

۲. قرار دادن منطق تجاری پیچیده

Decorator بهتر است قابلیت جانبی مانند لاگ، کش یا اعتبارسنجی را اضافه کند. منطق تجاری اصلی باید در سرویس مناسب قرار داشته باشد.

۳. ایجاد Decoratorهای بسیار بزرگ

اگر یک Decorator هم لاگ‌گیری، هم کش، هم اعتبارسنجی و هم مدیریت پرداخت انجام دهد، دوباره با یک کلاس چندمسئولیتی روبه‌رو می‌شویم.

۴. بی‌توجهی به ترتیب Decoratorها

ترتیب اجرای Decoratorها می‌تواند روی نتیجه، لاگ‌ها، کش و مدیریت خطا اثر بگذارد.

۵. مخفی کردن خطاها

Decorator نباید تمام Exceptionها را بدون ثبت یا تصمیم مشخص به false تبدیل کند.

۶. استفاده از Decorator برای هر تغییر کوچک

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

مزایای Decorator Pattern در PHP

مهم‌ترین مزایای Decorator Pattern عبارت‌اند از:

  • افزودن قابلیت بدون تغییر کلاس اصلی
  • رعایت اصل Open/Closed
  • کاهش وابستگی به Inheritance
  • امکان ترکیب چند قابلیت
  • تست‌پذیری بهتر
  • استفاده مجدد از قابلیت‌ها
  • حفظ Interface اصلی
  • جداسازی مسئولیت‌ها
  • امکان تغییر ترتیب قابلیت‌ها در زمان اجرا
  • مناسب بودن برای سرویس‌های خارجی و APIها

معایب Decorator Pattern

این الگو معایبی نیز دارد:

  • تعداد کلاس‌های پروژه افزایش پیدا می‌کند.
  • دنبال کردن زنجیره Decoratorها ممکن است برای افراد تازه‌کار دشوار باشد.
  • ترتیب Decoratorها باید به‌درستی مدیریت شود.
  • دیباگ کردن زنجیره‌های بسیار طولانی سخت‌تر است.
  • استفاده بیش از حد می‌تواند طراحی را پیچیده کند.

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

Decorator در شرایط زیر انتخاب مناسبی است:

  • می‌خواهیم قابلیت جانبی به یک سرویس اضافه کنیم.
  • نمی‌خواهیم کلاس اصلی را تغییر دهیم.
  • قابلیت جدید باید قابل استفاده مجدد باشد.
  • نیاز به ترکیب چند رفتار مختلف داریم.
  • می‌خواهیم از ایجاد تعداد زیادی کلاس فرزند جلوگیری کنیم.
  • سرویس اصلی از قبل Interface مناسبی دارد.
  • نیاز به لاگ‌گیری، کش، اعتبارسنجی یا محدودسازی درخواست‌ها داریم.

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

اگر Interface کلاس با نیاز برنامه سازگار نیست، Adapter انتخاب مناسب‌تری است.

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

اگر قابلیت جدید بخشی از منطق اصلی کسب‌وکار است، بهتر است آن را در Service یا Domain Object مناسب قرار دهیم.

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

جمع‌بندی

Decorator Pattern در PHP یکی از الگوهای طراحی ساختاری مهم است که برای افزودن قابلیت به Objectها بدون تغییر کد اصلی آن‌ها استفاده می‌شود.

در این الگو، Decorator همان Interface Object اصلی را پیاده‌سازی می‌کند و یک نمونه از آن را داخل خود نگه می‌دارد. به همین دلیل می‌توان چند Decorator را به‌صورت زنجیره‌ای ترکیب کرد.

در این مقاله با موارد زیر آشنا شدیم:

  • مفهوم Decorator Pattern در PHP
  • تفاوت Decorator با Inheritance
  • استفاده از Composition
  • ساخت Decorator پایه
  • افزودن لاگ‌گیری به سرویس
  • افزودن اعتبارسنجی
  • پیاده‌سازی Cache Decorator
  • محدودسازی درخواست‌ها
  • استفاده از Decorator در Laravel
  • ثبت Decorator در Service Container
  • تفاوت Decorator با Adapter، Proxy و Facade
  • تست کردن Decorator
  • اشتباهات رایج و زمان مناسب استفاده

اگر در پروژه PHP یا Laravel خود نیاز دارید بدون تغییر کلاس اصلی، قابلیت‌هایی مانند لاگ، کش، اعتبارسنجی یا محدودسازی را اضافه کنید، Decorator Pattern می‌تواند راهکاری تمیز و قابل توسعه باشد.


# سوالات متداول درباره Decorator Pattern در PHP

Decorator Pattern در PHP چیست؟

Decorator Pattern الگویی ساختاری است که بدون تغییر کلاس اصلی، رفتار یا قابلیت جدیدی به یک Object اضافه می‌کند.

آیا Decorator باید همان Interface کلاس اصلی را داشته باشد؟

بله، در پیاده‌سازی رایج Decorator، Decorator همان Interface Object اصلی را پیاده‌سازی می‌کند تا بتواند جایگزین آن شود.

تفاوت Decorator و Adapter چیست؟

Adapter برای تبدیل Interface ناسازگار استفاده می‌شود؛ اما Decorator برای افزودن قابلیت جدید به Objectی استفاده می‌شود که Interface آن از قبل سازگار است.

آیا Decorator از Inheritance استفاده می‌کند؟

Decorator می‌تواند از یک کلاس پایه ارث‌بری کند، اما ایده اصلی آن استفاده از Composition و قرار دادن یک Object داخل Object دیگر است.

آیا می‌توان چند Decorator را با هم ترکیب کرد؟

بله. یکی از مهم‌ترین مزایای Decorator Pattern امکان قرار دادن چند Decorator روی یکدیگر است.

Decorator در Laravel چه کاربردی دارد؟

در Laravel می‌توان از Decorator برای لاگ‌گیری درگاه پرداخت، کش کردن نتایج، اعتبارسنجی درخواست‌ها، محدودسازی API و توسعه سرویس‌های پیامک استفاده کرد.

آیا Decorator برای افزودن منطق تجاری مناسب است؟

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

آیا Decorator باعث افزایش تعداد کلاس‌ها می‌شود؟

بله، معمولاً تعداد کلاس‌ها بیشتر می‌شود؛ اما در مقابل، مسئولیت‌ها بهتر جدا می‌شوند و قابلیت‌ها قابل استفاده مجدد خواهند بود.


نظرات (0)

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

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

مقالات مرتبط

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

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

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

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

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

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

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

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

۲۲ دقیقه