策略模式
在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
前言
对于业务开发来说,业务逻辑的复杂是必然的,随着业务发展,需求只会越来越复杂,为了考虑到各种各样的情况,代码中不可避免的会出现很多if-else。
一旦代码中if-else过多,就会大大的影响其可读性和可维护性。
public String getCheckResult(String order) {
if ("校验1".equals(order)) {
return "执行业务逻辑1";
} else if ("校验2".equals(order)) {
return "执行业务逻辑2";
}else if ("校验3".equals(order)) {
return "执行业务逻辑3";
}else if ("校验4".equals(order)) {
return "执行业务逻辑4";
}else if ("校验5".equals(order)) {
return "执行业务逻辑5";
}else if ("校验6".equals(order)) {
return "执行业务逻辑6";
}else if ("校验7".equals(order)) {
return "执行业务逻辑7";
}else if ("校验8".equals(order)) {
return "执行业务逻辑8";
}else if ("校验9".equals(order)) {
return "执行业务逻辑9";
}
return "不在处理的逻辑中返回业务错误";
}
首先可读性,不言而喻,过多的if-else代码和嵌套,会使阅读代码的人很难理解到底是什么意思。尤其是那些没有注释的代码。
其次是可维护性,因为if-else特别多,想要新加一个分支的时候,就会很难添加,极其容易影响到其他的分支。
其实,if-else是有办法可以消除掉的,其中比较典型的并且使用广泛的就是借助策略模式和工厂模式,准确的说是利用这两个设计模式的思想,彻底消灭代码中的if-else。
一、策略模式是什么?
策略模式是oop中最著名的设计模式之一,是对方法行为的抽象,可以归类为行为设计模式,也是oop中interface经典的应用。其特点简单又实用,是我最喜欢的模式之一。策略模式定义了一个拥有共同行为的算法族,每个算法都被封装起来,可以互相替换,独立于客户端而变化。
我们可以从三个方面来理解策略模式:
算法族
使用多种不同的处理方式,做同样的事情,仅仅是具体行为有差别。这些处理方式,组合构成算法策略族,它们的共性,体现在策略接口行为上。
算法封装
将各个算法封装到不同的类中,这样有助于客户端来选择合适的算法。
可互相替换
客户端可以在运行时选择使用哪个算法,而且算法可以进行替换,所以客户端依赖于策略接口。
据此,可以推断出策略模式的使用场景:
针对同一问题的多种处理方式,仅仅是具体行为有差别时;
需要安全地封装多种同一类型的操作时;
同一抽象类有多个子类,而客户端需要使用 if-else 或者 switch-case 来选择具体子类时。
二、使用
1.UML图
可以看到策略模式涉及到三个角色:
环境(Context)角色:持有一个Strategy的引用。
抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
具体策略(ConcreteStrategy)角色:包装了相关的具体算法或行为。
2.示例
拿支付来说,支付包括微信支付,支付宝支付等,不同的支付有不同的支付方式
定义支付接口
strategy:
public interface Strategy {
boolean pay();
}
实现阿里云支付规则
// 阿里支付
public class ALiPayStrategy implements Strategy {
@Override
public boolean pay() {
System.out.println("aliPay===================");
return false;
}
}
实现微信支付规则
// 微信支付
public class WeiXinPayStrategy implements Strategy {
@Override
public boolean pay() {
System.out.println("WeiXinPay====================================");
return false;
}
}
context:
public interface Context {
Strategy getStrategy(Integer payType);
}
public class PayContext implements Context {
// 根据订单的具体类型,返回对应的支付策略
@Override
public Strategy getStrategy(Integer payType) {
switch (payType) {
case 1:
return new ALiPayStrategy();
case 2:
return new WeiXinPayStrategy();
default:
return null;
}
}
}
public static void main(String[] args) {
PayContext payContext = new PayContext();
// 阿里支付
Strategy strategy = payContext.getStrategy(1);
strategy.pay();
// 微信支付
Strategy strategy2 = payContext.getStrategy(2);
strategy2.pay();
}
3.拓展策略
如何拓展更多策略:例如再次增加qq支付
代码如下(示例):
增加QQ支付:
// qq支付
public class QQPayStrategy implements Strategy {
@Override
public boolean pay() {
System.out.println("QQPay====================================");
return false;
}
}
修改支付策略方法:
public class PayContext implements Context {
// 根据订单的具体类型,返回对应的支付策略
@Override
public Strategy getStrategy(Integer payType) {
switch (payType) {
case 1:
return new ALiPayStrategy();
case 2:
return new WeiXinPayStrategy();
case 3:
return new QQPayStrategy();
default:
return null;
}
}
}
实际应用中我们还可以借助工厂模式和Spring Bean的注册达到策略的自动加载,从而只用关系多个策略的执行规则.以上只是简单的实现策略模式的基本思路
4.优缺点
优点
易于扩展,增加一个新的策略只需要添加一个具体的策略类即可,基本不需要改变原有的代码,符合开放封闭原则
避免使用多重条件选择语句,充分体现面向对象设计思想
策略类之间可以自由切换,由于策略类都实现同一个接口,所以使它们之间可以自由切换
每个策略类使用一个策略类,符合单一职责原则
客户端与策略算法解耦,两者都依赖于抽象策略接口,符合依赖反转原则
客户端不需要知道都有哪些策略类,符合最小知识原则
缺点
策略模式,当策略算法太多时,会造成很多的策略类
客户端不知道有哪些策略类,不能决定使用哪个策略类,也可以考虑使用IOC容器和依赖注入的方式来解决
5.与工厂模式区别
差异:
用途不一样
工厂是创建型模式,它的作用就是创建对象;
策略是行为型模式,它的作用是让一个对象在许多行为中选择一种行为;
关注点不一样
一个关注对象创建
一个关注行为的封装
解决不同的问题:
工厂模式是创建型的设计模式,它接受指令,创建出符合要求的实例;它主要解决的是资源的统一分发,将对象的创建完全独立出来,让对象的创建和具体的使用客户无关。主要应用在多数据库选择,类库文件加载等。
策略模式是为了解决的是策略的切换与扩展,更简洁的说是定义策略族,分别封装起来,让他们之间可以相互替换,策略模式让策略的变化独立于使用策略的客户。
工厂相当于黑盒子,策略相当于白盒子;