ویرگول
ورودثبت نام
Melika Zare
Melika Zare
Melika Zare
Melika Zare
خواندن ۱۱ دقیقه·۱ سال پیش

Facade Pattern

تصور کنید دارید روی پروژه‌ای کار می‌کنید که با کلی کد پیچیده و کلاس‌های مختلف سروکار دارید. هر بار که می‌خواهید تغییر کوچکی بدهید، باید با کلی جزئیات فنی سروکله بزنید. حتی ممکن است نگران باشید که اشتباهی در بخشی از کد باعث شود کل سیستم از کار بیفتد. آشنا به نظر می‌آید، نه؟

الگوی طراحی Facade در این شرایط مطرح میشود. این الگو به شما کمک می‌کند تا این پیچیدگی‌ها را پشت یک دیوار ساده پنهان کنید و فقط با چیزهایی کار کنید که لازم دارید. Facade مثل یک واسطه عمل می‌کند که با وجود پیچیدگی‌های پشت صحنه، کار را برای شما راحت‌تر و سرراست‌تر می‌کند.

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

الگوی طراحی Facade بخشی از مجموعه الگوهای طراحی است که در کتاب معروف "Design Patterns: Elements of Reusable Object-Oriented Software" معرفی شد.

این کتاب که توسط چهار نویسنده، معروف به Gang of Four (GoF)، در سال 1994 منتشر شد، اولین مرجع رسمی برای دسته‌بندی و توصیف الگوهای طراحی بود.

بنابر گفته  GoF هدف از الگوی Facade عبارت است از:

تهیه یک واسط يكپارچه برای مجموعه‌ای از واسط‌ها در یک زیرسیستم. Facade، با تعریف یک واسط سطح بالاتر، استفاده از زیرسیستم را سـاده‌تـر مـی‌كنـد.

مفهوم الگو ؟

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

حتی در جنگ‌ها، ایده Facade به نوعی استفاده شده است. در ارتش‌ها، فرماندهان معمولاً اطلاعات را به صورت خلاصه و ساده به سربازان منتقل می‌کنند، نه جزئیات پیچیده استراتژی‌ها. این کار باعث می‌شود وظایف به درستی اجرا شود بدون اینکه کل ساختار در معرض دید قرار گیرد.

به عنوان مثال تصور کنید شما به عنوان مدیر پروژه برای یک پروژه نسبتا بزرگ انتخاب شده‌اید. در حالت اول شما خودتان وظیفه دارید مسئولیت تک تک افراد پروژه را به آن‌ها بگویید و وظایف واگذار شده به آن‌ها را تحویل بگیرید. در این حالت شما وظیفه دارید با تک تک افراد ارتباط برقرار کنید و همچنین برای بازخواست وظایف، شما باید نحوه ارتباط این افراد را با یکدیگر بدانید.

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

تاریخچه این الگو

واژه "Facade" برای اولین بار در زبان انگلیسی در قرن 17 میلادی از کلمه فرانسوی "façade" به معنی "صورت" یا "چهره" وارد شد. این انتخاب نشان می‌دهد که این الگو به جای نمایش تمام جزئیات، فقط "صورت" ساده‌ای از سیستم را به نمایش می‌گذارد.

اسم Facade را از معماری ساختمان‌ها وام گرفته‌اند. چرا؟ زیرا وقتی مقابل یک ساختمان شیک می‌ایستید، تنها نمای زیبا و مرتب آن را مشاهده می‌کنید. اما از آنچه در زیرزمین می‌گذرد، بی‌خبر می‌مانید؛ اینکه لوله‌کشی‌ها تا چه حد پیچیده طراحی شده‌اند، سیم‌کشی‌ها چگونه تنظیم شده‌اند، یا کارگرانی که چطور تلاش کرده‌اند تا این بنا شکل بگیرد. تنها چیزی که در نگاه اول به چشم می‌آید، همان ظاهر جذاب و منظم است، نه جزئیات پشت پرده.

مفهوم در نرم‌افزار؟

