第二章 商场促销-策略模式
1.1简单工厂实例
现在我们需要为某大型超市设计一个收银软件,收银员根据顾客购买的商品的单价和数量,向顾客收费
根据我们之前学到的简单工厂模式,我们可以这样做
代码结构图如下:

抽象产品Product角色CashSuper
package dp02Strategy;
public abstract class CashSuper {
//现金收取超类的抽象方法,收取现金,参数为原价,返回当前价
public abstract double acceptCash(double money);
}
具体Product角色之正常收费类CashNormal
package dp02Strategy;
public class CashNormal extends CashSuper{
public double acceptCash(double money) {
return money;
}
}
具体Product角色之打折收费类CashRebate
package dp02Strategy;
public class CashRebate extends CashSuper{
//暂定打折率为1
private double moneyRebate=1d;
CashRebate(double moneyRebate){
this.moneyRebate=moneyRebate;
}
public double acceptCash(double money) {
return money*moneyRebate;
}
}
具体Product角色之返利收费类,例如满400减50
package dp02Strategy;
public class CashReturn extends CashSuper{
//返利条件
private double returnCondition=0d;
//返利值
private double returnMoney=0d;
CashReturn(double returnCondition, double returnMoney) {
this.returnCondition = returnCondition;
this.returnMoney = returnMoney;
}
public double acceptCash(double money) {
double result=money;
if(money>returnCondition){
result=money-Math.floor(money/returnCondition)*returnMoney;
}
return result;
}
}
收费对象工厂CashFactory
package dp02Strategy;
public class CashFactory {
public static CashSuper createCashSuper(String type) {
CashSuper cs=null;
switch (type) {
case "正常收费":
cs=new CashNormal();
break;
case "打8折":
cs=new CashRebate(0.8);
break;
case "满300减100":
cs= new CashReturn(400, 50);
break;
}
return cs;
}
}
测试类Cashier
package dp02Strategy;
public class Cashier {
public static void main(String[] args) {
CashSuper cs1 = CashFactory.createCashSuper("正常收费");
double cash = cs1.acceptCash(100);
System.out.println(cash);
System.out.println("-----------------------------------");
CashSuper cs2 = CashFactory.createCashSuper("满400减50");
double cash2 = cs2.acceptCash(700);
System.out.println(cash2);
System.out.println("-----------------------------------");
CashSuper cs3 = CashFactory.createCashSuper("打8折");
double cash3 = cs3.acceptCash(700);
System.out.println(cash3);
}
}
测试结果

通过以上代码实现,我们发现简单工厂模式虽然也能解决这个问题,但是这个模式只是解决对象的创建问题,而且由于工厂本身包括了所有的收费方式,超市是可能经常性的更改打折额度和返利额度的,每次维护或者扩展收费方式都要改动这个工厂,以致代码需要重新编译部署,违反了开放-封闭原则;这真的是很糟糕的处理方式,所以用它不是最好的方法.面对算法的时常变动(产品的需求改动),应该使用更好的办法,即策略模式
2.1什么是策略模式?
策略模式 Strategy:它定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的用户
超市收银时如何促销,用打折还是返利,其实本质上都是一些算法,用工厂来生成算法对象,这没有错,但是算法本身只是一种策略,最重要的是这些算法是随时都可能互相替换的,这就是变化点,而封装变化点是我们面向对象的一种很重要的思维方式.
策略模式的UML图如下

