در این آموزش قصد داریم تا به بررسی Facade Design Pattern و نحوۀ پیادهسازی آن در زبان برنامهنویسی PHP بپردازیم و ببینیم که چگونه میتوان با بهکارگیری آن روند توسعه را بهبود داده و اپلیکیشنی با قابلیت نگاهداری بالا پیادهسازی کرد (لازم به یادآوری است که این الگوی طراحی به صورت Façade نیز نوشته میشود که در فارسی «فِساد» خوانده میشود.)
به طور کلی، الگوی طراحی فِساد زیرشاخۀ الگوهای طراحی Structural قرار میگیرد و استفاده از آن در شرایطی مؤثر واقع میشود که کدی پیچیده داشته باشیم که با چندین کلاس مختلف پیادهسازی شده است یا زمانی که یک کد به اصطلاح Legacy (قدیمی) داریم که ریفکتور کردن آن بسیار دشوار و زمانبر میباشد که در چنین شرایطی با استفاده از دیزاین پترن فِساد یک کلاسی میسازیم که امکان ارجاع به تمامی کلاسهای اپلیکیشن را داشته و در نهایت میتوانیم متدهای مد نظر خود را از طریق تنها یک متد فراخوانی کنیم که این امر منجر به کاهش پیچیدگی ساختار سیستم میشود (در همین راستا توصیه میکنیم به مقالهٔ چگونه یک سورسکد اصطلاحاً Legacy را ریفکتور کنیم؟ مراجعه نمایید.)
حال برای درک بهتر ساختار دیزاین پترن فِساد مثالی را در نظر میگیریم که در آن قصد داریم تا چند کلاس مختلف به منظور اشتراکگذاری پستهای وبلاگ خود در شبکههای اجتماعی توسعه دهیم به طوری که به ازای هر شبکۀ اجتماعی یک کلاس منحصربهفرد به همراه متدهای مد نظر را پیادهسازی میکنیم. در همین راستا، برای انتشار پستهای وبلاگ خود در شبکههایی همچون توئیتر، فیسبوک و گوگلپلاس سه کلاس مختلف توسعه میدهیم و داخل هر یک از آنها متدی مجزا برای انجام تَسک مد نظر پیادهسازی خواهیم کرد.
برای شروع، نیاز است تا در ابتدا فایلهای خود را بر اساس ساختار زیر و در پوشهای تحت عنوان facade-design-pattern
در لوکالهاست ایجاد کنیم که در طِی آموزش هر یک از آنها را توسعه خواهیم داد:
facade-design-pattern/
├── Facebook.php
├── GooglePlus.php
├── ShareInterface.php
├── SocialMedia.php
└── Twitter.php
پیش از ادامۀ بحث، توجه داشته باشیم که کلیهٔ فایلهای این آموزش با دستور php?>
شروع میشوند اما به دلیل شلوغ نشدن کدها، از نوشتن آن در تمامی اسکریپتها خودداری کردهایم.
برای شروع، اینترفیسی تحت عنوان ShareInterface
را در فایل ShareInterface.php
پیادهسازی میکنیم به طوری که این اینترفیس توسط تمامی کلاسهای وباپلیکیشن در دسترس خواهد بود و در آن گفتهایم تمامی کلاسهایی که از این اینترفیس اصطلاحاً implements
میشوند موظف بر پیادهسازی دو فانکشن زیر خواهند بود:
interface ShareInterface
{
public function setMessage($message);
public function share();
}
در ادامه، هر یک از کلاسهای فوقالذکر را به منظور اشتراکگذاری پستهای وبلاگ پیادهسازی میکنیم که برای مثال کلاسی تحت عنوان Twitter
را داخل فایلی به نام Twitter.php
از روی اینترفیس ShareInterface
ایمپلیمت میکنیم که به منظور انتشار پستها در شبکۀ اجتماعی توئیتر مورد استفاده قرار میگیرد:
require_once 'ShareInterface.php';
class Twitter implements ShareInterface
{
private $message;
public function setMessage($message)
{
$this->message = $message;
}
public function share()
{
echo "Sharing by $this->message on Twitter.<br/>";
}
}
همانطور که میبینید، در ابتدا فایل مربوط به اینترفیس ShareInterface
را ایمپورت کرده و در ادامه کلاس مد نظر را از روی آن ایمپلیمنت کردهایم و یک پراپرتی تحت عنوان message$
تعریف کردهایم که قرار است تا استرینگ مد نظر برای انتشار در توئیتر را نگاهداری کند. در سطر بعد فانکشنی تحت عنوان ()setMessage
تعریف کرده و پراپرتی message$
را به عنوان آرگومان ورودی به آن پاس داده و گفتهایم در صورت فراخوانی این فانکشن، پارامتر ورودی به پراپرتی this->message$
منتسب شود. در ادامه، فانشکنی دیگر تحت عنوان ()share
تعریف کرده و در آن گفتهایم استرینگ مربوط به پارامتر ورودی را با استرینگ «.Sharing on Twitter» کانکت کرده و در خروجی چاپ کند.
به همین ترتیب کلاس مربوط به اشتراکگذاری پستهای وبلاگ در شبکۀ گوگلپلاس را پیادهسازی میکنیم که برای این منظور در فایلی به نام GooglePlus.php
کلاسی تحت عنوان GooglePlus
را از روی اینترفیس ShareInterface
ایمپلیمت میکنیم:
require_once 'ShareInterface.php';
class GooglePlus implements ShareInterface
{
private $message;
public function setMessage($message)
{
$this->message = $message;
}
public function share()
{
echo "Sharing by $this->message on Google+.<br/>";
}
}
نحوۀ عملکرد این کلاس نیز مشابه کلاس پیشین بوده و تنها تفاوت آن در چاپ استرینگ مربوط به پارامتر ورودی به همراه استرینگ «.+Sharing on Google» میباشد. در ادامه، کلاس سوم تحت عنوان Facebook
را در فایلی به نام Facebook.php
از روی اینترفیس فوقالذکر ایمپلیمت میکنیم که در راستای چاپ استرینگ مربوط به انتشار پستها در شبکۀ اجتماعی فیسبوک مورد استفاده قرار میگیرد:
require_once 'ShareInterface.php';
class Facebook implements ShareInterface
{
private $message;
public function setMessage($message)
{
$this->message = $message;
}
public function share()
{
echo "Sharing by $this->message on Facebook.<br/>";
}
}
ساختار کلاس فوق نیز مشابه دو کلاس قبلی بوده و تنها تفاوت آن در چاپ استرینگ مربوط به پارامتر ورودی به همراه استرینگ «.Sharing on Facebook» میباشد.
حال فرض کنید که اپلیکیشن خود را تا همین سطح و بدون در نظر گرفتن الگوی طراحی فِساد پیادهسازی کردهایم و میخواهیم مثلاً یکی از پستهای خود را در شبکۀ اجتماعی توئیتر منتشر کنیم که برای این منظور لازم است تا به کلاس فوق دسترسی داشته و متدهای مد نظر را از آنها فراخوانی کنیم به طوری که کدی مانند زیر را در فایلی به نام index.php
مینویسیم:
require_once 'Twitter.php';
$twitter = new Twitter();
$twitter->setMessage('No Design Pattern');
$twitter->share();
در کد فوق ابتدا فایل مربوط به کلاس مد نظر را ایمپورت کرده و در ادامه آبجکتی تحت عنوان twitter$
از روی کلاس Twitter
ساخته و در سطر بعد متد ()setMessage
از این کلاس را روی آبجکت ساختهشده و منتسب به پراپرتی twitter$
فراخوانی کرده و استرینگ «No Design Pattern» را به عنوان پارامتر ورودی به آن دادهایم و در ادامه متد ()share
از این کلاس را روی آبجکت منتسب به پراپرتی twitter$
فراخوانی کردهایم که منجر به چاپ استرینگ زیر در خروجی خواهد شد:
Sharing by No Design Pattern on Twitter.
حال اگر بخواهیم پست مد نظر خود را در هر سه شبکۀ توئیتر، فیسبوک و گوگلپلاس منتشر کنیم باید تمامی سه گام فوق را برای هر سه کلاس پیادهسازی کنیم. به عبارتی، سه مرتبه باید ابتدا آبجکتی از کلاس مذکور ساخته و در ادامه متد ()setMessage
از آن را روی آبجکت مد نظر فراخوانی کرده و در نهایت متد ()share
از تکتک کلاسها را فراخوانی کنیم تا استرینگ ریترنشده از متد ()setMessage
را با استرینگ مربوط به کلاس متناظرش کانکت کرده و در خروجی چاپ کند که این امر موجب پیچیدگی سیستم و افزایش بار کاری دولوپر میشود که در همین راستا برای رفع مشکل فوق نیاز است تا دیزاین پترن فِساد را مورد استفاده قرار دهیم.
برای این منظور، فایلی تحت عنوان SocialMedia.php
ساخته و در ادامه کلاسی به نام SocialMedia
را در آن پیادهسازی میکنیم که این کلاس وظیفۀ سادهسازی دسترسی به کلاسهای مختلف و پیادهسازی متدی به منظور فراخوانی متدهای مورد نیاز از کلاسهای مذکور را دارا است که برای این منظور این کلاس را بدین صورت تکمیل میکنیم:
require_once 'Twitter.php';
require_once 'Facebook.php';
require_once 'GooglePlus.php';
class SocialMedia
{
private $twitter;
private $facebook;
private $googleplus;
public function __construct(ShareInterface $twitter, ShareInterface $facebook, ShareInterface $googleplus)
{
$this->twitter = $twitter;
$this->facebook = $facebook;
$this->googleplus= $googleplus;
}
public function shareMessage($message)
{
$this->twitter->setMessage($message);
$this->facebook->setMessage($message);
$this->googleplus->setMessage($message);
return $this;
}
public function shareOnSocial()
{
$this->twitter->share();
$this->facebook->share();
$this->googleplus->share();
}
}
در تفسیر کد فوق باید گفت که ابتدا فایلهای مربوط به سه کلاس مذکور را ایمپورت کرده و کلاسی به نام SocialMedia
را پیادهسازی میکنیم بدین صورت که سه پراپرتی مرتبط با شبکههای اجتماعی توییتر، فیسبوک و گوگلپلاس تعریف کرده که قرار است تا آبجکت ساختهشده از کلاس متناظرشان را نگاهداری کنند و در ادامه کانستراکتوری ساختهایم که این وظیفه را دارا است تا به محض ساخته شدن آبجکتی از روی این کلاس، آبجکتهایی از جنس کلاس ShareInterface
را ساخته و آنها را از طریق دستورهای زیر به ترتیب به پراپرتی متناظرشان منتسب کند:
$this->twitter
$this->facebook
$this->googleplus
در ادامه، فانکشنی تحت عنوان ()shareMessage
با یک آرگومان ورودی به نام message$
تعریف کرده و گفتهایم در صورت فراخوانی، متد ()setMessage
مربوط به هر یک از کلاسهای مد نظر روی آبجکت متناظرِ ساختهشده از آنها و منتسب به پراپرتی مذکور فراخوانی شود (برای مثال در سطر نوزدهم گفتهایم در صورت فراخوانی متد ()shareMessage
متد ()setMessage
با پارامتر ورودی message$
مربوط به کلاس Twitter
روی آبجکت منتسب به this->twitter$
فراخوانی شود.)
در ادامه فانکشن دیگری تحت عنوان ()shareOnSocial
تعریف کردهایم که این وظیفه را دارا است که متد ()share
مربوط به هر یک از کلاسهای مذکور را روی آبجکت ساختهشده از هر کدام فراخوانی کند و استرینگهای ریترنشده از متد ()shareMessage
را دریافت کرده و با استرینگ مربوط به کلاس متناظرش کانکت کرده و در خروجی چاپ کند. برای مثال، در دستور سطر بیستوهفتم گفتهایم فانکشن ()share
مربوط به کلاس Twitter
روی آبجکت منتسب به this->twitter$
فراخوانی شود و استرینگ ریترنشده از متد ()setMessage
مربوط به کلاس Twitter
را با استرینگ مربوطه کانکت کرده و در خروجی چاپ کند.
حال به منظور تست عملکرد اپلیکیشنمان که با استفاده از الگوی طراحی فِساد طراحی شده است، کدهای قبلی را از فایل index.php
پاک کرده و کدهای زیر را در آن مینویسیم:
require_once 'Twitter.php';
require_once 'Facebook.php';
require_once 'GooglePlus.php';
require_once 'SocialMedia.php';
$socialMedia = new SocialMedia(new Twitter(), new Facebook(), new GooglePlus());
$socialMedia->shareMessage('Facade Design Pattern')->shareonSocial();
در کد فوق ابتدا فایل کلاسهای مربوطه را ایمپورت کرده و در ادامه آبجکتی تحت عنوان socialMedia$
از کلاس SocialMedia
ساخته و در ادامه از هر سه کلاس مد نظر خود آبجکتهایی ساختهایم و آنها را به عنوان پارامتر ورودی به آبجکت منتسب به پراپرتی socialMedia$
میدهیم که بدین ترتیب کانستراکتور تعریفشده در این کلاس هر یک از آبجکتهای ورودی را به ترتیب به پراپرتیهای زیر منتسب میکند:
$twitter
$facebook
$googleplus
در سطر بعد نیز متد ()shareMessage
را روی هر یک از آبجکتهای مذکور فراخوانی کردهایم و بدین ترتیب به تمامی متدهای مد نظر خود از هر سه کلاس دسترسی خواهیم داشت و میتوانیم آنها را فراخوانی کنیم که در همین راستا نتیجۀ حاصل از فراخوانی این متد که بازگرداندن استرینگ مربوط به پارامتر ورودی است را به متد ()shareOnSocial
میدهیم تا استرینگ مذکور را با استرینگ مد نظر از کلاس مربوطه کانکت کرده و در خروجی چاپ کند. در نهایت، خروجی حاصل از اجرای کد فوق استرینگهایی به ترتیب زیر خواهد بود:
Sharing by Facade Design Pattern on Twitter.
Sharing by Facade Design Pattern on Facebook.
Sharing by Facade Design Pattern on Google+.
همانطور که میبینید، با پیادهسازی تعداد خطوط بسیار کمتری توانستیم به هر سه کلاس توئیتر، فیسبوک و گوگلپلاس دسترسی داشته و متدهای مد نظر را از آنها فراخوانی کنیم.
جمعبندی
به طور کلی، پیادهسازی Facade Design Pattern برای تسهیل توسعه و سادهسازی کار با اپلیکیشنهایی مناسب میباشد که پیچیدگی بالایی داشته و در آن چندین کلاس و متد مختلف پیادهسازی شدهاند به طوری که با پیادهسازی یک کلاس به اصطلاح Facade میتوان به تمامی کلاسهای اپلیکیشن دسترسی یافته و متدهای مد نظر از آنها را فراخوانی کرد.