همین مفهوم را به دنیای برنامه‌نویسی وارد کرده‌اند. Facade Pattern دقیقاً همان نمای شیک و مرتب است. برنامه‌نویسان از این الگو بهره می‌برند تا پیچیدگی‌ها را پشت یک رابط ساده پنهان کنند. به زبان ساده، این الگو به کاربران می‌گوید: "نگران این‌همه کلاس و متد پیچیده نباشید؛ اینجا یک رابط ساده برایتان طراحی شده که کارتان را راه می‌اندازد." به این ترتیب، کاربر با سیستمی ساده و قابل‌درک مواجه می‌شود، درحالی‌که تمام پیچیدگی‌ها و جزئیات همچنان پشت پرده باقی می‌مانند.

برای درک بهتر، کاری که Controller در پترن MVC انجام میدهد یا حتی وقتی دارید از Dependency Injection استفاده میکنید، در حقیقت از مفهومِ الگوی Facade هم استفاده میکنید.

این مفهوم را می‌توان به خرید یک ماشین هم تشبیه کرد. هنگام خرید خودرو، تنها با فرمان، پدال و دنده سروکار دارید و از این طریق ماشین را هدایت می‌کنید. اما هیچ‌گاه به جزئیات عملکرد موتور یا نحوه حرکت سوپاپ‌ها نمی‌پردازید. Facade Pattern نیز به همین شکل، پیچیدگی‌ها را پشت نمای ساده خود پنهان می‌کند!

الگوریتم الگو :

برای درک بهتر این الگو، بهتر هست با مفاهیم پایه برنامه‌نویسی شیءگرا مثل کلاس‌ها و ارتباطات بین آن ها آشنا باشید. بدانید الگوهای طراحی چی هستند و چرا استفاده میشوند، و بتوانید یک رابط ساده طراحی کنید که پیچیدگی سیستم را پنهان کند.

برای اینکه الگوریتم این الگو را به ساده‌ترین و جذاب‌ترین شکل ممکن توضیح بدهیم، مراحل زیر را در نظر بگیرید. Facade Pattern مثل یک مدیر پروژه عمل می‌کند که وظایف را به اعضای تیم محول می‌کند، اما خودش فقط با مشتری در ارتباط است. حالا این ایده را قدم به قدم بررسی کنیم:

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

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

در کلاس Facade، متدهایی تعریف می‌کنید که درخواست‌های کاربر را دریافت و آن‌ها را به کلاس‌های داخلی ارسال می‌کند. Facade می‌داند که برای انجام هر وظیفه، باید کدام کلاس‌ها را فراخوانی کند و چطور آن‌ها را باهم ترکیب کند.

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

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

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

فرض کنید می‌خواهید برنامه‌ای بنویسید که ویدیوهای کوتاه و بامزه از گربه‌ها را در شبکه‌های اجتماعی آپلود کند. این برنامه ممکن است به کتابخانه حرفه‌ای تبدیل ویدیو نیاز داشته باشد. اما واقعیت این است که تنها چیزی که نیاز دارید، کلاسی با یک متد ساده به نام encode(filename, format) است. با ایجاد چنین کلاسی و اتصال آن به کتابخانه تبدیل ویدیو، شما اولین Facade خود را ساخته‌اید.

اجزای الگوی Facade

در شکل زیر کلاس دیاگرام مربوط به این الگو را می‌بینید.

  • کلاس Facade: کلاسی که تمام زیرسیستم‌ها را می‌شناسد و می‌داند جواب درخواست کلاینت را باید از کجا بگیرد.
  • کلاس Subsystem : پیاده‌سازی وظایفی که یک زیرسیستم دارد، راه‌اندازی درخواست‌هایی که از طرف Facade صادر می‌شود در حالی که هیچ دانشی در خصوص Facade و هیچ ارجاعی به آن ندارد.

