设计模式(6)---策略模式
一、策略模式定义:
策略模式(Strategy)定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化不会影响到使用算法的客户。
分析:
封装的算法本身是一种策略,重要的是这些算法随时可能互相替换的,这就是变化点,而封装变化点是面向对象的一种重要的思维方式。策略模式定义的这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。
二、UML类图:
三、策略模式的基本代码:
class Program { static void Main(string[] args) { Contex context; context = new Contex(new ConcreteStrategyA()); context.ContexInterface(); context = new Contex(new ConcreteStrategyB()); context.ContexInterface(); Console.Read(); } } abstract class Strategy { public abstract void AlgorithmInterface(); } class ConcreteStrategyA : Strategy { public override void AlgorithmInterface() { Console.WriteLine("算法A实现"); } } class ConcreteStrategyB : Strategy { public override void AlgorithmInterface() { Console.WriteLine("算法B实现"); } } class ConcreteStrategyC : Strategy { public override void AlgorithmInterface() { Console.WriteLine("算法C实现"); } } class Contex { Strategy strategy; public Contex(Strategy strategy) { this.strategy = strategy; } public void ContexInterface() { strategy.AlgorithmInterface(); } }
四、举例说明:
功能:在一家商场有三种销售方式:正常价格、打折销售和返现销售,设计一个收银软件,要求能满足上述的销售方式。本例采用策略模式,将三种销售方式封装起来。
基本代码:
abstract class CashSuper { public abstract double acceptCash(double money); } //正常价格 class CashNormal : CashSuper { public override double acceptCash(double money) { return money; } } //打折策略 class CashRebate : CashSuper { private double moneyRebate = 1d; public CashRebate(string moneyRebate) { this.moneyRebate = double.Parse(moneyRebate); } public override double acceptCash(double money) { return money * moneyRebate; } } //返现策略 class CashReturn : CashSuper { private double moneyCondition = 0d; private double moneyReturn = 0d; public CashReturn(string moneyCondition, string moneyReturn) { this.moneyCondition = double.Parse(moneyCondition); this.moneyReturn = double.Parse(moneyReturn); } public override double acceptCash(double money) { double result = money; if (money >= moneyCondition) { result = money - Math.Floor(money / moneyCondition) * moneyReturn; } return result; } } class CashContext { private CashSuper cashSuper; public CashContext(CashSuper cashSuper) { this.cashSuper = cashSuper; } public double GetResult(double money) { return cashSuper.acceptCash(money); } }
调用方式:
cashContext = new CashContext(new CashRebate("0.8")); totalPrices = cashContext.GetResult(Convert.ToDouble(this.tbPrice.Text) * Convert.ToDouble(this.tbNumber.Text));
五、策略模式和简单工厂模式结合:
结合后,实例化具体策略的过程由客户端转移到Context类中。修改后的CashContext:
class CashContext { CashSuper cs = null; public CashContext(string type) { switch (type) { case "正常收费": CashNormal cs0 = new CashNormal(); cs = cs0; break; case "满300返100": CashReturn cs1 = new CashReturn("300", "100"); break; case "打八折": CashRebate cs2 = new CashRebate("0.8"); cs = cs2; break; } } public double GetResult(double money) { return cs.acceptCash(money); } }
六、适用场景:
1、在一个系统内有很多类,它们完成相同的功能仅在行为上有所区别,策略模式可以动态地选择一种行为。
2、一个系统的算法使用的数据不让客户端获取。策略模式可以避免客户端涉及到一些复杂的或特别的数据。
3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。此时,使用策略模式,把这些行为转移到相应的具体策略类里面,就可以避免使用难以维护的多重条件选择语句,并体现面向对象设计的概念。
4、 一个系统需要动态地在几种算法中选择一种。那么这些算法可以包装到一个个的具体算法类里面,而这些具体算法类都是一个抽象算法类的子类。换言之,这些具体算法类均有统一的接口,由于多态性原则,客户端可以选择使用任何一个具体算法类,并只持有一个数据类型是抽象算法类的对象。
七、策略模式优缺点:
优点:
1、 简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。
2、 定义了一系列的可供重用的算法或行为。继承有助于析取出这些算法中的公共功能。
3、 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。
缺点:
1. 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。
2. 策略模式造成很多的策略类。有时候可以通过把依赖于环境的状态保存到客户端里面,而将策略类设计成可共享的,这样策略类实例可以被不同客户端使用。换言之,可以使用享元模式来减少对象的数量。