تا کنون 23 الگوی طراحی برای رفع مشکلات متداول در برنامه نویسی شی گرا توسعه داده شده است که یکی از آنها الگوی طراحی کارخانه یا Factory Method است. الگوی طراحی کارخانه در دسته الگوهای طراحی سازنده یا Creational قرار دارد و به شما در مدیریت ایجاد اشیا کمک میکند. این الگوی طراحی، راهکاری در اختیار برنامه نویسان قرار میدهد تا آنها را قادر سازد که بدون اینکه کلاس دقیق یک شی را مشخص کنند آن را ایجاد کنند و به استفاده از آن بپردازند. در ادامه این مقاله با الگوی طراحی Factory Method آشنا خواهیم شد و خواهیم دید چه زمانی استفاده از این الگوی طراحی در کدنویسی به کمک ما خواهد آمد.
چرا باید از الگوی طراحی Factory استفاده کنیم
در بعضی موارد اپلیکیشنهای ما بسیار بزرگ و پیچیده هستند و یا در آینده گسترش خواهند یافت. معمولا این اپلیکیشنها دارای تعداد خیلی زیادی کلاس (Class) هستند که از همه آنها نمونه و شی (Object) ساخته میشود. تصور کنید که اپلیکیشن شما دارای 500 کلاس باشد که از هر کدام از آنها باید به صورت میانگین 10 شی و نمونه ساخته شود. بنابراین در نرم افزار حدود 5000 هزار شی از کلاسها مختلف ساخته شده است. مثلا کلاسی مثل User که در سرتاسر اپلیکیشن مورد استفاده قرار میگیرد.
بنابراین اگر مثلا بخواهیم به Constructor مربوط به کلاس User پارامتری جدید اضافه کنیم، باید کلاس User را تغییر دهیم. این کار باعث میشود که در تمام پروژه، هر جایی که از آن کلاس شی ای ساخته شده است پارامتر جدید را مقداردهی و اضافه کنیم که کاری بسیار دشوار و زمان بر خواهد بود. اما با استفاده از الگوی طراحی Factory برای ساخت اشیا کارخانه ای خواهیم ساخت که وظیفه ساخت اشیا از کلاسهای مختلف را بر عهده دارد. با این کار دیگر نیازی به ساخت شیها با استفاده از کیورد new به صورت جداگانه نخواهیم داشت و کافی است برای ساخت اشیا از کارخانه ساخت آن کلاس استفاده کنیم.
مثلا برای ساخت شیها و نمونه هایی از کلاس User از Userfactory استفاده میکنیم. با این کار اگر نیاز به اعمال تغییراتی در کلاس مورد نظر خود داشته باشیم، کافی است تغییرات در کارخانه یا Factory مربوط به آن کلاس اعمال کنیم. پس از این عمل، تغییرات اعمال شده توسط کارخانه بر تمام کلاسهای ساخته شده در آن، لحاظ میشوند.
مثالی از کاربرد الگوی طراحی Factory
تصور کنید که وظیفه طراحی نرم افزاری برای مدیریت حمل و نقل اجناس تولیدی یک کارخانه بر عهده شما باشد. اولین نسخه ای که از این نرم افزار میسازید فقط میتواند حمل و نقل جاده ای را مدیریت کند. بنابراین بخش عمده ای از کدهای نوشته شده توسط شما در کلاس کامیون (Truck) نوشته شده اند. سپس بعد از مدتی نرم افزاری که طراحی کرده اید مورد توجه کاربران و مدیران کارخانه قرار میگیرد و به این دلیل تعداد زیادی درخواست برای توسعه نرم افزار به منظور پشتیبانی از مدیریت حمل و نقل دریایی دریافت میکنید.
قطعا اولین سوالی که در ذهن شما شکل میگیرد مربوط به چگونگی پیاده سازی ویژگیهای جدید مربوط به حمل نقل دریایی است. زیرا در حال حاضر، بیشتر کد شما در کلاس مربوط به کامیونها وارد شده است. اگر بخواهید کشتیها (Ships) را به نرم افزار خود اضافه کنید، نیازمند تغییر در تمام کدهای اصلی نرم افزار خواهید بود. اگر در آینده دوباره تصمیم بگیرید نرم افزار را گسترش دهید، همچنین با این چالشها مواجه خواهید شد. اگر بخواهید این چالشها را به صورت سنتی حل کنید، نرم افزار شما انعطاف پذیر نخواهد بود و امکان دارد با مشکلات زیادی مواجه شود.
چه زمانی باید از الگوی طراحی Factory استفاده کنیم
کاربرد این الگوی طراحی برای شرایطی است که چندین کلاس ما از یک کلاس دیگر ارث بری کرده باشند و معمولا از آن کلاس نمونه یا شی ساخته میشود. همچنین این الگوی طراحی در موارد دیگر هم نیز استفاده میشود. این موارد عبارتند از :
کلاس هایی داشته باشیم که از آنها شیهای زیادی ساخته میشود. با این کار برنامه نویس دیگر اشیا را ایجاد نخواهد کرد بلکه تمام مسئولیت ایجاد کلاس را به Factory Method واگذار میکند.
قبل از ایجاد شی هایی که نرم افزار با آنها کار میکند، نوع و وابستگی مربوط به آن ها را ندانید.
می خواهید با استفاده از اشیا موجود و جلوگیری از ساختن دوباره آن ها، در منابع سیستم صرفه جویی کنید.
پیاده سازی الگوی طراحی Factory
در این بخش به بررسی مراحل پیاده سازی الگوی طراحی Factory میپردازیم، سپس با استفاده از زبان برنامه نویسی PHP این الگو را در قالب یک مثال برای تولید ماشینهای سواری پژو 405 و سمند پیاده سازی خواهیم کرد. در این مثال با استفاده از الگوی طراحی کارخانه دیگر نیازی به تعیین ویژگیهای هر ماشین در زمان تولید آن به صورت تک به تک نخواهیم داشت. زیرا با ساخت کارخانه تولیدی ماشینهای سواری و مشخص کردن ویژگیهای هر یک از آن ها، وظیفه ساخت این خودروها بر عهده کارخانه خواهد بود. مراحل پیاده سازی این الگو عبارتند از :
ابتدا باید یک Interface تعریف کنیم که در آن توابعی تعریف شود که در همه اشیا مشترک و کاربردی است. سپس تمام اشیا باید از یک Interface پیروی کنند.
باید یک تابع به عنوان کارخانه سازنده (Factory) آن شی در کلاسش اضافه شود. نوع متغیری که این تابع Return میکند باید با Interface که در بخش قبل ساخته شد، مطابقت داشته باشد.
در کدهای نوشته شده هرجایی که اشیا توسط روش سنتی (به وسیله کیورد new) ایجاد شده اند باید پیدا شوند و برای ساخت آنها از تابع کارخانه سازنده آنها استفاده شود.
باید مجموعه ای از کلاسهای فرزند سازنده (creator) برای انواع اشیایی که کارخانه میسازد، تعریف شوند. سپس در این کلاس ها باید تابع کارخانه بازنویسی (Override) شود تا اصولی که در این تابع تعریف شده اند، به شکل مورد نیاز تغییر داده شوند. مثلا اگر در نحوه ساخت یک خودروی سواری و خودروی باربری تفاوتی وجود دارد، در کلاسهای مربوط به خود این موضوع در نظر گرفته شود.
اگر انواع مختلف و زیادی از اشیا وجود دارند که از نظر شما ساختن کلاسهای فرزند برای همه آنها منطقی به نظر نمیرسد و دارای ویژگیهای مشترکی هستند، میتوانید آن ویژگی را در کلاس والد وارد کنید و از تعریف مکرر آن در کلاسهای فرزند خودداری کنید.
اگر مشاهده کردید میان اشیای ساخته شده توسط تابع کارخانه هیچ ویژگی مشترکی وجود ندارد و تابع کارخانه خالی مانده است، میتوانید آن را به صورت abstract تعریف کنید. همچنین اگر خلاف این امر صادق بود و ویژگی در این تابع باقی ماند، آن ویژگی را میتوان، یک ویژگی پیشفرض برای اشیا در نظر گرفت.
<?php
namespace Web7learn\FactoryMethod\CarFactory;
/**
* The CarFactory class declares the carfactory method that is supposed to return an
* object of a Car class. The CarFactory's subclasses usually provide the
* implementation of this method.
*/
abstract class CarFactory
{
/**
* Note that the CarFactory may also provide some default implementation of the
* carfactory method.
*/
abstract public function buildcar(): Car;
/**
* Also note that, despite its name, the CarFactory's primary responsibility is
* not creating Cars. Usually, it contains some core business logic that
* relies on car objects, returned by the buildcar method. Subclasses can
* indirectly change that business logic by overriding the factory method
* and returning a different type of Car from it.
*/
public function someOperation(): Type
{
//U can Call the buildCar method to create a car object and use that Here.
}
}
/**
* Concrete Car Factories override the factory method in order to change the
* resulting car's type.
*/
class PeugeotFactory extends CarFactory
{
/**
* Note that the signature of the method still uses the abstract Car
* type, even though the concrete Car is actually returned from the
* method. This way the CarFactory can stay independent of concrete Car
* classes.
*/
public function buildcar(): Car
{
return new Peugeot405;
}
}
class SamandFactory extends CarFactory
{
public function buildcar(): Car
{
return new Samand;
}
}
/**
* The Car interface declares the operations that all concrete Cars must
* implement.
*/
interface Car
{
public function operation(): Type;
}
/**
* Concrete Cars provide various implementations of the Car interface.
*/
class PeugeotFactory implements Car
{
public function operation(): Type
{
return "{Result of the Peugeot405}";
}
}
class SamandFactory implements Car
{
public function operation(): Type
{
return "{Result of the Samand}";
}
}
اگر میخواهید بیشتر راجب الگوهای طراحی بدانید, مقالات زیر را مطالعه کنید
پیاده سازی الگوی طراحی Factory برای نرم افزارهایی که امکان دارد در آینده گسترش یابند بسیار مفید است. پیاده سازی این الگوی طراحی در نرم افزارها به افزایش انعطاف آنها در برابر تغییرات بسیار کمک میکند. همچنین این الگو باعث بهینه سازی در مصرف منابع خواهد شد. نظر شما در مورد الگوی طراحی کارخانه چیست؟ آیا تاکنون از این الگوی طراحی در طراحی نرم افزارهای خود استفاده کرده اید؟
۹ دیدگاه
۲۲ خرداد ۱۴۰۱، ۱۶:۳۵
در کل یعنی این که اگه چند کلاس داشته باشیم و توابع یا متغیرها نامشون مشترک باشه ولی بدنه شون فرق داشته باشه از الگوی factory استفاده میشه ، درسته ؟
نازنین کریمی مقدم۲۲ خرداد ۱۴۰۱، ۱۹:۳۵
بله تقریبا همین هست که فرمودید :)
Mahdi Niksirat۰۹ خرداد ۱۴۰۱، ۰۴:۵۵
در مثال خودرو در انتهای مطلب نام گذاری کلاس هایی که اینترفیس car رو implement کردند صحیح نیست و باعث گمراهی خواننده میشه.
علی۱۲ دی ۱۴۰۲، ۱۱:۲۶
این حرف درستیه لطفا رسیدگی کنید.
مسعود ابراهیمی۱۱ اسفند ۱۳۹۹، ۱۱:۴۷
خیلی ممنون از این مقاله کاربردی
فقط لطف کنید یک مثال عملی بزنید که توی پروژههای برنامه نویسی به کار بیاد و ما بدونیم که این الگو کجا کاربرد داره
سپاس از شما
نازنین کریمی مقدم۱۳ اسفند ۱۳۹۹، ۰۰:۲۷
درود.
این الگوهای طراحی هرکدوم یک مثال معروف دارند تا هم فهمشون راحتتر بشه و هم بهتر در یاد بمونند.
مثال الگوی factory، پیتزا فروشی هست. فرض کنید شما میخواهید که یک سامانه فروش غذا رو پیاده سازی کنید.
شما یک کلاس باجه دارید که سفارشات به اون ارسال میشوند.
یک کلاس آشپزخانه دارید که سفارشات رو یکی یکی از باجه گرفته و اونها رو بپزه. روند پخت همه پیتزاها یکی هست، همه شون باید محتویات روی خمیر ریخته و در فر قرار بگیرند. اما براساس اینکه اسم پیتزا چی هست، محتویات متفاوته.
اینجا میایم یک کلاس پخت پیتزا میگیریم و بقیه پیتزاها (پپرونی، سبزیجات،مخلوط و...) ازش ارث بری میکنند و متدها رو براساس نیاز override میکنند.
یک مثال فنی دیگه هم مثلا میتونه این باشه که شما یک کلاس دکمه دارید و چندین نوع دکمه ازش ارث بری میکنید (تاگل باتن، سلکت باتن و...)
حمید۱۸ مرداد ۱۳۹۹، ۱۶:۰۲
سلام و عرض ادب و احترام.
ممنون از بابت مقالات مفیدتون در باره دیزاین پترن.
ضمن تایید صحبتهای آقا امین به نظرم این مثالی که زدید abstract factory هست. نه factory,
نازنین کریمی مقدم۰۸ مهر ۱۳۹۹، ۲۲:۲۲
درود. حق با شماست.
انشالله در آینده که این سری مقالات رو تجمیع کردیم مثال رو هم اصلاح خواهیم کرد.
امین۰۷ مهر ۱۳۹۸، ۰۸:۴۹
با سلام
ممنون بابت اشتراک گذاری مقالات دیزاین پترن. این مقالات واقعا مفید و در اکثر سایتها به صورت معرفی سطحی یا ترجمههای یکسان موجود میباشد.
نظرات پیشنهادی:
* بهتر است منبع ترجمه را در انتهای مقاله ذکر کنید و متن کامل منبع را هم ترجمه و در همین مقاله قرار دهید و بعد مثال خود را (نمونه کد موجود در پست بالا در مورد تولید پژو یا سمند) هم در انتهای آن بیاورید.
* در انتهای کد, در نام دو کلاس آخر نباید از کلمه فکتوری استفاده شود. آنها کلاسهای فکتوری نیستند و باید به صورت Peugeot و Samand نامگذاری شوند.
با تشکر