Strategy类,定义所有支持的算法的公共接口
package dp02Strategy;
/**
*
* @ClassName: Strategy
* @Description: 策略类,定义所有支持的算法的公共接口
* @author lizhaowen
* @date 2017年4月12日 上午11:05:21
* 策略模式Strategy:它定义了算法家族,分别封装起来,让他们之间可以互相
* 替换,此模式让算法的的变化,不会影响到使用算法的客户
*/
public abstract class Strategy {
//算法方法
public abstract void algorithmInterface();
}
ConcreteStrategy,封装了具体的算法或者行为,它继承于Strategy
package dp02Strategy;
public class ConcreteStrategyA extends Strategy{
//算法A实现方法
public void algorithmInterface() {
System.out.println("算法A实现");
}
}
package dp02Strategy;
public class ConcreteStrategyB extends Strategy{
//算法B实现方法
public void algorithmInterface() {
System.out.println("算法B实现");
}
}
package dp02Strategy;
public class ConcreteStrategyC extends Strategy{
//算法C实现方法
public void algorithmInterface() {
System.out.println("算法C实现");
}
}
Context上下文,我们使用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用
package dp02Strategy;
/**
*
* @ClassName: Context
* @Description: Context上下文,用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用
* @author lizhaowen
* @date 2017年4月12日 上午11:17:10
*
*/
public class Context {
Strategy strategy;
public Context(Strategy strategy) {//初始化时,传入具体的的策略对象
this.strategy = strategy;
}
//上下文接口
public void ContextInterface(){
//根据具体的策略对象,调用其算法的方法
strategy.algorithmInterface();
}
}
测试类StrategyClient
package dp02Strategy;
public class StrategyClient {
public static void main(String[] args) {
// 由于实例化不同的策略,所以最终在调用context.ContextInterface();时,所获得的结果就不尽相同
Context context;
context = new Context(new ConcreteStrategyA());
context.ContextInterface();
context = new Context(new ConcreteStrategyB());
context.ContextInterface();
context = new Context(new ConcreteStrategyC());
context.ContextInterface();
}
}
对照一下我们使用简单工厂实现的功能代码,CashSuper就是抽象策略,而正常收费CashNormal 打折收费CashRebate 和返利收费CashReturn就是三个具体策略,也就是策略模式中说的具体算法.我们现在只需要添加一个CashContext类,并修改一下测试类就OK了!
代码结构图

CashContext类
package dp02Strategy02;
public class CashContext {
CashSuper cs;// 声明一个{@see CashSuper}对象
// 注意参数不是具体的收费类策略对象,而是一个字符串,表示收费类型
public CashContext(String type) {
//将实例化具体策略的过程由客户端转移到Context中,这是简单工厂的应用
switch (type) {
case "正常收费":
cs = new CashNormal();
break;
case "满300返100":
cs = new CashReturn(300, 100);
break;
case "打5折":
cs = new CashRebate(0.5);
break;
}
}
// 上下文接口
public double getResult(double money) {
return cs.acceptCash(money);
}
}
测试类
package dp02Strategy02;
public class CashClient {
public static void main(String[] args) {
double totalPrices=0d;
CashContext context = new CashContext("正常收费");
totalPrices = context.getResult(100);
System.out.println(totalPrices);
}
}
对比简单工厂模式与策略模式的客户端代码
|
//简单工厂模式的用法
CashSuper cs1 = CashFactory.createCashSuper("正常收费");
|
|
//策略模式与简单工厂模式结合的用法
CashContext context = new CashContext("正常收费");
|
经过对比,我们发现SimpleFactory我们需要让测试类认识两个类CashSuper和CashFactory,而Strategy与SimpleFactory相结合的用法,客户端只需要认识一个类CashContext就可以了,更加降低了耦合度
2.2解析策略模式
策略模式是一种定义了一系列算法的方法,从概念(策略模式 Strategy:它定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的用户 )上看,所有这些算法完成的都是相同的工作,只是实现不同,他可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合
其优点有:
优点其一,策略模式的Strategy类层次为Context定义了一系列的可供重用的算法或者行为,继承有助于析取这些算法中的公共功能,在本例中公共的功能是获得计算费用的结果getResult,这使得算法间有了抽象的父类CashSuper.
优点其二,简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试
优点其三,我们在最开始编程时,不得不在测试类中为了判断使用哪一个算法而用了switch条件分支,这是正常的,因为当不同的行为堆彻在一个类中,就难免使用条件语句来选择合适的行为,将这些行为封装在一个个独立的Strategy类中,可以在使用这些行为的类中消除条件语句,就超市收银系统的例子而言,在测试类中就消除条件语句,避免了大量的判断,这是非常重要的进展.我们用一句话概括之:策略模式封装了变化.
策略模式就是用来封装算法的,但在实践中,我们发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性.
来源:https://www.cnblogs.com/lizhaowen/p/6700165.html