چگونه پیاده‌سازی کنیم؟

  1. ابتدا بررسی کنید که آیا می‌توانید همه چیز را ساده‌تر کنید.
    ببینید آیا می‌توانید یک رابط (Interface) طراحی کنید که کاربران فقط از طریق آن کار کنند و نیازی به درگیری با جزئیات پیچیده سیستم اصلی نداشته باشند. اگر این کار باعث شد کد کاربران ساده‌تر و مستقل‌تر شود، مسیر درستی را انتخاب کرده‌اید.
  2. یک کلاس Facade ایجاد کنید.
    این کلاس مسئول هدایت درخواست‌های کاربران به بخش‌های مناسب سیستم است. همچنین باید اطمینان حاصل کند که سیستم به درستی مقداردهی اولیه (Initialize) شده و در طول کار به‌خوبی عمل می‌کند (مگر اینکه کاربران خودشان قبلاً این کار را انجام داده باشند).
  3. ارتباط کاربران را تنها از طریق Facade انجام دهید.
    کاری کنید که کدهای کاربران فقط از طریق Facade با سیستم ارتباط برقرار کنند و به‌طور مستقیم با سیستم اصلی درگیر نشوند. این روش کمک می‌کند که اگر روزی سیستم اصلی ارتقا یا تغییر یافت، تنها نیاز باشد کد Facade اصلاح شود، نه تمام کدهای کاربران.
  4. در صورت شلوغ شدن، وظایف را از هم جدا کنید.
    اگر Facade بیش از حد بزرگ و پیچیده شده است، بخشی از وظایف آن را به یک Facade کوچک‌تر انتقال دهید تا ساختار سیستم مرتب‌تر شود.

+ یک کلاس Facade اضافی می‌تواند ایجاد شود تا از آلوده شدن یک Facade اصلی به ویژگی‌های نامرتبط جلوگیری کند. این کار باعث می‌شود Facade به ساختاری پیچیده و دست‌وپاگیر تبدیل نشود. این Facade‌های اضافی می‌توانند هم توسط کاربران (Clients) و هم توسط دیگر Facade‌ها مورد استفاده قرار گیرند.

در کل کلاس‌های زیرسیستم هیچ اطلاعی از وجود Facade ندارند. آن‌ها به‌طور مستقل در سیستم عمل می‌کنند و مستقیماً با یکدیگر تعامل دارند.

مثال از کاربرد در برنامه نویسی:

فرض کنیم می‌خوایم به کاربرهایمان SMS بدهیم و برای این کار کتابخونه‌ای داریم که به ما اجازه میدهد از سرویس‌دهنده‌های مختلفی استفاده کنیم.

کدهایی که این کتابخونه برای ارسال پیام به ما ارائه میدهند به این صورت هست:

class SmsLibrary {
constructor(client_id, client_secret, driver) {
// ...
}

public recipient(phone_number) {
// ...
}

public send(text) {
// send
}
}

که برای استفاده از آن باید چنین کدی بنویسیم:

const client_id = config('sms.client_id');
const client_secret = config('sms.client_secret');
const sms_driver = config('sms.driver');

const sms = new SmsLibrary(client_id, client_secret, sms_driver);

sms.recipient('+989...');
sms.send('Welcome. Our app is great!');

همانطور که می‌بینیم برای ارسال پیام باید کتابخونه را به این صورت پیاده‌سازی کنیم. کار زمانی سخت میشود که می‌خواهیم در چندین قسمت دیگه از برنامه چنین کاری را انجام بدهیم. الگوی Facade اینجا به کمک ما میاید تا این پیچیدگی‌ها رو حذف کنیم و در نتیجه کدهای ساده‌تر و با قابلیت استفاده مجدد داشته باشیم!

راه حل با استفاده از الگوی Facade:

می‌خواهیم ارسال SMS را با این الگو بنویسم. برای این کار ابتدا یک کلاس می‌سازیم. این کلاس، همان کلاس Facade ما هست که در آن جزییات پیاده‌سازی کتابخونه را می‌نویسیم:

