الگوی طراحی Strategy، یک الگوی طراحی رفتاری است که این امکان را به ما میدهد تا عملکردهای مختلفی را تعریف کرده و هریک را در یک کلاس جداگانه قرار دهیم و باعث شویم تا آبجکتهای مربوط به این کلاسها با یکدیگر قابل تعویض باشند.
به عبارتی دیگر، ما یک آبجکت Context اصلی داریم که یک رفرنس از آبجکت Strategy را در خود نگه میدارد و وظیفه اجرای عملکرد مورد نظر را به Strategy محول میکند. اگر بخواهیم نحوه عملکرد Context را تغییر دهیم، میتوانیم آبجکت فعلی Strategy را با آبجکت دیگری جایگزین کنیم.
این مقاله، بخشی از مجموعه آموزشی زیر است:
- الگوی طراحی Builder و Fluent Builder
- اینترفیس Fluent Builder به همراه Generic بازگشتی
- Facated Builder
- متد Factory
- Singleton
- Adapter
- Composite
- Decorator
- Command
- Strategy
- Facade
بنابراین بدون هیچ مقدمه ای، بیایید به پیاده سازی الگوی طراحی Strategy بپردازیم.
سورس کد در این لینک موجود است: الگوی طراحی Strategy – سورس کد.
برای مشاهده لیست کامل مقالات این مجموعه آموزشی، الگوهای طراحی #C را بررسی کنید.
این مقاله به قسمتهای زیر تقسیم میشود:
- ساختار الگوی طراحی Strategy
- پیاده سازی الگوی طراحی Strategy
- چه زمانی باید از الگوی طراحی Strategy استفاده کنیم
- نتیجه گیری
ساختار الگوی طراحی Strategy
همانطور که در بالا ذکر کردیم، الگوی طراحی Strategy شامل آبجکت Context است که یک رفرنس از strategy را در خود نگه میدارد. اما کل داستان این نیست. جهت پیاده سازی کامل، ما نیاز داریم تا آبجکت Strategy (اینترفیس) یک شیوه ای را برای آبجکت Context برای اجرای strategy و آبجکتهای Concrete Strategy ها که اینترفیس Strategy را پیاده سازی میکنند تعریف کند.
الگوی طراحی Strategy در زبان #C به دلیل استفاده های مختلف این الگو برای تغییر رفتار یک کلاس بدون اینکه تغییری در این کلاس دهیم کاملا رایج است. این مطابق با قوانین اصل Open Closed است که در یکی از مقالات قبلی در مورد آن صحبت کردیم.
پیاده سازی الگوی طراحی Strategy
در مثالی که میخواهیم انجام دهیم، task اصلی، محاسبه جمع هزینه حقوقهای developer ها است، اما برای سطوح مختلف developer ها، حقوق به صورت متفاوت محاسبه میشود. حالا ما قصد داریم این مثال را با الگوی طراحی Strategy انجام دهیم.
بنابراین با شمارنده DeveloperLevel
و یک آبجکت ساده DeveloperReport
شروع میکنیم:
1 2 3 4 5 |
public enum DeveloperLevel { Senior, Junior } |
1 2 3 4 5 6 7 8 9 10 |
public class DeveloperReport { public int Id { get; set; } public string Name { get; set; } public DeveloperLevel Level { get; set; } public int WorkingHours { get; set; } public double HourlyRate { get; set; } public double CalculateSalary() => WorkingHours * HourlyRate; } |
در ادامه، اینترفیس Strategy به نام ISalaryCalculator را ایجاد میکنیم:
1 2 3 4 |
public interface ISalaryCalculator { double CalculateTotalSalary(IEnumerable<DeveloperReport> reports); } |
حالا چیزی که نیاز داریم، آبجکتهای concrete strategy است که تمام report ها را دریافت کرده و جمع حقوق را برای سطح مورد نیاز توسعه دهنده محاسبه میکند:
1 2 3 4 5 6 7 8 |
public class JuniorDevSalaryCalculator : ISalaryCalculator { public double CalculateTotalSalary(IEnumerable<DeveloperReport> reports) => reports .Where(r => r.Level == DeveloperLevel.Junior) .Select(r => r.CalculateSalary()) .Sum(); } |
1 2 3 4 5 6 7 8 |
public class SeniorDevSalaryCalculator : ISalaryCalculator { public double CalculateTotalSalary(IEnumerable<DeveloperReport> reports) => reports .Where(r => r.Level == DeveloperLevel.Senior) .Select(r => r.CalculateSalary() * 1.2) .Sum(); } |
همانطور که میبینیم، برای senior developer ها، ما 20 درصد پاداش به حقوق اضافه می کنیم. علاوه بر این، ما منطق محاسباتی خود را برای سطوح مختلف توسعهدهنده جدا کردهایم و اضافه کردن منطق محاسبه را به فرض مثال برای توسعهدهندگان سطح متوسط آسانتر کردهایم. تمام کاری که باید انجام دهیم اضافه کردن یک آبجکت strategy دیگر است که اینترفیس ISalaryCalculator
را پیاده سازی میکند.
هنگامی که آبجکتهای strategy را در اختیار داریم، در پیاده سازی خود به آبجکت context نیاز داریم:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class SalaryCalculator { private ISalaryCalculator _calculator; public SalaryCalculator(ISalaryCalculator calculator) { _calculator = calculator; } public void SetCalculator(ISalaryCalculator calculator) => _calculator = calculator; public double Calculate(IEnumerable<DeveloperReport> reports) => _calculator.CalculateTotalSalary(reports); } |
ما در این آبجکت context، آبجکت strategy را در زمان کامپایل توسط سازنده یا در زمان اجرای برنامه، با متد SetCalculator
فراهم میکنیم. علاوه بر این، متد Calculate فقط عملکرد آبجکت strategy را اجرا می کند.
بنابراین، برای اتصال تمام نقاط به یکدیگر، اجازه دهید کلاس Program.cs را تغییر دهیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class Program { static void Main(string[] args) { var reports = new List<DeveloperReport> { new DeveloperReport {Id = 1, Name = "Dev1", Level = DeveloperLevel.Senior, HourlyRate = 30.5, WorkingHours = 160 }, new DeveloperReport { Id = 2, Name = "Dev2", Level = DeveloperLevel.Junior, HourlyRate = 20, WorkingHours = 120 }, new DeveloperReport { Id = 3, Name = "Dev3", Level = DeveloperLevel.Senior, HourlyRate = 32.5, WorkingHours = 130 }, new DeveloperReport { Id = 4, Name = "Dev4", Level = DeveloperLevel.Junior, HourlyRate = 24.5, WorkingHours = 140 } }; var calculatorContext = new SalaryCalculator(new JuniorDevSalaryCalculator()); var juniorTotal = calculatorContext.Calculate(reports); Console.WriteLine($"Total amount for junior salaries is: {juniorTotal}"); calculatorContext.SetCalculator(new SeniorDevSalaryCalculator()); var seniorTotal = calculatorContext.Calculate(reports); Console.WriteLine($"Total amount for senior salaries is: {seniorTotal}"); Console.WriteLine($"Total cost for all the salaries is: {juniorTotal+seniorTotal}"); } } |
نتیجه باید به این صورت باشد:

