الگوی Strategy یک الگوی Behavioral هست که به ما این اجازه رو میده که خانوادهای از الگوریتمهای مختلف رو به عنوان کلاسهای مجزا تعریف کنیم و اشیا آنها را قابل تعویض کنیم(یعنی بتونن با اشیا دیگری از همون کلاسها جایگزین بشن).
مدتی هست که تصمیم گرفتم چیزهای جدیدی که یاد میگیرم رو به اشتراک بزارم و امروز یه مقاله در مورد این الگو خوندم و قصد داشتم چیزی که خودم یادگرفتم رو بنویسم ولی دیدم این مقاله ساختاربهتری داره و تصمیم گرفتم ترجمش کنم که بقیه هم بتونن ازش استفاده کنن . سعی کردم چیزی رو که درک کردم رو ترجمه کنم پس اگر اشتباهی دیدید معذرت میخوام.
این مقاله یکم پیشرفته هست و اگر به تازگی شروع به برنامهنویسی کردید ممکن هست که درکش نکنید.
فرض کنید قراره یه اپلکیشن مسیریابی برای توریستها طراحی کنید که قابلیت مسیریابی هوشمند رو داشته باشه و سریعترین مسیر رو به کاربر پیشنهاد بده.
برای اولین نسخه اپلکیشن فقط مسیرهای ماشین رو پیشنهاد داده میشه ولی بعد از مدتی کاربران درخواست مسیرهایی برای عابرین پیاده میکنن و یا عدهای دیگر درخواست مسیرهایی رو داشته باشن که از طریق وسایل حمل و نقل عمومی به مقصد برسن. خیلی زود نیاز به مسیر برای دوچرخه سوارها هم به وجود میاد و بعضی از کاربران درخواست مسیرهایی رو میکنن که از مکانهای توریستی هم میگذرن.
از لحاظ تجاری همه چیز خوب بوده و ما موفق عمل کردیم, ولی از لحاظ فنی مشکلات زیادی ایجاد شده و با اضافه شدن هر ویژگی جدید اندازه کلاس اصلی بزرگتر و بزرگتر میشه.
هر تغییر کوچکی روی الگوریتمها باعث میشه با چندین خطای دیگه روبهرو بشیم. از طرف دیگه کار تیمی هم با مشکل روبهرو شده و توسعه دهندهها زمان زیادی رو صرف merge conflict ها میکنن و هر تغییر کوچیکی زمان زیادی از اونها میگیره.
الگوی Strategy راهکاری رو ارائه میده که یک کلاس یک کار مشخص رو به چندین روش مختلف انجام میده و تمام اون روشها رو از کلاس ها مجزا که اونها را strategy مینامیم استخراج میکنه.
کلاس اصلی رو کلاس Context مینامیم و این کلاس باید یک رابطه با یکی از کلاسهای Strategy داشته باشد و کلاس Context باید به جای انجام این عمل توسط خودش وظیفه را به آن کلاس Strategy محول کند.
کلاس Context وظیفه انتخاب الگوریتم مناسب را بر عهده ندارد. در عوض Client کلاس Strategy دلخواه را به Context میدهد. در حقیقت کلاس Context اطلاعات زیادی در مورد strategy ها ندارد. این کلاس از طریق یک رابط (interface) عمومی که فقط یک متد برای فراخوانی الگوریتمهای استراتژی در اختیار قرار میدهد با Strategy ها کار میکند.
در این حالت کلاس Context به طور مستقل فعالیت میکند و با افزودن Strategy های جدید یا تغییر Strategy های قبلی تغییری در کلاس Context ایجاد نمیشود.
در برنامه مسیریابی ما، هر الگوریتم مسیریابی را می توان با فراخوانی متد buildRoute از کلاس خود استخراج کرد. این روش مبدا و مقصد را می پذیرد و مجموعه ای از مسیرها را برمی گرداند.
اگرچه با توجه به همان استدلال ها، هر کلاس مسیریابی می تواند مسیری متفاوتی را ایجاد کند، کلاس اصلی مسیریاب اهمیتی نمی دهد که چه الگوریتم انتخاب شده باشد زیرا کار اصلی آن ارائه مجموعه ای از مسیرها در نقشه است. کلاس دارای روشی برای تغییر استراتژی مسیریابی فعال است، بنابراین Client های آن می توانند به راحتی رفتار مسیریاب انتخاب شده در حال حاضر را با دیگری جایگزین کنند.
تصور کنید که باید به فرودگاه بروید. می توانید اتوبوس بگیرید، یک تاکسی سفارش دهید یا دوچرخه خود را سوار شوید. این راهکارهای حمل و نقل شماست. بسته به عواملی مانند محدودیت بودجه یا زمان، می توانید یکی از این استراتژی ها را انتخاب کنید.
در این مثال، کلاس Context از چندین استراتژی برای اجرای عملیاتهای متنوع حسابی استفاده می کند.
// The strategy interface declares operations common to all // supported versions of some algorithm. The context uses this // interface to call the algorithm defined by the concrete // strategies. interface Strategy is method execute(a, b) // Concrete strategies implement the algorithm while following // the base strategy interface. The interface makes them // interchangeable in the context. class ConcreteStrategyAdd implements Strategy is method execute(a, b) is return a + b class ConcreteStrategySubtract implements Strategy is method execute(a, b) is return a - b class ConcreteStrategyMultiply implements Strategy is method execute(a, b) is return a * b // The context defines the interface of interest to clients. class Context is // The context maintains a reference to one of the strategy // objects. The context doesn't know the concrete class of a // strategy. It should work with all strategies via the // strategy interface. private strategy: Strategy // Usually the context accepts a strategy through the // constructor, and also provides a setter so that the // strategy can be switched at runtime. method setStrategy(Strategy strategy) is this.strategy = strategy // The context delegates some work to the strategy object // instead of implementing multiple versions of the // algorithm on its own. method executeStrategy(int a, int b) is return strategy.execute(a, b) // The client code picks a concrete strategy and passes it to // the context. The client should be aware of the differences // between strategies in order to make the right choice. class ExampleApplication is method main() is Create context object. Read first number. Read last number. Read the desired action from user input. if (action == addition) then context.setStrategy(new ConcreteStrategyAdd()) if (action == subtraction) then context.setStrategy(new ConcreteStrategySubtract()) if (action == multiplication) then context.setStrategy(new ConcreteStrategyMultiply()) result = context.executeStrategy(First number, Second number) Print result
وقتی می خواهید از انواع مختلفی از الگوریتم درون یک شی استفاده کنید و قابلیت تغییر از یک الگوریتم به الگوریتم دیگری در زمان اجرا را داشته باشید، از الگوی استراتژی استفاده کنید.
الگوی strategy به شما امکان میدهد تا به صورت غیر مستقیم رفتار شی، در زمان اجرا را، از طریق مرتبط کردن آن با اشیايی از زیرکلاسهای متفاوت که هرکدام وظایف مشخصی را به صورت های متفاوت انجام میدهند، تغییر دهید.
زمانی که تعداد زیادی کلاس مشابه دارید که تفاوت آنها فقط در اجرای بعضی از رفتارهاست، از الگوی strategy استفاده کنید.
الگوی استراتزی به شما این امکان را میدهد که رفتارهای متفاوت را در کلاس های متفاوت قرار دهید و کلاسهای اصلی را در یک کلاس ترکیب کنید و از این طریق کدهای تکراری را کاهش دهید.
با استفاده از الگوی strategy منطق تجاری (business logic)، از جزییات پیادهسازی الگوریتمها، که ممکن است به اندازه محتوای آن logic مهم نباشند، از هم جدا میشوند.
الگوی strategy به شما امکان می دهد تا کد، داده های داخلی و وابستگی های الگوریتم های مختلف را از بقیه کد جدا کنید. client های مختلف برای اجرای الگوریتم ها و تعویض آنها در زمان اجرا، یک رابط (interface) ساده دریافت می کنند.
وقتی که کلاس شما یک اپراتور شرطی بزرگ دارد(زمانی که حالت های مختلفی برای اجرا بررسی میشوند) و بر اساس آن بین پیاده سازیهای متفاوت از یک الگوریتم(مثلا پیاده سازی های متفاوت الگوریتم مرتب سازی یا پیاده سازی های متفاوت الگوریتم جستجو) یکسان جابجا میشود از الگوی strategy استفاده کنید.
الگوی strategy به شما امکان می دهد با استخراج کلیه الگوریتم ها در کلاس های جداگانه، که همه آنها یک رابط (interface) یکسان را پیادهسازی میکنند، از شر چنین شرطی خلاص شوید. شی اصلی به جای پیاده سازی تمام انواع الگوریتم، اجرای را به یکی از این اشیاء واگذار می کند.