设计模式之——策略模式
什么是策略模式
策略模式(Strategy Pattern):定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
策略模式是一种定义了一系列算法的方法,从概念上来看,所以得这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。
策略模式的结构
我们先观察策略模式(Strategy Pattern)的UML类图:
根据前面的类图我们可以知道策略模式由下面的几个部分组成:
- 抽象策略类:所以策略子类的通用接口,通常由一个抽象类和接口来实现。
- 具体策略类:实现了抽象策略类,包装的相关的算法和行为。
- Context类:供客户代码调用,持有一个抽象策略类的引用,根据客户端不同的调用,返回不同的具体策略实例。
策略模式的优缺点
策略模式的优点:
- 提供了一种替代继承的方法,而且既保持了继承的优点(代码重用)还比继承更灵活(算法独立,可以任意扩展)。
- 策略模式的Strategy类层次为Context定义了一系列可供重用的算法或行为。继承有助于析取出这些算法中的公共功能。
- 当不同的行为堆砌在一个类中时,就很难避免使用条件语句来选择合适的行为。将这些行为封装在一个个独立的Strategy类中,可以再使用这些行为的类中消除条件语句。
- 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。
- 策略模式简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。
策略模式的缺点:
- 因为每个具体策略类都会产生一个新类,所以会增加系统需要维护的类的数量。
- 与简单工厂结合后还需要修改switch分支语句。
策略模式的代码实例
根据商场打折、促销等,算法需要经常性的变动的场景来提供一个策略模式实现的一个收费程序。
首先定义一个结算的抽象类(接口):
1 /// <summary> 2 /// 现金收费抽象类 3 /// </summary> 4 abstract class CashSuper 5 { 6 //现金收取超类的抽象方法,收取现金,参数为原价,返回当前价。 7 public abstract double acceptCash(double money); 8 }
然后根据打折、促销等不同的活动实现具体收费功能的子类
1 /// <summary> 2 /// 正常收费子类 3 /// </summary> 4 class CashNormal:CashSuper 5 { 6 public override double acceptCash(double money) 7 { 8 return money; 9 } 10 } 11 12 /// <summary> 13 /// 打折子类 14 /// </summary> 15 class CashRebate:CashSuper 16 { 17 private double moneyRebate = 1d; 18 public CashRebate(string moneyRebate) 19 { 20 this.moneyRebate = double.Parse(moneyRebate); 21 } 22 23 public override double acceptCash(double money) 24 { 25 return money * moneyRebate; 26 } 27 } 28 29 /// <summary> 30 /// 返利子类 31 /// </summary> 32 class CashReturn:CashSuper 33 { 34 private double moneyCondition = 0.0d; 35 private double moneyReturn = 0.0d; 36 37 /// <summary> 38 /// 返利子类构造方法 39 /// </summary> 40 /// <param name="moneyCondition">返利标准</param> 41 /// <param name="moneyReturn">返利值</param> 42 public CashReturn(string moneyCondition, string moneyReturn) 43 { 44 this.moneyCondition = double.Parse(moneyCondition); 45 this.moneyReturn = double.Parse(moneyReturn); 46 } 47 48 public override double acceptCash(double money) 49 { 50 double result = money; 51 52 if (money >= moneyCondition) 53 result = money - Math.Floor(money / moneyCondition) * moneyReturn; 54 55 return result; 56 } 57 }
最后创建的是提供给客户端调用的Context类
1 class CashContext 2 { 3 CashSuper cs; 4 5 public CashContext(string type) 6 { 7 //简单工厂构造函数,克服客户端需要知道所以算法子类的缺点(封装了变化) 8 switch (type) 9 { 10 case "正常收费": 11 cs = new CashNormal(); 12 break; 13 case "满300返100": 14 cs = new CashReturn("300", "100"); 15 break; 16 case "打8折": 17 cs = new CashRebate("0.8"); 18 break; 19 } 20 } 21 22 //和简单工厂的不同之处 23 public double GetResult(double money) 24 { 25 return cs.acceptCash(money); 26 } 27 }
在基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式的Context对象。这本身并没有解除客户端需要判断选择的压力,而策略模式与简单工厂结合后,选择具体实现的职责也可以由Context承担,这就最大化减轻了客户端的职责。
客户代码调用,编译运行:
1 CashContext csuper = new CashContext(cmbDiscount.SelectedItem.ToString()); 2 double totalPrices = 0d; 3 totalPrices = csuper.GetResult(double.Parse(this.txtPrice.Text) * double.Parse(this.txtNumber.Text)); 4 5 this.lbxResult.Items.Add("单价:" + this.txtPrice.Text + " 数量:" + this.txtNumber.Text + " " + cmbDiscount.SelectedItem + " " + "合计" + totalPrices.ToString()); 6 7 total = total + totalPrices; 8 this.lblAmount.Text = total.ToString();
策略模式和简单工厂的区别
通过对比简单工厂和策略模式的UML类图我们可以发现二者结构比较相似:
我们可以发现使用策略模式实现的都可以使用简单工厂来实现,二者都是通过使用继承以多态的形式来实现的。在产品构造多次重叠,并且在不同时刻应用不同的规则(算法)时使用策略模式比较合适。我们可以发现简单工厂只是通过一个静态的类方法来创建不同的子类,但是在策略模式中除了通过多态创建不同的算法子类,还有一个调用子类示例函数的一个成员方法(Context类中),所以在客户端中我们只需要获取一个Context实例时候,然后通过直接调用它的实例方法来间接调用子类的成员方法就可以了(构造函数使用简单工厂来实现);但是在简单工厂中我们除了通过调用工厂类的静态方法获取产品子类的实例,还要直接调用子类的成员方法。
猛击下载:示例代码
参考资料:《大话设计模式》
作者:晴天猪
出处:http://www.cnblogs.com/IPrograming
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。