همانطور که میدانید، الگوها راهحلهایی برای مشکلات رایج در طراحی شیءگرا هستند. در ادامه قصد داریم تا به بررسی الگوی طراحی Factory بپردازیم.
الگوی طراحی Factory، یک الگوی طراحیِ ایجادی (Creational) است که اینترفیسی را برای ایجاد اشیا در یک کلاس فراهم میکند، اما به زیرکلاسها اجازه میدهد تا نوع اشیایی که ایجاد میشوند را تغییر دهند. بهطور کلی الگوهای طراحیِ ایجادی به ارائهٔ راهکار برای ایجاد اشیا میپردازند. حال برای درک بهتر الگوی طراحی Factory خوب است ابتدا به ساختار آن توجه کنیم:
همانطور که از اسم این الگو هم پیداست، در این ساختار کارخانهای وجود دارد که نحوهٔ ایجاد اشیا را کنترل میکند. در این کارخانه، کلاس Creator وظیفهٔ ایجاد محصولات را بر عهده دارد و متدی را ارائه میدهد که یک شیء از نوع اینترفیس Product را برمیگرداند. همچنین، در این کارخانه میتوان بهوسیله ConcreteCreatorها محصولات مختلف را ایجاد کرد. در این ساختار، Product نیز اینترفیسی است که ویژگیهای محصولات ایجادشده توسط کارخانه را مشخص میکند. ConcreteProductها نیز پیادهسازیهای مختلف اینترفیس Product است. در این ساختار میتوان متد createProduct را بهصورت abstract تعریف کرد تا هر یک از ConcreteCreatorها متد را با توجه به نیاز خود پیادهسازی نماید.
مثال عملی از الگوی طراحی Factory
حال که با ساختار الگوی طراحی Factory آشنا شدیم خوب است که به بررسی مثالی عملی در مورد این الگو بپردازیم. فرض کنید میخواهیم قابلیتی را فراهم کنیم تا در پلتفرمهای مختلف یک صفحه را به شیوههای مختلفی به کاربر نمایش دهیم. این قابلیت قرار است هدر هر صفحه را مخصوص هر پلتفرم ایجاد و برگرداند. برای پیادهسازی این قابلیت با الگوی Factory باید بهصورت زیر عمل کنیم:
۱- ابتدا اینترفیس محصولات (در اینجا هدر) را میسازیم. توجه کنید که همهٔ محصولات باید این اینترفیس را پیادهسازی کنند:
// Product Interface
interface Header
{
public function render($content): void;
}
۲- حال باید کارخانه (کلاس Creator که در این مثال صفحه است) را بسازیم و در صورت نیاز زیرکلاسهای موردنیازمان (که در این مثال صفحه پلتفرمهای مختلف است) را ایجاد کنیم:
// Factory
// --------
// Creator
abstract class Page
{
abstract public function createHeader(): Header;
public function show($content): void
{
$network = $this->createHeader();
$network->render($content);
}
}
// Concrete Creator for Web
class WebPage extends Page
{
public function createHeader(): Header
{
return new WebHeader();
}
}
// Concrete Creator for Android
class AndroidPage extends Page
{
public function createHeader(): Header
{
return new AndroidHeader();
}
}
// Concrete Creator for IOS
class IOSPage extends Page
{
public function createHeader(): Header
{
return new IOSHeader();
}
}
۳- در این مرحله باید به پیادهسازی محصولات مختلف (که در این مثال هدر مخصوص هر پلتفرم است) برای تکمیل نمایش هر صفحه مربوط به یک پلتفرم خاص بپردازیم:
// Concrete Product for Web
class WebHeader implements Header
{
public function render($content): void
{
echo "Render Web Header Content: $content <br>";
}
}
// Concrete Product for Android
class AndroidHeader implements Header
{
public function render($content): void
{
echo "Render Android Header Content: $content <br>";
}
}
// Concrete Product for IOS
class IOSHeader implements Header
{
public function render($content): void
{
echo "Render IOS Header Content: $content <br>";
}
}
حال پیادهسازی ما تمام شده و با استفاده از قطعه کد زیر میتوانید این برنامه را تست کنید:
function showPage(Page $creator)
{
$creator->show("Hello world!");
$creator->show("Hello Quera!");
}
echo "Testing Concrete Creator for Web: <br>";
showPage(new WebPage());
echo "<br><br>";
echo "Testing Concrete Creator for Android: <br>";
showPage(new AndroidPage());
echo "<br><br>";
echo "Testing Concrete Creator for IOS: <br>";
showPage(new IOSPage());
با مشاهده خروجی خواهید دید که هدر متناسب با هر پلتفرم ایجاد خواهد شد.
احتمالاً تا اینجا متوجه شدهاید که این الگو دارای مزایا و معایبی است که خوب است برخی از آنها را نیز بررسی کنیم. اگر از دیدگاه اصول SOLID به این الگو نگاه کنیم، متوجه میشویم که کلاسهای موجود در ساختار این الگو به یکدیگر وابستگی ندارند و هر کلاس تنها به انجام وظیفهٔ خود میپردازد. بنابراین اصل Single Responsibility بهخوبی رعایت خواهد شد. همچنین در این الگو شما بهراحتی میتوانید زیرکلاسهای مدنظرتان را ایجاد کنید و به توسعهٔ برنامهتان بپردازید، پس مشخص است که اصل Open/Closed Principle نیز بهخوبی رعایت میشود. اما گاهی اوقات ممکن است با رشد پروژه، حجم زیر کلاسها برای پیادهسازی این الگو زیاد شود که این حالت از معایب این الگو بهشمار میآید. بنابراین در این شرایط باید از الگوی Factory استفاده کنیم:
- زمانی که از قبل، ویژگی دقیق اشیایی که قرار است با آنها کار کنیم را نمیدانیم.
- زمانی که میخواهیم به کاربران پروژهمان (کتابخانه یا چارچوب)، راهی برای توسعه اجزای داخلی آن ارائه دهیم.
- زمانی که میخواهیم مدیریت کاملی روی کلاسها برای ایجاد یا استفاده مجدد از اشیا داشته باشیم.
برای آشنایی بیشتر با سایر الگوهای طراحی و همچنین حل تمرینهای مختلف در رابطه با همین الگوها، میتوانید از دوره دیزاین پترن کوئرا استفاده کنید.
بیشتر بخوانید: دیزاین پترن چیست ؟ – هرچه باید درمورد الگوی طراحی (Design Pattern) بدانید