چه زمانی باید از الگوی طراحی Strategy استفاده کنیم
زمانیکه ما گونه های متفاوتی از عملکردها در یک آبجکت را در اختیار داریم باید از این الگو استفاده کنیم و ما میخواهیم در زمان اجرا، از یک گونه به گونه دیگر سوئیچ کنیم. علاوه بر این اگر کلاسهای مشابهی در پروژه خود داشته باشیم که فقط در نحوه اجرای برخی رفتارها متفاوت است، الگوی Strategy باید انتخاب مناسبی برای ما باشد.
ما باید این الگو را در شرایطی در نظر بگیریم که یک کلاس منفرد دارای شرایط متعددی بر روی تغییرات مختلفی از یک عملکرد یکسان است. به این دلیل که الگوی Strategy به ما این امکان را می دهد تا این تغییرات را به داخل کلاس های جداگانه استخراج کنیم (concrete strategy ها). سپس می توانیم آنها را در کلاس context فراخوانی کنیم.
نتیجه گیری
همانطور که مشاهده کردید، پیاده سازی الگوی طراحی Strategy اصلا سخت یا پیچیده نیست.
این الگو، کد ما را خواناتر و جهت نگه داری آسانتر میسازد. بله، این الگو به پیاده سازی کلاسهای اضافی در پروژه نیاز دارد، اما این بهایی است که ارزش پرداخت آن را دارد.
بنابراین، ما یاد گرفتیم که:
- الگوی طراحی Strategy چیست
- نحوه پیاه سازی آن در #C
- و زمان استفاده از آن