Decorator Pattern


ارسال در تاریخ ۲۵ بهمن,۱۳۹۵



ما از این الگوی طراحی زمانی استفاده می کنیم که می خواهیم مسئولیت هایی را به کلاس پایه اضافه کنیم، اما نمی خواهیم کد کلاس پایه را دستکاری کنیم. اگر فکر می کنید که با sub-class ها می توانید به ویژگی های اضافه برسیم، به کدهای زیر توجه داشته باشید.

ما یک کلاس داریم که مسئولیت تولید محتوی یک ایمیل را بر عهده دارد.

class eMailBody {
    private $header = 'This is email header';
    private $footer = 'This is email Footer';
    public $body = '';
 
    public function loadBody() {
        $this->body .= "This is Main Email body.<br />";
    }
}

حالا کریسمس از راه رسیده و می خواهیم یک پیام تبریک برای کریسمس به خوانندگان خبرنامه بدهیم. برای این کار می توان بصورت مستقیم کلاس eMailBody را ویرایش کرد، اما خب ما این کار را انجام نمی دهیم. ما وراثت را برای رسیدن به هدف پیاده سازی می کنیم. خب یک کلاس جداگانه بصورت زیر ایجاد می کنیم که از eMailBody ارث بری کرده است.

class christmasEmail extends eMailBody {
    public function loadBody() {
        parent::loadBody();
        $this->body .= "Added Content for Xmas<br />";
    }
}

و بصورت زیر از این کلاس استفاده می کنیم تا پیام تبریک کریسمس را ارسال کنیم.

$christmasEmail = new christmasEmail();
$christmasEmail->loadBody();
echo $christmasEmail->body;

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

class newYearEmail extends eMailBody {
    public function loadBody() {
        parent::loadBody();
        $this->body .= "Added Content for New Year<br />";
    }
}
 
$newYearEmail = new newYearEmail();
$newYearEmail->loadBody();
echo $newYearEmail->body;

خب این کار را خیلی خوب و بدون هیچ مشکلی انجام دادیم. حالا ما فراموش کرده ایم که پیام تبریک برای هر دو مورد (کریسمس و سال نو) به خوانندگان بدهیم و می خواهیم هر دو پیام تبریک را در یک ایمیل ارسال کنیم، بدون هیچ دستکاری در کد کلاس پایه.

سریع این سوال به ذهنمان می رسد که آیا با sub-class و ارث بری می توان این کار را انجام داد؟ با کدهای اضافه و غیر ضروری می توان انجام داد. ما می توانیم از traits که قابلیت پیاده سازی چیزی شبیه به وراثت چندگانه را به ما  می دهد انجام دهیم.

جواب:

این مشکل که در مورد آن صحبت شد می تواند توسط الگوی طراحی decorator حل بشود. همان طور که در بالا دیدیم می توانستیم توسط یک sub-class و ارث بری، ویژگی ها و رفتارهایی اضافه کنیم ، اما اگر بخواهیم چندین ویژگی و رفتار اضافه کنیم کد ما خیلی طولانی و پیچیده می شود و اینجا جایی است که باید از الگوی decorator استفاده کنیم.

interface eMailBody {
    public function loadBody();
}

این یک interface ساده است که به ما این اطمینان را می دهد که کلاس ها باید متدهای لازم را پیاده سازی کنند.

class eMail implements eMailBody {
    public function loadBody() {
        echo "This is Main Email body.<br />";
    } 
}

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

abstract class emailBodyDecorator implements eMailBody {
         
    protected $emailBody;
     
    public function __construct(eMailBody $emailBody) {
        $this->emailBody = $emailBody;
    }
     
    abstract public function loadBody();
     
}

این کلاس decorator اصلی ما هست که یک ارجاع به کلاس اصلی ایمیل نگهداری می کند و رفتارها را آنگونه که می خواهیم تغییر می دهد. در اینجا ما یک متد abstract داریم، loadBody، که sub decorator ها لازم است که این متد را برای تغییر رفتار  پیاده سازی کنند.

class christmasEmailBody extends emailBodyDecorator {
         
    public function loadBody() {
         
        echo 'This is Extra Content for Christmas<br />';
        $this->emailBody->loadBody();
         
    }
     
}
 
class newYearEmailBody extends emailBodyDecorator {
 
    public function loadBody() {
         
        echo 'This is Extra Content for New Year.<br />';
        $this->emailBody->loadBody();
         
    }
 
}

در اینجا ما دو تا sub-class از کلاس decorator اصلی ایجاد کرده ایم که بطور واقعی تغییر رفتار را در کلاس ایمیل اصلی انجام می دهد.

حالا در روش های مختلف از این decorator استفاده می کنیم.

/*
 *  Normal Email
 */
 
$email = new eMail();
$email->loadBody();
 
// Output
This is Main Email body.
 
 
/*
 *  Email with Xmas Greetings
 */
 
$email = new eMail();
$email = new christmasEmailBody($email);
$email->loadBody();
 
// Output
This is Extra Content for Christmas
This is Main Email body.
 
/*
 *  Email with New Year Greetings
 */
 
$email = new eMail();
$email = new newYearEmailBody($email);
$email->loadBody();
 
 
// Output
This is Extra Content for New Year.
This is Main Email body.
 
/*
 *  Email with Xmas and New Year Greetings
 */
 
$email = new eMail();
$email = new christmasEmailBody($email);
$email = new newYearEmailBody($email);
$email->loadBody();
 
// Output
This is Extra Content for New Year.
This is Extra Content for Christmas
This is Main Email body.

همانطور که مشاهده می کنید، توانستیم توسط الگوی طراحی decorator بدنه ایمیل را طبق خواسته خودمان و بدون تغییر کد کلاس ایمیل اصلی انجام دهیم.


برای ارسال نظر لطفا وارد شوید.