策略模式
- 策略对应于解决某一个问题的一个算法族,允许用户从该算法族中任选一种算法用来解决某一问题,同时可以方便的更新算法和增加算法,符合开闭原则(对修改关闭,扩展开放)。
- 在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
- 在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。
- 策略对象改变 context 对象的执行算法。
本质:分离算法,选择实现
优点:
- 算法可以自由切换
- 避免使用多重条件判断
- 扩展性良好
缺点:
- 策略类会增多
- 所有策略类都需要对外暴露
使用场景:
- 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为
- 一个系统需要动态地在几种算法中选择一种
- 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现
开发中常见场景:
- JavaSE中GUI编程,布局管理
- Spring框架中,Resource接口,资源访问策略
注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
实现
案例:这里针对不同客户不同的进货方式,供应商提供不同的报价
如果不采用策略模式,代码如下:
package com.ly.strategy;
/**
* liyang 2020-08-28
* 测试不使用策略模式
*/
public class NonStrategy {
private final String NEW_FEW = "新客户小批量";
private final String NEW_MANY = "新客户大批量";
private final String OLD_FEW = "老客户小批量";
private final String OLD_MANY = "老客户大批量";
public static void main(String[] args) {
NonStrategy nonStrategy = new NonStrategy();
double originalPrice = 9999.99;
System.out.println("报价:" + nonStrategy.getPrice(nonStrategy.NEW_FEW, originalPrice));
System.out.println("报价:" + nonStrategy.getPrice(nonStrategy.NEW_MANY, originalPrice));
System.out.println("报价:" + nonStrategy.getPrice(nonStrategy.OLD_FEW, originalPrice));
System.out.println("报价:" + nonStrategy.getPrice(nonStrategy.OLD_MANY, originalPrice));
}
private double getPrice(String type, double originalPrice) {
if(type.equals(NEW_FEW)) {
System.out.println(type + ":" + "不打折");
}
else if(type.equals(NEW_MANY)) {
System.out.println(type + ":" +"打8折");
originalPrice = originalPrice * 0.8;
}
else if(type.equals(OLD_FEW)) {
System.out.println(type + ":" +"打9折");
originalPrice = originalPrice * 0.9;
}
else if(type.equals(OLD_MANY)) {
System.out.println(type + ":" +"打7折");
originalPrice = originalPrice * 0.7;
}
return originalPrice;
}
}
结果:
新客户小批量:不打折
报价:9999.99
新客户大批量:打8折
报价:7999.992
老客户小批量:打9折
报价:8999.991
老客户大批量:打7折
报价:6999.9929999999995
Process finished with exit code 0
假设,类型特别多,算法比较复杂时,整个条件控制代码会变得很长,难以维护,不符合开闭原则。
采用策略模式,实现如下:
步骤1:创建策略接口,定义策略函数接口
package com.ly.strategy;
/**
* liyang 2020-08-27
* 策略接口
*/
public interface Strategy {
public double getPrice(double originalPrice);
}
步骤2:创建具体实现类,实现strategy接口,重写策略算法函数
package com.ly.strategy;
/**
* liyang 2020-08-27
* 新客户少批量的价格策略
*/
public class NewCustomerFewStrategy implements Strategy {
@Override
public double getPrice(double originalPrice) {
System.out.println("新客户小批量进货,不打折");
return originalPrice;
}
}
package com.ly.strategy;
/**
* liyang 2020-08-27
* 新客户大批量的价格策略
*/
public class NewCustomerManyStrategy implements Strategy {
@Override
public double getPrice(double originalPrice) {
System.out.println("新客户大批量进货,打8折");
return originalPrice * 0.8;
}
}
package com.ly.strategy;
/**
* liyang 2020-08-27
* 老客户少批量的价格策略
*/
public class OldCustomerFewStrategy implements Strategy {
@Override
public double getPrice(double originalPrice) {
System.out.println("老客户小批量进货,打9折");
return originalPrice * 0.9;
}
}
package com.ly.strategy;
/**
* liyang 2020-08-27
* 老客户大批量的价格策略
*/
public class OldCustomerManyStrategy implements Strategy {
@Override
public double getPrice(double originalPrice) {
System.out.println("老客户大批量进货,打7折");
return originalPrice * 0.7;
}
}
步骤3:上下文Context,根据策略类型,调用相应策略对象的策略算法
package com.ly.strategy;
/**
* liyang 2020-08-27
* 上下文类,拥有strategy类对象
* 负责和具体的策略类进行交互
* 从而具体的算法彻底和客户端分离,对外暴露的更少,且算法相对于客户端独立变化
*
* 如果使用spring的依赖注入功能,还可以通过配置文件动态的注入不用的策略对象,从而动态的切换不同的算法
*/
public class Context {
private Strategy strategy;
public Context() {
}
//可以通过构造器方法来注入
public Context(Strategy strategy) {
super();
this.strategy = strategy;
}
//也可以通过set方法进行注入
public Context setStrategy(Strategy strategy) {
this.strategy = strategy;
return this;
}
public double getPrice(double originalPrice) {
return strategy.getPrice(originalPrice);
}
}
步骤4:客户端测试
package com.ly.strategy;
/**
* liyang 2020-08-27
* 策略模式的客户端测试
*/
public class Client {
public static void main(String[] args) {
//初始价格
double originalPrice = 9999.99;
double currentPrice;
//策略管理类
Context context = new Context();
//不同的客户不同采购量
NewCustomerFewStrategy newFew = new NewCustomerFewStrategy();
NewCustomerManyStrategy newMany = new NewCustomerManyStrategy();
OldCustomerFewStrategy oldFew = new OldCustomerFewStrategy();
OldCustomerManyStrategy oldMany = new OldCustomerManyStrategy();
//新客户小批量
currentPrice = context.setStrategy(newFew).getPrice(originalPrice);
System.out.println("报价:" + currentPrice);
//新客户大批量
currentPrice = context.setStrategy(newMany).getPrice(originalPrice);
System.out.println("报价:" + currentPrice);
//老客户小批量
currentPrice = context.setStrategy(oldFew).getPrice(originalPrice);
System.out.println("报价:" + currentPrice);
//老客户大批量
currentPrice = context.setStrategy(oldMany).getPrice(originalPrice);
System.out.println("报价:" + currentPrice);
}
}
结果:
新客户小批量进货,不打折
报价:9999.99
新客户大批量进货,打8折
报价:7999.992
老客户小批量进货,打9折
报价:8999.991
老客户大批量进货,打7折
报价:6999.9929999999995
Process finished with exit code 0