
بسم الله
سلام
مشکل یادگیری #الگوهای_طراحی (design pattern) در قالب مثالهایی با کلاسهای «Foo» و «Bar» یا کلاسهای «Vehicle»، «Bus»، «Bicycle» و «Car»، اینه که بعدا توی مدلسازیها، ذهن به سختی میتونه متناظر این مفاهیم رو توی کار پیدا کنه به خصوص این که همیشه مفاهیم به این شفافی نیستن و ذهن به تمرینهای انتزاعیتری نیاز داره. به همین دلیل مثالهایی که آدم از روی تجربه باهاشون برخورد میکنه میتونن تمرین خوبی برای یادگیری بهتر این الگوها باشن.
لازم بود که نقطه/خط/چندضلعی رو به صورت عکسی رندر کنم (یعنی به عکس تبدیل کنم). برا این کار حداقل ۴ روش رندر وجود داشت که اینجا مهم نیست اونا چی هستن. خب اولین چیزی که به ذهن ما میرسه اینه که بیاییم و یه enum برا روشهای مختلف تعریف کنیم و بر اساس اون توی کد رندر رو انجام بدیم (شکل زیر):

این کد یه مجموعه نقطه/خط/چندضلعی رو در قالب feature می گرفت و یه عکس تولید میکرد. همه چی خوب بود. تا این که کندی سرعت اجرای این رندرینگها وقتی تعداد شکلهای هندسی زیاد میشد اذیت کننده شد. برا همین مجبور شدیم یه روش رو امتحان کنیم و اون این بود که صفحه مانیتور رو به چندین تایل (Tile) با سایز ۲۵۶*۲۵۶ پیکلس تقسیم کردیم و رندرینگ هر تایل که تموم می شد به کاربر اونو نمایش میدادیم. هر چند که در مجموع ممکن بود رندر شدن کل تایلها زمان بیشتری از رندرینگ یکباره تمام صفحه ببره ولی چون اولین تایل سریعتر نمایش داده میشد، کاربر احساس بهتری داشت که سیستم داره یه کاری انجام میده. اما برای این روش تایل-مبنا هم ما مجبور شدیم مجدد همون switch بالا رو استفاده کنیم! پس تا این جا دو تا متد داریم که هر دو از یه switch استفاده کردن.
فرض کنید غیر از روشهای «رندرینگ یکباره تمام صفحه» و «رندرینگ تایل-مبنا» یه روش دیگه هم در آینده اضافه بشه اون وقت مجدد این switchرو باید استفاده کنیم. از طرفی اگه به خود روشهای رندرینگ هم یه چیزی اضافه بشه (مثلا Approach5 اضافه بشه) اون وقت باید یادمون باشه کجاها روی اون enum ما switch گذاشتیم و همه switch ها رو اصلاح کنیم.
همیشه وقتی برای انجام یه کاری چند روش (استراتژی) مستقل از هم وجود داره به یاد الگوی استراتژی بیفتید. این الگو، تصمیمگیری (کد switch) در مورد این که کدوم روش استفاده بشه رو به عهده یه کلاس به اسم Context میذاره و سایر جاها همه از اون استفاده میکنن و این طوری از تکرار کد جلوگیری میکنه.

حالا جاهای مختلف کد چطوری از این استفاده میکنن؟ (شکل زیر) همون طور که میبینید دیگه به اون switch نیازی نیست و در واقع تصمیمگیری روش رندرینگ اصلا وظیفهای نیست که بخواد توی متدهای مختلف تکرار بشه بلکه به صورت متمرکز بر عهده کلاس کانتکس هست.

وقتی با مسالهای روبرو هستید که چند روش مستقل از هم دیگه داره و قرار نیست این روشها با هم ترکیب بشن، احتمالا الگوی استراتژی به کار میاد.
وقتی کدهای switch که در مورد انتخاب روش دارن تصمیمگیری می کنن چند جا تکرار بشن احتمالا الگوی استراتژی به کار میاد.
الگوی استراتژی با انتقال کد تصمیمگیری در مورد روش انتخابی از متدهای مختلف به یه کلاس واحد (Context)، به رعایت اصل Single Responsibility (SRP) برای اون متدها کمک میکنه.
الگوی استراتژی باعث می شه با اضافه شدن روش جدید، فقط یه switch رو اصلاح کنید و لازم نباشه بگردید همه جا رو اصلاح کنید. این کار به رعایت اصل Open/Closed Principle (OCP) برای اون متدها کمک میکنه چون هم اون متدها تغییری نمیکنن و هم امکان اضافه شدن روش جدید هست. تنها جایی که تغییر میکنه کلاس Context هست که اونم وظیفه اصلیش همینه.
علاوه بر همه مزایای بالا، با تعریف یه تابع واحد Render توی کلاس strategy باعث شد که همه این متدها از نظر ورودی یکسانسازی بشن. این کار باعث شد که یه سری تبدیلات دیگه توی این متدها انجام نشه و این جا هم به رعایت SRP برا خود این متدها کمک کرد در حالی که توی نسخههای قدیمی این قید وجود نداشت.
با شکسته شدن کدها توی کلاسها و فایلهای کوچیکتر، شانس ایجاد conflict زمان merge شدن کدها هم کمتر میشه و به این ترتیب کار تیمی سادهتر میشه.