概念:
策略模式是一种行为设计模式,用于定义一系列算法,将他们封装起来,并使他们可以互相替换。使用策略模式可以让代码更加灵活,且易于扩展和维护
背景:
假设你有一个功能需要多种不同的算法或行为实现(如不同的排序方式、不同的支付方式、不同的搜索方式等),这些行为是相似的。但是在细节上有所不同。
核心:
将这些可变的行为封装为不同的策略累,并定义一个接口来规范这些策略的实现。这样就可以在运行时动态切换策略,而不需要修改客户端的代码
用途:
1、替换不同的算法:可以根据需要动态的选择不同的算法或实现。比如不同的折扣策略,不同的支付方式
2、简化条件语句:用策略模式可以替换大量的if-else语句
3、提高可扩展性:新增一种行为时,只需要添加一个新的策略类,而不需要修改现有的代码
基本结构:
1、策略接口(Strategy Interface):定义所有策略类必须实现的方法。
2、具体策略类(Concrete Strategy):每个类实现具体的算法或行为。
3、上下文类(Context):持有一个策略对象并调用策略的方法。
示例:
假设你有一个电商平台,根据用户的等级(普通用户、会员、VIP)给予不同的折扣。我们可以使用策略模式来实现不同的折扣逻辑。
1、定义策略接口
// 策略接口,定义计算折扣的方法
public interface IDiscountStrategy
{decimal CalculateDiscount(decimal price);
}
2、实现具体的策略类
// 普通用户的折扣策略:无折扣
public class NoDiscountStrategy : IDiscountStrategy
{public decimal CalculateDiscount(decimal price){return price; // 无折扣}
}// 会员的折扣策略:10% 折扣
public class MemberDiscountStrategy : IDiscountStrategy
{public decimal CalculateDiscount(decimal price){return price * 0.9m; // 10% 折扣}
}// VIP 的折扣策略:20% 折扣
public class VIPDiscountStrategy : IDiscountStrategy
{public decimal CalculateDiscount(decimal price){return price * 0.8m; // 20% 折扣}
}
3、创建上下文
// 上下文类,用于选择和执行策略
public class ShoppingCart
{private IDiscountStrategy _discountStrategy;// 通过构造函数设置策略public ShoppingCart(IDiscountStrategy discountStrategy){_discountStrategy = discountStrategy;}// 计算折扣后的价格public decimal CalculateTotalPrice(decimal price){return _discountStrategy.CalculateDiscount(price);}
}
4、使用策略模式
public class Program
{public static void Main(string[] args){decimal originalPrice = 100m; // 商品的原价// 普通用户IDiscountStrategy noDiscount = new NoDiscountStrategy();ShoppingCart cartForNormalUser = new ShoppingCart(noDiscount);Console.WriteLine($"普通用户的价格: {cartForNormalUser.CalculateTotalPrice(originalPrice)}");// 会员用户IDiscountStrategy memberDiscount = new MemberDiscountStrategy();ShoppingCart cartForMember = new ShoppingCart(memberDiscount);Console.WriteLine($"会员用户的价格: {cartForMember.CalculateTotalPrice(originalPrice)}");// VIP 用户IDiscountStrategy vipDiscount = new VIPDiscountStrategy();ShoppingCart cartForVIP = new ShoppingCart(vipDiscount);Console.WriteLine($"VIP 用户的价格: {cartForVIP.CalculateTotalPrice(originalPrice)}");}
}
运行结果:
普通用户的价格: 100
会员用户的价格: 90
VIP 用户的价格: 80
理解了策略模式后,开始设计搜索功能。
1、定义策略接口
public interface ISearchStrategy{List<PatientData> ExecuteSearch();}
2、实现具体的策略类,由于搜索条件有可能同时满足,所以定义组合策略类
/// <summary>/// 日期搜索/// </summary>public class DateSearchStrategy : ISearchStrategy{private DateTime _date1, _date2;public DateSearchStrategy(DateTime date1, DateTime date2){_date1 = date1.Date;_date2 = date2.Date;}public List<PatientData> ExecuteSearch(){return T_PatientDataAccess.GetPatientArchives(startDate:_date1,endDate: _date2);}}/// <summary>/// 病人姓名搜索/// </summary>public class PatientNameSearchStrategy : ISearchStrategy{private string _name;public PatientNameSearchStrategy(string name){_name = name;}public List<PatientData> ExecuteSearch(){return T_PatientDataAccess.GetPatientArchives(patientName:_name);}}/// <summary>/// 医生姓名搜索/// </summary>public class DoctorNameSearchStrategy : ISearchStrategy{private string _name;public DoctorNameSearchStrategy(string name){_name = name;}public List<PatientData> ExecuteSearch(){return T_PatientDataAccess.GetPatientArchives(patientName: _name);}}/// <summary>/// 组合搜索/// </summary>public class CompositeSearchStrategy : ISearchStrategy{private readonly List<ISearchStrategy> _strategies = new List<ISearchStrategy>();public void AddStrategy(ISearchStrategy strategy){_strategies.Add(strategy);}public List<PatientData> ExecuteSearch(){List<PatientData> datas = new List<PatientData>();// 如果没有条件,返回所有数据if (_strategies.Count == 0){return datas;}// 逐步过滤数据foreach (var strategy in _strategies){if (datas.Count == 0){datas = strategy.ExecuteSearch(); //第一条搜索}else{datas = datas.Where(p1 => strategy.ExecuteSearch().Any(p2 => p1.inspectNumber == p2.inspectNumber)).ToList(); //求交集}}return datas;}
3、定义上下文
public class B_T_patientSearchService{/// <summary>/// 搜索服务/// </summary>/// <param name="startDate">开始日期</param>/// <param name="endDate">结束日期</param>/// <param name="patientName">病人名字</param>/// <param name="doctorName">医生姓名</param>/// <returns></returns>public List<PatientData> Search(string startDate, string endDate, string patientName, string doctorName){var searchStrategy = new CompositeSearchStrategy();if (!string.IsNullOrWhiteSpace(startDate) && !string.IsNullOrWhiteSpace(endDate)){ searchStrategy.AddStrategy(new DateSearchStrategy(DateTime.Parse(startDate),DateTime.Parse(endDate)));}if (!string.IsNullOrWhiteSpace(patientName)){searchStrategy.AddStrategy(new PatientNameSearchStrategy(patientName));}if (!string.IsNullOrWhiteSpace(doctorName)){searchStrategy.AddStrategy(new DoctorNameSearchStrategy(doctorName));}// 传入所有数据,依次应用过滤策略return searchStrategy.ExecuteSearch();}}
4、狠狠的使用它
/// <summary>/// 搜索功能实现/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnSearch_Click(object sender, EventArgs e){PerformSearch(tbStartDate.Text, tbEndDate.Text, cbPatientName.Text, cbDoctorName.Text);ControlStatus(false);}/// <summary>/// 执行查询并绑定数据/// </summary>/// <param name="startDate">开始日期</param>/// <param name="endDate">结束日期</param>/// <param name="patientName">患者姓名</param>/// <param name="doctorName">医生姓名</param>private void PerformSearch(string startDate, string endDate, string patientName, string doctorName){var searchService = new B_T_patientSearchService();var list = searchService.Search(startDate, endDate, patientName, doctorName);DataTable dataTable = new DataTable();if (list != null && list.Count > 0){dataTable = ConvertToDataTable(list);tempTable = dataTable; dataGridView1.AutoGenerateColumns = false; // 固定标题栏dataGridView1.DataSource = tempTable;// 绑定数据LoadT_PatientArchivesManageToText();}else{dataGridView1.DataSource = dataTable;}lblTotal.Text = "查询总数:" + dataGridView1.RowCount;}