در این آموزش قصد داریم تا به بررسی 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 میتوان به تمامی کلاسهای اپلیکیشن دسترسی یافته و متدهای مد نظر از آنها را فراخوانی کرد.
