书接上回,本篇讲一下行为型模式-策略模式
策略设计模式
定义:定义一系列算法,把它们一个个封装起来,并且使它们可相互替换。让算法能独立于客户端。
UML图
IStrategy:策略接口,定制策略算法基本规则。
ConcreteStrategyA/B:具体策略实现,实现IStrategy接口,不同实现类,实现不同算法。
Context:上下文,复杂与具体策略类交互。
/**
* 策略接口
*/
public interface IStrategy {
/**
* 策略算法逻辑
*/
void doLogic();
}
/**
* 策略实现类
*/
public class ConcreteStrategyA implements IStrategy{
@Override
public void doLogic() {
System.out.println("具体策略实现A");
}
}
/**
* 策略实现类
*/
public class ConcreteStrategyB implements IStrategy{
@Override
public void doLogic() {
System.out.println("具体策略实现B");
}
}
/**
* 上下文环境,负责与策略类交互
*/
public class Context {
//指定的执行策略对象
private IStrategy strategy;
public Context(IStrategy strategy) {
this.strategy = strategy;
}
//执行策略
void strategyExecute(){
strategy.doLogic();
}
}
public class App {
public static void main(String[] args) {
Context context = new Context(new ConcreteStrategyA());
context.strategyExecute();
context = new Context(new ConcreteStrategyB());
context.strategyExecute();
}
}
解析
在App测试类中,通过new不同的策略实现类对象,Context类执行的结果不一致,但好处在于,后续如果想拓展出现的算法,只需要创建新的策略实现类即可,这妥妥的开闭原则。
案例分析
需求:设计一套支付系统,支持微信支付,支付宝支付,银联支付
分析:
从需求上看,支付有3种实现方式微信,支付宝,银联,用户在某一时刻支付时,需要选择这3种支付中的一种,并保证支付能成功。从设计上考虑,3种支付都属于支付问题,算法主体一致,实现各有不同,这是可以使用策略模式实现。
支付策略接口:负责定制支付算法实现规则(定义支付方法骨架)
微信,支付宝,银联:实现各自的支付算法逻辑。
上下文:用户付款时,根据需要选择不同的支付方式。
UML图
IPayStrategy
/**
* 支付策略
*/
public interface IPayStrategy {
//常量标记
int PAY_WECHAT = 1;
int PAY_ALI = 2;
int PAY_UNION = 3;
/**
* 支付
*/
void pay(int money);
}
UnionStrategy
//银联支付
public class UnionStrategy implements IPayStrategy{
@Override
public void pay(int money) {
System.out.println("银联支付:" + money);
}
}
WeChatPayStrategy
//微信支付
public class WeChatPayStrategy implements IPayStrategy{
@Override
public void pay(int money) {
System.out.println("微信支付:" + money);
}
}
AliPayStrategy
//支付宝支付
public class AliPayStrategy implements IPayStrategy{
@Override
public void pay(int money) {
System.out.println("支付宝支付:" + money);
}
}
MeituanApp
/**
* 美团App
*/
public class MeituanApp {
//订单支付
public void orderPay(int type, int money){
if(IPayStrategy.PAY_WECHAT == type){
new WeChatPayStrategy().pay(money);
}else if(IPayStrategy.PAY_ALI == type){
new AliPayStrategy().pay(money);
}else if(IPayStrategy.PAY_UNION == type){
new UnionStrategy().pay(money);
}else{
throw new RuntimeException("支付失败");
}
}
}
public class App {
public static void main(String[] args) {
//模拟用户支付
MeituanApp app = new MeituanApp();
//模拟用户挑选支付宝支付
app.orderPay(IPayStrategy.PAY_ALI, 100);
//模拟用户挑选微信支付
app.orderPay(IPayStrategy.PAY_WECHAT, 100);
//模拟用户挑选银联支付
app.orderPay(IPayStrategy.PAY_UNION, 100);
}
}
解析
上面美团MeituanApp 是支付策略实现的关键,根据用户不同选择,使用不同的支付系统。但是,使用if...else if...else 作判断,不算太优雅,可以适当转换一下。
/**
* 美团App
*/
public class MeituanApp {
//缓存支付策略
private static Map<Integer, IPayStrategy> STRATEGY_MAP = new HashMap<>();
static{
STRATEGY_MAP.put(IPayStrategy.PAY_WECHAT, new WeChatPayStrategy());
STRATEGY_MAP.put(IPayStrategy.PAY_ALI, new AliPayStrategy());
STRATEGY_MAP.put(IPayStrategy.PAY_UNION, new UnionStrategy());
}
//订单支付
public void orderPay(int type, int money){
IPayStrategy payStrategy = STRATEGY_MAP.get(type);
if(payStrategy != null){
payStrategy.pay(money);
}else{
throw new RuntimeException("支付失败");
}
}
}
适用场景
1>系统有很多类,而他们的区别仅仅在于它们的行为不同(实现算法)。
2>一个系统需要从多种执行策略中选择一种执行。
优缺点
优点
1>符合开闭原则,能优雅实现对修改关闭,对拓展开发
2>可以避免使用多重条件转移语句(比如:if..else if..else使用)
3>提高算法的保密性和安全性(算法封装在类内部)
缺点
1>使用策略的客户端需要知道所有策略类
2>会生成很多策略类
开发案例
JDK中的Compartor比较接口
Compartor接口就是一个策略接口, 每一次比较规则就是一个策略实现
比如:集合倒序排
Collections.sort(Arrays.asList(1, 2, 3, 4, 5), (v1, v2)->v2.compareTo(v1));
Spring中的资源文件加载
Resource 资源加载类,它也是一个策略接口类,有不同的加载实现
public interface Resource extends InputStreamSource {
boolean exists();
default boolean isReadable() {
return this.exists();
}
default boolean isOpen() {
return false;
}
default boolean isFile() {
return false;
}
URL getURL() throws IOException;
URI getURI() throws IOException;
}
实现类1:ClassPathResource
public class ClassPathResource extends AbstractFileResolvingResource {
public ClassPathResource(String path, @Nullable ClassLoader classLoader) {
Assert.notNull(path, "Path must not be null");
String pathToUse = StringUtils.cleanPath(path);
if (pathToUse.startsWith("/")) {
pathToUse = pathToUse.substring(1);
}
this.path = pathToUse;
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
}
}
实现类2:UrlResource
public class UrlResource extends AbstractFileResolvingResource {
public UrlResource(URI uri) throws MalformedURLException {
Assert.notNull(uri, "URI must not be null");
this.uri = uri;
this.url = uri.toURL();
}
}
总结
策略模式本质:分离算法,选择性实现