策略模式
策略模式
目录- 策略模式
- 1.定义
- 2.为什么使用策略模式
- 3.简单业务场景说明
- 4.策略模式与简单工厂模式的对比
- 相似点
- 差异
- 5.策略模式与简单工厂模式的结合
- 6.策略模式在Java中的应用
1.定义
- 策略模式
- 它定义了算法家族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化,不会影响到使用算法的客户。
- 策略模式的基本代码
package cn.sun.code.two.strategy;
/**
* Created by rider on 2018/11/22.
*
* 抽象算法类
*/
abstract class Strategy {
// 算法方法
public abstract void AlgorithmInterface();
}
// 具体算法A
class ConcreteStrategyA extends Strategy {
// 算法A实现方法
@Override
public void AlgorithmInterface() {
System.out.println("算法A实现");
}
}
// 具体算法B
class ConcreteStrategyB extends Strategy {
// 算法B实现方法
@Override
public void AlgorithmInterface() {
System.out.println("算法B实现");
}
}
// 具体算法C
class ConcreteStrategyC extends Strategy {
// 算法B实现方法
@Override
public void AlgorithmInterface() {
System.out.println("算法C实现");
}
}
package cn.sun.code.two.strategy;
/**
* Created by rider on 2018/11/22.
*
* 上下文
*/
public class Context {
Strategy strategy = null;
/**
* 初始化时,传入具体的策略对象
*
* @param strategy
*/
public Context(Strategy strategy) {
this.strategy = strategy;
}
/**
* 根据具体的策略对象,调用其算法的方法
*
* 上下文接口
*/
public void ContextInterface() {
strategy.AlgorithmInterface();
}
/**
* 客户端代码
* 由于实例化不同的策略,所以最终在调用context.ContextInterface时所获得的结果就不尽相同
*/
public static void main(String[] args) {
Context context = null;
context = new Context(new ConcreteStrategyA());
context.ContextInterface();
context = new Context(new ConcreteStrategyB());
context.ContextInterface();
context = new Context(new ConcreteStrategyC());
context.ContextInterface();
}
}
2.为什么使用策略模式
- 策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少各种算法类与使用算法类之间的耦合。
- 策略模式的另一个优点是简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。
- 策略模式就是用来封装算法的,但在实践中,我们发现可以用来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。
3.简单业务场景说明
- 做一个商场收银软件,根据客户所购买商品的单价和数量,向客户收费。但是商场经常会有一些活动,比如说满减,打折之类的活动。基于这样业务场景写出相关代码。
-
新手(好吧,我一开始也是这么考虑的...)最常见的一种写法就是在一个类中通过各种逻辑判断语句写出以上代码的实现,但是这样,商场频繁的活动以及新增活动都要修改这个类,这其实违背了单一职责原则和“开放--封闭”原则。也说明你的简单工厂模式白学了...
-
其实可以考虑用简单工厂模式实现以上功能。
- 子类要写几个?不能八折、七折、五折各写一个子类。因为因为分类的基础是抽象,具有相同属性和功能的对象的抽象集合才是类。
- 简单工厂模式实现商场收银软件
package cn.sun.code.two.shop; /** * 现金收费抽象类 */ abstract class CashSuper { /** * 现金抽取超类的抽象方法 * * @param money 原价 * @return 当前价 */ public abstract double acceptCash(double money); } package cn.sun.code.two.shop; /** * 正常收费子类 */ public class CashNormal extends CashSuper { @Override public double acceptCash(double money) { return money; } } package cn.sun.code.two.shop; /** * 打折收费子类 */ public class CashRebate extends CashSuper { private double moneyRebate = 1d; public CashRebate(String moneyRebate) { this.moneyRebate = Double.parseDouble(moneyRebate); } @Override public double acceptCash(double money) { return money * moneyRebate; } } package cn.sun.code.two.shop; /** * 返利收费子类 */ public class CashReturn extends CashSuper { private double moneyCondition = 0.0d; private double moneyReturn = 0.0d; public CashReturn(String moneyCondition, String moneyReturn) { this.moneyCondition = Double.parseDouble(moneyCondition); this.moneyReturn = Double.parseDouble(moneyReturn); } @Override public double acceptCash(double money) { double result = money; if (money >= moneyCondition) { result = money - Math.floor(money / moneyCondition) * moneyReturn; } return result; } } package cn.sun.code.two.shop; /** * 现金收费工厂 */ public class CashFactory { public static CashSuper createCashAccept(String type) { CashSuper cs = null; switch (type) { case "正常收费": cs = new CashNormal(); break; case "满300反100": cs = new CashReturn("300", "100"); break; case "打8折": cs = new CashRebate("0.8"); break; } return cs; } }
-
简单工厂模式虽然也能解决这个问题,但是这个模式只是解决对象的创建问题,面对算法的时常变动,应该有更好的办法。
-
策略模式实现商场收银软件
package cn.sun.code.two.shopstrategy;
/**
* 现金收费抽象类
*/
abstract class CashSuper {
/**
* 现金抽取超类的抽象方法
*
* @param money 原价
* @return 当前价
*/
public abstract double acceptCash(double money);
}
package cn.sun.code.two.shopstrategy;
/**
* 正常收费子类
*/
public class CashNormal extends CashSuper {
@Override
public double acceptCash(double money) {
return money;
}
}
package cn.sun.code.two.shopstrategy;
/**
* 打折收费子类
*/
public class CashRebate extends CashSuper {
private double moneyRebate = 1d;
public CashRebate(String moneyRebate) {
this.moneyRebate = Double.parseDouble(moneyRebate);
}
@Override
public double acceptCash(double money) {
return money * moneyRebate;
}
}
package cn.sun.code.two.shopstrategy;
/**
* 返利收费子类
*/
public class CashReturn extends CashSuper {
private double moneyCondition = 0.0d;
private double moneyReturn = 0.0d;
public CashReturn(String moneyCondition, String moneyReturn) {
this.moneyCondition = Double.parseDouble(moneyCondition);
this.moneyReturn = Double.parseDouble(moneyReturn);
}
@Override
public double acceptCash(double money) {
double result = money;
if (money >= moneyCondition) {
result = money - Math.floor(money / moneyCondition) * moneyReturn;
}
return result;
}
}
package cn.sun.code.two.shopstrategy;
/**
* 正常收费子类
*/
public class CashNormal extends CashSuper {
@Override
public double acceptCash(double money) {
return money;
}
}
package cn.sun.code.two.shopstrategy;
/**
* 打折收费子类
*/
public class CashRebate extends CashSuper {
private double moneyRebate = 1d;
public CashRebate(String moneyRebate) {
this.moneyRebate = Double.parseDouble(moneyRebate);
}
@Override
public double acceptCash(double money) {
return money * moneyRebate;
}
}
package cn.sun.code.two.shopstrategy;
/**
* 返利收费子类
*/
public class CashReturn extends CashSuper {
private double moneyCondition = 0.0d;
private double moneyReturn = 0.0d;
public CashReturn(String moneyCondition, String moneyReturn) {
this.moneyCondition = Double.parseDouble(moneyCondition);
this.moneyReturn = Double.parseDouble(moneyReturn);
}
@Override
public double acceptCash(double money) {
double result = money;
if (money >= moneyCondition) {
result = money - Math.floor(money / moneyCondition) * moneyReturn;
}
return result;
}
}
package cn.sun.code.two.shopstrategy;
/**
* Created by rider on 2018/11/22.
*/
public class CashContext {
// 声明一个CashSuper对象
private CashSuper cs;
// 通过构造方法,传入具体的收费策略
public CashContext(CashSuper cashSuper) {
this.cs = cashSuper;
}
// 根据收费策略的不同,获取计算结果
public double getResult(double money) {
return cs.acceptCash(money);
}
/**
* 客户端代码
*/
public static void main(String[] args) {
CashContext cc = null;
switch ("") {
case "正常收费":
cc = new CashContext(new CashNormal());
break;
case "满300减100":
cc = new CashContext(new CashReturn("300", "100"));
break;
case "打8折":
cc = new CashContext(new CashRebate("0.8"));
break;
}
}
}
4.策略模式与简单工厂模式的对比
- 以下内容参考
相似点
- 在模式结构上,两者很相似
- 其实很多人之所以觉得二者相似是因为它们都是通过多态的方式实现不同的行为,其实从结构上来说,二者是有区别的:
- 简单工厂模式实际创建出一个产品对象,但是用产品基类的引用来接收,通过这种方式实多态
- 策略模式则是在初始化Context时传入具体的策略对象,而构造器用策略基类来接受具体的策略对象,通过这种方式实现多态
差异
- 用途不一样
- 工厂模式是创建型模式,它的作用就是创建对象
- 策略模式是行为模式,它的作用是让一个对象在许多行为中选择一种行为
- 关注点不一样
- 一个关注对象的创建
- 一个关注行为的封装
- 解决不同的问题
- 工厂模式是创建型的设计模式,它接受指令,创建出符合要求的实例;它主要解决的是资源的统一分发,将对象的创建完全独立出来,让对象的创建和具体的使用客户无关。主要应用在多数据库选择,类库文件加载等。
- 策略模式是为了解决的是策略的切换与扩展,更简洁的说是定义策略族,分别封装起来,让他们之间可以相互替换,策略模式让策略的变化独立于使用策略的客户。
- 在运行时,两者都是通过传入参数进行配置。简单工厂模式是选择创建出需要的对象,而策略模式则是配置出需要的行为算法。一个是对象的创建,一个是行为算法的替换。
5.策略模式与简单工厂模式的结合
package cn.sun.code.two.shopstrategy;
/**
* Created by rider on 2018/11/22.
* 改造后的CashContext
*/
public class CashContext2 {
CashSuper cs = null;
public CashContext2(String type) { // 注意参数不是具体的收费策略对象,而是一个字符串,表示收费类型
switch (type) {
case "正常收费":
CashNormal cashNormal = new CashNormal();
cs = cashNormal;
break;
case "满300减100":
CashReturn cashReturn = new CashReturn("300", "100");
cs = cashReturn;
break;
case "打8折":
CashRebate cashRebate = new CashRebate("0.8");
cs = cashRebate;
break;
// 将实例化具体策略的过程由客户端转移到Context中。简单工厂的应用
}
}
public double getResult(double money) {
return cs.acceptCash(money);
}
}
6.策略模式在Java中的应用
- 策略模式在Java中的最经典应用就是Compartor接口的使用。比如Collections中的sort方法,当集合中为自定义对象时(本身不具备比较性的T),可以通过传入一个比较器来使用该方法排序。
public static void sort(List list, Comparator<? super T> c) {
Object[] a = list.toArray();
Arrays.sort(a, (Comparator)c);
ListIterator i = list.listIterator();
for (int j=0; j
- 在使用
sort()
方法过程中,通常传入一个自定义的比较器Comparator以是的集合按照自己定义的方式排序。