概念:
策略模式是一种行为设计模式,用于定义一系列算法,将他们封装起来,并使他们可以互相替换。使用策略模式可以让代码更加灵活,且易于扩展和维护
背景:
假设你有一个功能需要多种不同的算法或行为实现(如不同的排序方式、不同的支付方式、不同的搜索方式等),这些行为是相似的。但是在细节上有所不同。
核心:
将这些可变的行为封装为不同的策略累,并定义一个接口来规范这些策略的实现。这样就可以在运行时动态切换策略,而不需要修改客户端的代码
用途:
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()
{
var list = PatientDataAccess.GetAllPatientArchives();
var filteredList = list.Where(p =>
{
DateTime inspectDate;
bool isValidDate = DateTime.TryParse(p.inspectDate, out inspectDate);
return isValidDate && inspectDate >= _date1 && inspectDate <= _date2;
}).ToList();
return filteredList;
}
}
/// <summary>
/// 病人姓名搜索
/// </summary>
public class PatientNameSearchStrategy : ISearchStrategy
{
private string _name;
public PatientNameSearchStrategy(string name)
{
_name = name;
}
public List<PatientData> ExecuteSearch()
{
var list = PatientDataAccess.GetAllPatientArchives();
var filteredList = list.Where(p =>
{
//return p.Name == _name; //不能用==,string为引用类型
return p.Name.Equals(_name);
}).ToList();
return filteredList;
}
}
/// <summary>
/// 医生姓名搜索
/// </summary>
public class DoctorNameSearchStrategy : ISearchStrategy
{
private string _name;
public DoctorNameSearchStrategy(string name)
{
_name = name;
}
public List<PatientData> ExecuteSearch()
{
var list = PatientDataAccess.GetAllPatientArchives();
var filteredList = list.Where(p =>
{
return p.inspectdoctor.Equals(_name);
}).ToList();
return filteredList;
}
}
/// <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()
{
if (_strategies.Count == 0) //没有条件达成,显示全部(看需求)。
{
return PatientDataAccess.GetAllPatientArchives();
}
List<PatientData> resultList = _strategies[0].ExecuteSearch(); //获取第一结果
for (int i = 1; i < _strategies.Count; i++)
{
//求与余下结果的交集
resultList = resultList.Where(p1 => _strategies[i].ExecuteSearch().Any(p2 => p1.inspectNumber == p2.inspectNumber)).ToList();
}
return resultList;
}
}
3、定义上下文
public class SearchService
{
/// <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 searchStrateay = new CompositeSearchStrategy();
//根据输入条件选择
if (!string.IsNullOrWhiteSpace(startDate) && !string.IsNullOrWhiteSpace(endDate))
{
if (DateTime.Parse(startDate) > DateTime.Parse(endDate))
{
MessageBox.Show("开始时间需早于结束时间!");
return null;
}
searchStrateay.AddStrategy(new DateSearchStrategy(DateTime.Parse(startDate), DateTime.Parse(endDate)));
}
if (!string.IsNullOrWhiteSpace(patientName))
{
searchStrateay.AddStrategy(new PatientNameSearchStrategy(patientName));
}
if (!string.IsNullOrWhiteSpace(doctorName))
{
searchStrateay.AddStrategy(new DoctorNameSearchStrategy(doctorName));
}
return searchStrateay.ExecuteSearch();
}
}
4、狠狠的使用它
/// <summary>
/// 搜索功能实现
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSearch_Click(object sender, EventArgs e)
{
var searchService = new SearchService();
var list = searchService.Search(tbStartDate.Text, tbEndDate.Text, cbPatientName.Text, cbDoctorName.Text);
if (!(list == null))
{
list.Reverse();
dataGridView1.AutoGenerateColumns = false; //固定标题栏
dataGridView1.DataSource = list;
lblTotal.Text = "查询总数:" + dataGridView1.RowCount;
}
}