الگوی طراحی Strategy یک الگوی طراحی رفتاری(behavioral) است که به شما امکان می دهد خانواده ای از الگوریتم ها را تعریف کنید، هر یک از آنها را در یک کلاس جداگانه قرار دهید و اشیاء ساخته شده از این کلاسها را قابل تعویض کنید.
مشکل
فرض کنید یک روز تصمیم گرفتید یک برنامه ناوبری(مسیریابی) را برای مسافرانی که گهگاه قصد جابهجایی دارند، ایجاد کنید. در این برنامه یک نقشه زیبا قرار گرفته است که به کاربران کمک می کند تا به سرعت در هر شهری که هستند، مقصد دلخواه خود را مسیریابی کنند.
یکی از درخواستیترین ویژگیها برای برنامه، امکان مسیریابی اتوماتیک است. کاربر باید بتواند یک آدرس را وارد کرده و سریعترین مسیری را که به آن مقصد وجود دارد، مشاهده کند.
نسخه اول برنامه مثلا میتواند فقط مسیرهای جادهای بسازد و به کاربر نمایش دهد. افرادی که با اتومبیل سفر می کنند از این امکان استفاده میکنند و شما بزودی با فیدبکهای مثبت و ابراز علاقه کاربران روبرو میشوید. برنامه سادهی شما ظاهرا به سمت هدفش در حال حرکت است. اما ناگهان، متوجه میشوید که همه دوست ندارند با خودروی شخصی رانندگی کنند. بنابراین با بروزرسانی بعدی گزینهای برای ساخت مسیرهای پیاده روی اضافه می کنید. به ظاهر مشکل برطرف میشود اما مشکلات بعدی به سرعت در حال پدیدار شدن هستند. مسئلهی بعدی کاربرانی هستند که نه خودروی شخصی دارند و نه تمایلی به پیادهروی کردن. این کاربران به طور مداوم از سیستم حمل و نقل عمومی استفاده میکنند. اگر برنامهی شما بتواند برای رسیدن به مقصدی، مسیریابی را با سیستم حمل و نقل عمومی انجام دهد، مسئله برطرف میشود. پس بلافاصله دست به کار میشوید! احتمالا در بروز رسانی بعدی گزینهی دیگری را برای استفاده مردم از سیستم حمل و نقل عمومی در مسیرهای خود اضافه میکنید.
با این حال، این فقط آغاز راه است. بعدتر باید به نیاز کاربرانی توجه کنید که اهل دوچرخه سواری هستند. باید مسیریابی با دوچرخه را نیز به برنامه اضافه کنید. و حتی بعداً ، گزینه دیگری برای ایجاد مسیرها به نحوی که مسافر از جاذبه های گردشگری شهر عبور کند.
در حالی که از دیدگاه تجاری برنامه موفقیت آمیز پیش میرود، بخش فنی گرفتار دردسرهای زیادی خواهد شد. هر بار که الگوریتم مسیریابی جدیدی را اضافه می کنید، کلاس اصلی ناوبری از نظر حجم کد، دو برابر می شود. در بعضی مواقع ، این جانور عظیمالجسه آنقدر پیچیده میشود که نگهداری از آن بسیار سخت خواهد شد.
هرگونه تغییر در یکی از الگوریتم ها، خواه یک رفع اشکال ساده باشد یا تعدیل جزئی از نمره خیابان، بر کل کلاس تأثیر خواهد گذاشت، در نتیجه، احتمال ایجاد خطا در کدی که قبلا نوشته شده، افزایش مییابد.
علاوه بر این، کارِ گروهی ناکارآمد شده میشود. هم تیمی های شما که بلافاصله پس از انتشار موفقیت آمیز اولین نسخه استخدام شده اند، شکایت دارند که چرا باید وقت زیادی را برای حل تداخلات کدشان با دیگر توسعه دهندههای تیم صرف کنند. پیادهسازی یک ویژگی جدید شما را ملزم به تغییر همان کلاس عظیم می کند، و با این کار، احتمالا کد جدید با کد قبلی مغایرت خواهد داشت و این شروع دردسرهای بزرگتر است.
راه حل
الگوی طراحی Strategy نشان می دهد که شما یک کلاس را به نحوی کدنویسی میکنید که به روشهای مختلف، کاری خاص انجام می دهد و همه این الگوریتم ها را در کلاس های جداگانه بنام Strategy ها قرار میدهید.
کلاس اصلی، که قبلا بسیار حجیم و پیچیده بود، اکنون بسیار ساده و کوچک شده و از این پس آن را با نام Context میشناسیم. تنها چیزی که این کلاس نیاز دارد، وجود راهی برای ارجاع به یکی از استراتژی ها است. میدانیم که اکنون، تمام استراتژیها یک مسئله را حل خواهند کرد و آن مسئله مسیریابی است. در مثال این آموزش، Strategy ها میتوانند مسیریابی به کمک ماشین شخصی، پیادهروی، دوچرخه، سیستم حمل و نقل عمومی یا هر امکان دیگری باشد که حتی ممکن است بعدا به برنامه اضافه شود. اضافه شدن یک استراتژی جدید، تغییری در کدقبلی اعمال نمیکند. فقط روشی جدید برای مسیریابی(یک استراتژی جدید) به برنامه اضافه شده است. از طرف دیگر Context دیگر کار مسیریابی را خود انجام نمیدهد. بلکه مسئله را به یک استراتژی مناسب واگذار میکند.
همچنین Context مسئولیت انتخاب یک الگوریتم مناسب برای مسیریابی را بر عهده ندارد. درعوض، کاربر استراتژی مورد نظر را به Context میدهد. در واقع، Context در مورد استراتژیها چیز زیادی نمی داند و این بسیار سودمند است. زیرا در صورتی که Context به Strategy ها وابسته نباشد، زیاد شدن تعداد Strategy ها نیز تاثیری بر روی Context نخواهد گذاشت. برای اینکه Context بتواند مسئله را به استراژی مربوطه بدهد و پاسخ را دریافت کند، باید ارتباط را از طریق یک Interface برقرار کند.
به این ترتیب Context مستقل از استراتژی ها می شود، بنابراین می توانید الگوریتم های جدیدی را اضافه کرده یا موارد قبلی را بدون تغییر کد Context یا سایر استراتژیها تغییر دهید.
نکتهای که در تصویر بالا وجود دارد این است که در کلاس Context یک Object از نوع رابط Route Strategy ساختیم. در صورتی که Interface یک رابط است نه کلاس! درست است که با این Object نمیتوانیم مسیریابی را انجام دهیم، زیرا میدانیم در داخل Interface کدی برای حل مسئله وجود ندارد. Interface تنها یک قانون است که بجای ارثبری(Inheritance) باید از آن تبعیت(Implement) کرد. اما با این حال، در برنامه نویسی شیگرا، میتوان Object ای از یک رابط ساخت اما نوع آن را برابر با هر کدام از کلاسهای تبعیت کننده قرار داد. با اینکار Object ای داریم که در زمان اجرا میتواند به هریک از استراتژیها تبدیل شود.
کلاس های Road Strategy، Walking Strategy و Public Transport Strategy از رابط Route Strategy تبعیت میکنند. یعنی نام هر تابعی که در رابط Route Strategy باشد، در تمام کلاسهای تبعیت کننده، همان تابع با همان نام حتما حضور دارد. حال با تغییر Object موجود در کلاس Context به استراتژی مورد نیاز، میتوانیم الگوریتم مسیریابی مناسب را انتخاب کرده و مسئله را حل کنیم.
مثالی از الگوی Strategy در دنیای واقعی
تصور کنید که باید به فرودگاه بروید. می توانید اتوبوس بگیرید، یک تاکسی بگیرید یا دوچرخه خود را سوار شوید. این استراتژیهای حمل و نقل شما هستند. بسته به عواملی مانند محدودیت بودجه یا زمان، می توانید یکی از استراتژی ها را انتخاب کنید.
ساختار الگوی طراحی Strategy
در این آموزش سعی کردیم تا مفهوم الگوی طراحی Strategy را بیان کنیم و بدانیم که این الگو چه چالشی را برطرف خواهد کرد. برای پیادهسازی عملی الگوی طراحی Strategy ، بسته به زبان برنامهنویسی مورد نظر، نمونه کدهایی وجود دارد که اکنون قادر به درک آنها هستید.
همچنین برای آشنایی با مفاهیم دیگر الگوهای طراحی، به دسته مربوطه مراجعه کنید.
موفق و پیروز باشید.