پترن Abstract Factory به زبان ساده

یکی از دیزاین پترنایی که معمولا دولوپرا تو فهمش مشکل دارن AFD یا همون Abstract Factory Design Pattern هستش تو این پرسش و پاسخ سعی کردم خیلی ساده این پتترنو توضیح بدم .خب قبلش باید Factory Pattern رو توضیح بدم که شاید از قبل میدونینش ولی بد نیست یه بار دیگه مرورش کنیم .+ کاربرد factory pattern چیه ؟ - توی OOP یه Factory ابجکتیه که ابجکتای دیگه رو میسازه !!+ چه نیازی هست به اینکار وقتی میتونیم هرجا خواستیم با new ابجکت رو بسازیم !؟-دیگه با پیچیدگی های ساخت ابجکت درگیر نمیشیم !!+ چه پیچیدگی ؟- مثلا فرض کن یه ابجکت میخوای که کلی dependency داره , باید هر جا که اون ابجت رو بسازی و همه dependency هارو به عنوان پارامتر بهش بدی !+ خب اینکارو بکنیم - اینجوری هم اصل DRY بهم میخوره هم کدت کثیف میشه .بریم سراغ مثال , فرض کن میخوایم ابجکت از شکل های مختلف بسازیم تو برناممون , یه interface داریم که استراتژی shape رو تعریف میکنه تا کلاسای شکل هایی که ما میخوایم پیاده ش کنن . interface shape{ public function draw(); }حالا مثلا یه کلاس میخوام واسه ساخت مربع :class Square2d implements shape{ public function draw(){ echo 'i am draw a square'; } }یه کلاس هم میخوام واسه ساختن یه دایره :class Circle2d implements shape{ public function draw(){ echo 'i draw a circle'; } }خب تا اینجا دوتا کلاس تعریف کردیم که هر کدوم ابجکت مخصوص خودشو میسازه حالا باید یه کلاس تعریف کنیم که اینارو بسازه واسه ما که میشه همون Factory .class Factory2d { public function createShape($shape){ if($shape === 'square'){ return new Square() } } }خب اینم از factory ما . حالا اگر بخوایم مثلا یه ابجکت شکل مربع درست کنیم به این صورت عمل میکنیم :,$factory = new Factory2d(); $shape = $factory->createShape('square'); $shape->draw();واسه ی دایره هم همینطور .+ خب این چه پیچیدگی ای رو کم کرد؟ خیلی ساده میتونستیم با new square2d() ابجکتمونو بسازیم .- این واسه مثال بود اگه square توی constructor ش شونصدتا دپندنسی میخواست چی ؟فقط یه بار توی متد createShape کلاس factory اینو تعریف میکردیم :if( $shape === 'square' ){ return new Square( dependency1 , denedency2 , dependency3 ......); }بعد از این دیگه فقط هر دفه به square نیازی باشه فقط کافیه $factory->createShape('square');صدا زده بشه .خب حالا بریم سراغ Abstract Factory .- پترن factory که بالا توضیح دادم تا یه جاهایی خوبه حالا فرض کن یه نوع شکل دیگه میخوایم که فرق میکنه باید چیکار کنیم ؟+ چه فرقی ؟- مثلا اینا اشکال ۲ بعدی بودن حالا ۳ بعدی میخوایم !+ خب کلاساشونو تعریف میکنیم دیگه مثلا اگر circle3D بخوایم که بازم همون اینترفیس shape رو پیاده میکنه اینکارو میکنیم ! class circle3D implements shape { public function draw(){ echo ' i am drawing a 3D circle'; } } + بعد به متد create توی کلاس factory شرطشو اضافه میکنیم :class factory { public function createShape($shape){ if ($shape === 'square'){ return new Square(); }else if($shape === 'circle'){ return new Circle(); }else if($shape === 'circle3D'){ return new circle3D(); } } }+ حالا راحت ابجکتشو میسازیم با factory :$factory = new Factory() ; $circle3D = $factory->createShape( 'circle3D' ); $circle3D->draw();- نه این درست نیست ! اول اینکه شد همون simple factory دوم اینکه تمام logic روی توی یه factory میذاری که این باعث میشه کنترل روی هر خانواده از ابجکت ها سختتر بشه !+یعنی چی ابجکت های هم خانواده ؟-منظورم اینه که ابجکت هایی که از یه دسته نیستن ولی شبیه به همن مثلا تو این مثال ما اشکال 2D رو یه خانواده در نظر میگیریم و اشکال 3D رو یه خانواده ی دیگه ! + چیکار کنیم حالا ؟-دیگه اینجا چون یه خانواده دیگه از ابجکتا میخوایم دیگه نمیایم تو همون simple factory قاطی اشکال 2D قرار بدیم میایم factory هامون رو abstract میکنیم و برای هر خانواده از ابجکت ها یه factory درست میکنیم , و در نهایت جایی که میخوایم ابجکت مد نظرمونو درست کنیم , اول مشخص میکنیم کدوم factory رو میخوایم بعد به اون فکتوری میگیم چه ابجکتی رو میخوایم . باید برای هر نوع ابجکت یه factory مجزا باشه .بعد همه این factory ها هم از یه abstract class به عنوان abstractFactoy یا هرچی که استراتژی factory هارو تعریف میکنه , ابسترکت میشن . دقت کن که توی Simple Factory این abstract class رو نداشتیم چون یه دونه factory بیشتر نداشتیم که کارش فقط ساختن یک مدل ابجکت بود , ولی اینجا چون factory های مجزا هستن و هرکدوم یه خانواده از ابجکتارو میسازن ولی استراتژیشون یکی هست , همشون از یه abstract class ابسترکت میشن .خب اول abstract class رو تعریف میکنیم :abstract class abstractFactory{ public function createShape(); }+چرا اینترفیس استفاده نکردیم ؟ -میتونی استفاده کنی ولی چون این factory ها کارشون شبیه به همه و ممکنه متد های مشترکی از لحاظ پیاده سازی داشته باشن پس بهتره abstract class استفاده کنی چون توی interface پیاده سازی نداریم.حالا یه factory واسه اشکال 2D که تعریف میکنیم که این abstractFactory رو پیاده میکنه :class Factory2d extends abstractFactory { public function createShape($shape){ if ($shape === 'square'){ return new Square2d(); }else if($shape === 'circle'){ return new Circle2d(); } } }یکی هم واسه 3d :class Factory3d extends abstractFactory { public function createShape($shape){ if ($shape === 'square'){ return new Square3d(); }else if($shape === 'circle'){ return new Circle3d(); } } }حالا میریم سراغ Factory Producer : + چی هست ؟- اون متد یا فانکشن یا ابجکتی که این factory هارو میسازه ! یعنی جریان اینجوریه که به Factory Producer میگیم factory چیزی که میخوایم رو بساز , مثلا میگی factory شکلای 2d رو میخوام, اونم بهت Factory2d رو برمیگردونه دیگه هرشکل 2D که میخوای با اون میسازی.function producer($shapeType){ if($shapeType === '2D' ){ return new Factory2d(); }else if ($shapeType === '3D'){ return new Factory3d(); } } حالا دیگه تو برنامه ت اینجوری استفاده میکنی , مثلا میخوام تو برنامم میخوام یه مربع بسازم :$factory = producer('2D') ; $square = $factory->createShape('square); $square->draw();یا مثلا دایره سه بعدی میخوام یا همون کره :$factory = producer('3D') ; $square = $factory->createShape('circle'); $square->draw();دتس ایت و دیگر هیچ ...نظری داشتین, چیزی کم و کسر بود لطفا کامنت کنین ...