策略模式 问题引入 问题描述 大鸟给小菜出了个作业,要求实现一个商场收银软件程序,营业员可以通过输入客户所有购买商品的单价和数量,程序自动计算出总金额。同时,商场有时会有打折活动(如商品打7折),或满减促销活动(如商品满300-100),程序应能考虑这些活动的情形,在尽量减少重复代码的前提下,实现正确的金额计算。 考虑到商场可能经常性地更改打折额度和返利额度,上述问题虽然可以用简单工厂模式实现,但如果每次扩展收费形式都要改动工厂,导致代码重新编译部署,则确实不是最好的处理方式。此时可以考虑使用 。 模式定义 是指定义一个算法家族,使得家族内的不同算法都遵从算法家族的接口及方法规范,从而可以实现算法间互相替换,且不会影响到使用算法的客户。
大鸟给小菜出了个作业,要求实现一个商场收银软件程序,营业员可以通过输入客户所有购买商品的单价和数量,程序自动计算出总金额。同时,商场有时会有打折活动(如商品打7折),或满减促销活动(如商品满300-100),程序应能考虑这些活动的情形,在尽量减少重复代码的前提下,实现正确的金额计算。

考虑到商场可能经常性地更改打折额度和返利额度,上述问题虽然可以用简单工厂模式实现,但如果每次扩展收费形式都要改动工厂,导致代码重新编译部署,则确实不是最好的处理方式。此时可以考虑使用策略模式。
策略模式(Strategy Pattern)是指定义一个算法家族,使得家族内的不同算法都遵从算法家族的接口及方法规范,从而可以实现算法间互相替换,且不会影响到使用算法的客户。
策略模式可以用于这一问题,因为商场促销活动变化繁多,如打7折,打8折,满300-100,满700-200等,这些促销活动逻辑类似,而且随时可以相互替换,恰符合策略模式的应用场景。
使用策略模式来解决问题。
CashSupur,作为所有促销活动算法的抽象类,同时定义所有支持算法的公共接口,定义方法acceptCash()用于得到结果;CashNormal,CashRebate等,继承于抽象算法类CashSupur,覆写acceptCash()实现具体的促销算法;CashContext,维护对算法对象的引用,使用时根据用户输入,传入一个具体的促销算法类来配置。此处我们使用Java语言来实现这一方案,C#语言实现可见原书原版,本项目的所有语言实现可见本项目Github仓库,其中包括:C++,Java,python,读者可按需参阅。
首先定义一个收费抽象类。
public abstract class CashSuper{ public abstract double acceptCash(double money); }
定义具体的促销算法类,包括正常收费类CashNormal,打折收费类CashRebate,返利收费类CashReturn。
public class CashNormal extends CashSuper { @Override public double acceptCash(double money) { return money; } } public class CashRebate extends CashSuper { private double moneyRebate; public CashRebate(String moneyRebate) { this.moneyRebate = Double.parseDouble(moneyRebate); } @Override public double acceptCash(double money) { return money * moneyRebate; } public double getMoneyRebate() { return moneyRebate; } public void setMoneyRebate(double moneyRebate) { this.moneyRebate = moneyRebate; } } public class CashReturn extends CashSuper { private double moneyCondition; private double moneyReturn; public CashReturn(String moneyCondition, String moneyReturn) { this.moneyCondition = Double.parseDouble(moneyCondition); this.moneyReturn = Double.parseDouble(moneyReturn); } @Override public double acceptCash(double money) { if(money >= this.moneyCondition) { return money - Math.floor(money / this.moneyCondition) * moneyReturn; } return money; } public double getMoneyCondition() { return moneyCondition; } public void setMoneyCondition(double moneyCondition) { this.moneyCondition = moneyCondition; } public double getMoneyReturn() { return moneyReturn; } public void setMoneyReturn(double moneyReturn) { this.moneyReturn = moneyReturn; } }
定义上下文类CashContext,维护对算法对象的引用。
public class CashContext { private CashSuper cs; public CashContext(String type) { switch(type){ case "正常收费": this.cs = new CashNormal(); break; case "满返": this.cs = new CashReturn("300", "100"); break; case "打折": this.cs = new CashRebate("0.8"); break; } } public double getResult(double money){ return cs.acceptCash(money); } }
客户端如下。
public class CacheClient { public static void main(String[] args) { double num = 10; double price = 100; CashContext csuper1 = new CashContext("正常收费"); CashContext csuper2 = new CashContext("满返"); CashContext csuper3 = new CashContext("打折"); System.out.println(csuper1.getResult(price * num)); System.out.println(csuper2.getResult(price * num)); System.out.println(csuper3.getResult(price * num)); } }
运行结果如下。
1000 700 800
策略模式由三类主要角色组成:
结构示意图如下

策略模式通用结构示意图如下

一个系统中有多个算法和类很相似,区分这些类和算法的只是其内部行为。
策略模式的优点有
策略模式的缺点有