定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式可使算法独立于使用它的客户端而变化。
适用性
- 许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。
- 需要使用一个算法的不同变体。例如,你可能会定义一些反应不同的空间/时间权衡的算法。当这些实体变现为一个算法的类层次时,可以使用策略模式。
- 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的,与算法相关的数据结构。
- 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。
参与者
Strategy
——定义所有支持算法的公共接口。Context使用这个接口来调用某ConcreteStrategy定义的算法
ConcreteStrategy
——以Strategy接口实现某具体算法
Context
——用一个ConcreteStrategy对象来配置
——维护一个对Strategy对象的引用
——可定义一个接口来让Strategy访问它的数据
例如,中国的所得税,分为企业所得税、外商投资企业或外商企业所得税和个人所得税,针对于这3种所得税,针对每种,所计算的方式不同,个人所得税有个人所得税的计算方式,而企业所得税有其对应计算方式。如果不采用策略模式来实现这样一个需求的话,可能我们会定义一个所得税类,该类有一个属性来标识所得税的类型,并且有一个计算税收的CalculateTax()方法,在该方法体内需要对税收类型进行判断,通过if-else语句来针对不同的税收类型来计算其所得税。这样的实现确实可以解决这个场景吗,但是这样的设计不利于扩展,如果系统后期需要增加一种所得税时,此时不得不回去修改CalculateTax方法来多添加一个判断语句,这样明白违背了“开放——封闭”原则。此时,我们可以考虑使用策略模式来解决这个问题,既然税收方法是这个场景中的变化部分,此时自然可以想到对税收方法进行抽象。具体的实现代码:
1 // 所得税计算策略 2 public interface ITaxStragety 3 { 4 double CalculateTax(double income); 5 }
1 // 个人所得税 2 public class PersonalTaxStrategy : ITaxStragety 3 { 4 public double CalculateTax(double income) 5 { 6 return income * 0.12; 7 } 8 }
1 // 企业所得税 2 public class EnterpriseTaxStrategy : ITaxStragety 3 { 4 public double CalculateTax(double income) 5 { 6 return (income - 3500) > 0 ? (income - 3500) * 0.045 : 0.0; 7 } 8 }
1 public class InterestOperation 2 { 3 private ITaxStragety m_strategy; 4 public InterestOperation(ITaxStragety strategy) 5 { 6 this.m_strategy = strategy; 7 } 8 9 public double GetTax(double income) 10 { 11 return m_strategy.CalculateTax(income); 12 } 13 }
1 class App 2 { 3 static void Main(string[] args) 4 { 5 // 个人所得税方式 6 InterestOperation operation = new InterestOperation(new PersonalTaxStrategy()); 7 Console.WriteLine("个人支付的税为:{0}", operation.GetTax(5000.00)); 8 9 // 企业所得税 10 operation = new InterestOperation(new EnterpriseTaxStrategy()); 11 Console.WriteLine("企业支付的税为:{0}", operation.GetTax(50000.00)); 12 13 Console.Read(); 14 } 15 }
策略者模式在.NET中应用
在.NET Framework中也不乏策略模式的应用例子。例如,在.NET中,为集合类型ArrayList和List<T>提供的排序功能,其中实现就利用了策略模式,定义了IComparer接口来对比较算法进行封装,实现IComparer接口的类可以是顺序,或逆序地比较两个对象的大小,具体.NET中的实现可以使用反编译工具查看List<T>.Sort(IComparer<T>)的实现。其中List<T>就是承担着环境角色,而IComparer<T>接口承担着抽象策略角色,具体的策略角色就是实现了IComparer<T>接口的类,List<T>类本身实现了存在实现了该接口的类,我们可以自定义继承与该接口的具体策略类。
到这里,策略模式的介绍就结束了,策略模式主要是对方法的封装,把一系列方法封装到一系列的策略类中,从而使不同的策略类可以自由切换和避免在系统使用多重条件选择语句来选择针对不同情况来选择不同的方法。在下一章将会大家介绍责任链模式。