همانطور که در مقاله ی "الگوی طراحی simple factory(فکتوری)" گفته شد الگو فکتوری، سه حالت دارد:simple factory، factory methodو static factory. در این مقاله به بررسی دونوع دیگر آن پرداخته میشود.
به بیان ساده میتوان گفت که factory method یک راه برای ایجاد و توسعه اشیاء در subclass ها میباشد. زیر کلاسها مسئول ایجاد جزئیات هستند. این الگو داخل کلاس interface یا abstract تعریف شده و instantiation در زیر کلاسها انجام میشود.
مقاله ی خود را با یک مثال شروع میکنیم. دپارتمان استخدام نیروی جدید را درنظر بگیرید. وجود یک مصاحبه کننده برای تمام موقعیتهای شغلی غیر ممکن است. چون مصاحبه کننده به تخصص نیاز دارد. به همین دلیل بر اساس فرصتهای شغلی موجود، مدیر استخدام باید در مورد مصاحبه کننده تصمیم بگیرد و این موضوع را به افراد متفاوتی واگذار کند.
پیشنهاد این است که بجای ساخت مستقیم یک obj جدید از هریک از مصاحبه کننده ها از factory method استفاده کنید. در نگاه اول ، این حرکت ممکن است بی معنی به نظر برسد: ما فقط تماس سازنده را از یک قسمت برنامه به قسمت دیگر منتقل کردیم. با این حال ، این را در نظر بگیرید: اکنون می توانید روش دپارتمان را در یک زیر کلاس نادیده بگیرید و کلاس مصاحبه کننده ی ایجاد شده با این روش را تغییر دهید.
در مثال مدیریت استخدام، ابتدا interface مصاحبه کننده و نمونه هایی از آن را برای موقعیتهای شغلی مورد نظرمان پیاده سازی میکنیم.
interface Interviewer { public function askQuestions(); }
حال کلاسهایی که راجع به موقعیت های شغلی متفاوت هستند و باید این قرارداد تعریف شده در اینترفیس را تعریف کنند را میسازیم.
class Developer implements Interviewer { public function askQuestions() { echo 'Asking about design patterns!'; } } class Accounter implements Interviewer { public function askQuestions() { echo 'Asking about wages!'; } }
حال نوبت به پیاده سازی مدیر استخدام (HiringManager )است. که در اینجا همان تعریف فکتوری متد ما صورت میگیرد.
abstract class HiringManager { // factory method abstract protected function makeInterviewer(): Interviewer; public function takeInterview() { $interviewer = $this->makeInterviewer(); $interviewer->askQuestions(); } }
در این کلاس ابسترکت ما دو متد تعریف کردیم. که یکیشون مصاحبه کننده را میگیرد و دیگری مصاحبه کننده موردنظر را میسازد. با دقت در method factory در کلاس بالا متوجه میشوید که با این روش کلاسهای فرزند میتوانند به راحتی آن را توسعه دهند و البته که متد ابسترکت تعریف شده باید حتما با توجه به ماهیت کلاس و متد در کلاس فرزندان تعریف شود(منبع).
class DevelopmentManager extends HiringManager { protected function makeInterviewer(): Interviewer { return new Developer(); } } class AccounterManager extends HiringManager { protected function makeInterviewer(): Interviewer { return new Accounter (); } }
نحوه ی استفاده:
$devManager = new DevelopmentManager(); $devManager->takeInterview(); // Output: Asking about design patterns $marketingManager = new MarketingManager();$marketingManager->takeInterview(); // Output:Asking about wages!
قابلیت استفاده:
- زمانیکه وابستگی های بین اشیاء نامشخص است و نمیدانید چه زیر کلاسی دقیقا ً ممکن است نیاز باشد.
- وقتی چندین کلاس با منطق مشترک دارید و به دنبال راهی برای توسعه اجزای داخلی کلاس های اصلی هستید. method factory باعث میشود یک طراحی قابل تنظیم تر اما پیچیده داشته باشید.
این پیچیدگی بد نیست، چون با استفاده از آن به جای نیاز به ایجاد کلاس ها فقط عملیات جدید را پیاده سازی می کنید.
شما میتوانید مثال پرداخت را برای این مثال نیز درنظر بگیرید. و در کامنت ها به آن بپردازید.
یوزکیس بعدی هم که ازین الگوی طراحی استفاده میکند repository در لاراول است که در آینده به آن پرداخت میشود(منبع).
به بیان ساده Abstract Factory به شما امکان میدهد که خانواده ای از اشیا مرتبط بدون class concrete آنها را تولید کنید. همچنین به ما این امکان را میدهد که برای ایجاد اشیاء از یک الگوی کلی پیروی کنیم.
کلاسی که تمام متدهای آن پیاده سازی شده است و متدهای غیر قابل اجرا ندارند.
شما یک درب چوبی از فروشگاه درب های چوبی، درب آهنی از فروشگاه درب های آهنی و همچنین درب PVC از فروشگاه مرتبط نیاز دارید که تهیه کنید. به علاوه اینکه نیاز به متخصص برای نصب دربها نیز دارید. نجار برای درب چوبی و جوشکار برای درب آهنی. همانطور که میبینید بین دربها وابستگی هایی وجود دارد (نجار و جوشکار)
در ابتدا یک interface از درب و پیاده سازی هایی از آن را داریم:
interface Door { public function getDescription(); } class WoodenDoor implements Door { public function getDescription() { echo 'I am a wooden door'; } } class IronDoor implements Door { public function getDescription() { echo 'I am an iron door'; } }
سپس برای تعریف متخصص هم یک اینترفیس تعریف میکنیم و برای هرنوع درب یک متخصص خواهیم داشت(نجار و جوشکار)
interface DoorFittingExpert { public function getDescription(); } class Welder implements DoorFittingExpert{ public function getDescription() { echo 'I can only fit iron doors'; } } class Welder implements Carpenter{ public function getDescription() { echo 'I can only fit iron doors'; } }
تا اینجا کلاس های وابسته/مرتبط را پیاده سازی کردیم.
اکنون factory abstract را مینویسیم که امکان تشکیل خانواده اشیاء مرتبط را برای ما فراهم میکند. کارخانه درب چوبی یک درب و متخصص درب چوبی را ایجاد میکند و کارخانه درب آهنی هم یک درب و متخصص درب آهنی را میسازد.
interface DoorFactory { public function makeDoor(): Door; public function makeFittingExpert(): DoorFittingExpert; } // Wooden factory(concrete factory) to return carpenter and wooden door class WoodenDoorFactory implements DoorFactory { public function makeDoor(): Door {return new WoodenDoor(); } public function makeFittingExpert(): DoorFittingExpert { return new Carpenter(); } } // Iron door factory(concrete factory) to get iron door and the relevant fitting expert class IronDoorFactory implements DoorFactory { public function makeDoor(): Door { return new IronDoor(); } public function makeFittingExpert(): DoorFittingExpert { return new Welder(); } }
شیوه ی استفاده و تست :
$woodenFactory = new WoodenDoorFactory(); $door = $woodenFactory->makeDoor(); $expert = $woodenFactory->makeFittingExpert(); $door->getDescription();// Output: I am a wooden door $expert->getDescription(); // Output: I can only fit wooden doors
و برای درب آهنی:
// Same for Iron Factory $ironFactory = new IronDoorFactory(); $door = $ironFactory->makeDoor(); $expert = $ironFactory->makeFittingExpert(); $door->getDescription();// Output: I am an iron door $expert->getDescription(); // Output: I can only fit iron doors
قابلیت استفاده
- در یک طراحی خوب هر کلاس فقط مسئول یک چیز است. زمانیکه یک کلاس با انواع مختلفی از محصولات سروکار دارد نیاز به پیاده سازی این الگو احساس خواهد شد.
- هنگامی که سیستم نیاز به مستقل بودن از چگونگی ایجاد، ترکیب و نمایش آن دارد.
- وقتی ایجاد اشیاء مرتبط به سادگی امکانپذیر نیست. در صورتی که وابستگی های متقابل بین اشیاء وجود دارد instantiation آنها به صورت تکی/جداگانه منطقی خواهد بود.
برای مشاهده مثال و توضیح متفاوت و بدون درنظر گرفتن زبان خاصی میتوانید به سایت refactoring.guru مراجعه کنید.
درخواستی هم که دارم در صورتیکه useCase ای برای این الگو داشتین کامنت بزارید بلکه بتوانیم در زمینه ی برنامه نویسی به روند پیشرفت منابع فارسی کمکی کرده باشیم.