class SmsFacade {
public static send(text, recipient) {
const client_id = config('sms.client_id');
const client_secret = config('sms.client_secret');
const sms_driver = config('sms.driver');

const sms = new SmsLibrary(client_id, client_secret, sms_driver);

sms.recipient(recipient);
sms.send(text);
}
}

حالا هر جایی از برنامه که می‌خواهیم پیام ارسال کنیم، کافی هست که این کلاس را اضافه و به این صورت از آن استفاده کنیم:

SmsFacade.send('Welcome!', '+989...');
// ...
SmsFacade.send('Your 2FA code', '+001...');

همونطور که می‌بینیم، این الگو نقش یک رابط را بین کاربر (قسمتی که از کتابخونه استفاده می‌کند) و کدهای اصلی کتابخونه بازی می‌کند. این الگو فقط دستورات ر ا منتقل می‌کند و خودش تاثیری درخروجی ندارد. درست مثل پیشخدمت که خودش تاثیری در خروجی غذایی که می‌خواهیم ندارد و فقط دستورات را منتقل می‌کند.

+ همچنین کتابخانه پیچیده ی خارجی که آن را توی Facade می‌نویسیم، اصلا از وجود Facade بی‌خبر هست و برایش فرقی نمی‌کند که به صورت مستقیم دارد استفاده میشود و یا توسط Facade !

کد کامل این قسمت روی گیت هاب

مزایا:

  • کار شما را از پیچیدگی‌های سیستم اصلی جدا می‌کند و دیگر نیازی نیست با جزئیات آن درگیر شوید. استفاده از Facade نه تنها پیچیدگی را کاهش می‌دهد، بلکه خوانایی کد را نیز افزایش می‌دهد.
  • استفاده از سيستم را آسان‌تر می‌کند.
  • واسط کلاس Facade می‌تواند  به طور کامل با واسط زیرسیستم‌ها  تفاوت داشته باشد.
  • به ازای هر تعدادی از زیرسیستم‌ها می‌توان یک Facade تعریف کرد. البته اگر تعداد زیر سیستم انتخابی برای یک Facade کم باشد، تعداد زیاد Facadeها، باعث می‌شود که اتصال بین کلاس‌ها سست شود.
  • اجازه لایه‌بندی سیستم را به ما می‌دهد.

معایب:

  • اگر دقت کافی نداشته باشید، Facade می‌تواند به یک شیء بزرگ و وابسته به تمام بخش‌های برنامه تبدیل شود که خودش می‌تواند مشکل‌ساز باشد.
  • الگوی Facade بسیار کلی است و به طور دقیق مشخص نمی‌کند چگونه باید Facade را توسعه دهید. به عبارت دیگر پیاده‌سازی آن شفاف نیست. برای نمونه:
  • مشخص نمی‌کند چگونه باید واسط یک زیرسیستم شلوغ و پیچیده را ساده کنید.
  • مشخص نمی‌کند Facade خودش چگونه باید با زیرسیستم و کلاس‌های آن کار کند.

زمانی از این الگو استفاده کنید که :

  • یک یا چند زیرسیستم پیچیده دارید و می‌خواهید واسط آن‌ها را ساده کنید.
  • می‌خواهید کلاینت‌ها، در برابر توسعه و تغییرات زیرسیستم‌های شما آسیب نبینند.
  • می‌خواهید سیستم خود را لایه‌بندی کنید.

جمع بندی

این الگو مثل یه منشی حرفه‌ای هست که ما را از دردسر سیستم‌های پیچیده نجات می‌دهد. به جای اینکه ما با تعداد زیادی از کلاس ها و متد ها سر و کله بزنیم، فقط با یه رابط ساده (Facade) کار می‌کنیم و بقیه کارها خودشان در پشت صحنه حل‌ و فصل می‌شوند. مزیت آن: کار راحت‌تر، کد مرتب‌تر. ولی اگر دقت نکنید، Facade می‌تواند خودش یک مشکل بزرگ بشود .



الگوهای طراحیfacade
۲
۱
Melika Zare
Melika Zare
شاید از این پست‌ها خوشتان بیاید