目录
10、策略模式
Strategy Pattern
[ˈstrætədʒi]
1)基本介绍
- 策略模式(Strategy Pattern)中,定义算法族(策略组),分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
- 体现了几个设计原则
- 把变化的代码从不变的代码中分离出来
- 针对接口编程而不是具体类(定义了策略接口)
- 多用组合/聚合,少用继承(客户通过组合方式使用策略)
2)原理类图
客户 context 有成员变量 strategy 或者其他的策略接口,需要使用到哪个策略,可以在构造器中指定
3)鸭子案例分析
鸭子项目,具体要求如下:
有各种鸭子(比如野鸭、北京鸭、玩具鸭等,鸭子有各种行为,比如叫、飞行等)
显示鸭子的信息
飞翔行为接口 FlyBehavior
package strategy.behavior.fly;
/**
* @author 土味儿
* Date 2021/7/31
* @version 1.0
* 飞翔行为接口
*/
public interface FlyBehavior {
void fly();
}
飞翔行为实现类
public class NoFlyBehavior implements FlyBehavior{
@Override
public void fly() {
System.out.println("不会飞!");
}
}
public class LowFlyBehavior implements FlyBehavior{
@Override
public void fly() {
System.out.println("可以低飞!");
}
}
public class HighFlyBehavior implements FlyBehavior{
@Override
public void fly() {
System.out.println("可以高飞!");
}
}
叫声行为接口 QuackBehavior
package strategy.behavior.quack;
/**
* @author 土味儿
* Date 2021/7/31
* @version 1.0
* 叫声接口
*/
public interface QuackBehavior {
void quack();
}
叫声行为实现类
public class NoQuackBehavior implements QuackBehavior{
@Override
public void quack() {
System.out.println("不会叫!");
}
}
public class CanQuackBehavior implements QuackBehavior{
@Override
public void quack() {
System.out.println("可以叫!");
}
}
鸭子抽象类 Duck
package strategy.duck;
import strategy.behavior.fly.FlyBehavior;
import strategy.behavior.quack.QuackBehavior;
/**
* @author 土味儿
* Date 2021/7/31
* @version 1.0
* 鸭子抽象类
*/
public abstract class Duck {
// 飞翔行为(聚合)
protected FlyBehavior flyBehavior;
// 叫声行为(聚合)
protected QuackBehavior quackBehavior;
/**
* 飞翔
*/
public void fly() {
if (flyBehavior != null) {
flyBehavior.fly();
}
}
/**
* 叫声
*/
public void quack() {
if (quackBehavior != null) {
quackBehavior.quack();
}
}
/**
* 显示信息
*/
public abstract void display();
}
鸭子实现类
public class WildDuck extends Duck {
/**
* 构造器
* 野鸭可以高飞,可以叫
*/
public WildDuck() {
flyBehavior = new HighFlyBehavior();
quackBehavior = new CanQuackBehavior();
}
@Override
public void display() {
System.out.println("\n===== 野鸭 =====");
}
}
public class PekingDuck extends Duck {
/**
* 构造器
* 北京鸭可以低飞,可以叫
*/
public PekingDuck() {
flyBehavior = new LowFlyBehavior();
quackBehavior = new CanQuackBehavior();
}
@Override
public void display() {
System.out.println("\n===== 北京鸭 =====");
}
}
public class ToyDuck extends Duck {
/**
* 构造器
* 玩具鸭不会飞,不会叫
*/
public ToyDuck() {
flyBehavior = new NoFlyBehavior();
quackBehavior = new NoQuackBehavior();
}
@Override
public void display() {
System.out.println("\n===== 玩具鸭 =====");
}
}
客户端
package strategy;
import strategy.duck.*;
/**
* @author 土味儿
* Date 2021/7/31
* @version 1.0
* 客户端
*/
public class Client {
public static void main(String[] args) {
// 测试野鸭
Duck wildDuck = new WildDuck();
wildDuck.display();
wildDuck.fly();
wildDuck.quack();
// 测试北京鸭
Duck pekingDuck = new PekingDuck();
pekingDuck.display();
pekingDuck.fly();
pekingDuck.quack();
// 测试玩具鸭
Duck toyDuck = new ToyDuck();
toyDuck.display();
toyDuck.fly();
toyDuck.quack();
}
}
运行结果
===== 野鸭 =====
可以高飞!
可以叫!
===== 北京鸭 =====
可以低飞!
可以叫!
===== 玩具鸭 =====
不会飞!
不会叫!
4)员工年假年终奖案例
经典案例:见另一篇博客 员工年假 策略模式
5)JDK 中 Arrays 的案例
JDK的 Arrays 的 Comparator 就使用了策略模式
6)注意事项和细节
- 策略模式的关键是:分析项目中变化部分与不变部分
- 核心思想是:多用组合/ 聚合,少用继承;用行为类组合,而不是行为的继承;更有弹性
- 体现了OCP开闭原则,客户端增加行为不用修改原有代码,只要添加一种策略(或者行为)即可,避免了使用多重转移语句(if..else if..else)
- 提供了可以替换继承关系的办法:策略模式将算法封装在独立的 Strategy 类中使得可以独立于其 Context 改变它,易于切换、易于理解、易于扩展
- 需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞
11、职责链模式
Chain of Responsibility Pattern
responsibility [rɪˌspɑːnsəˈbɪləti] 职责
chain [tʃeɪn] 链条
1)基本介绍
- 职责链模式(Chain of Responsibility Pattern),又叫责任链模式,为请求创建了一个接收者对象的链。这种模式对请求的 发送者 和 接收者 进行解耦
- 职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的 请求传给下一个接收者,依此类推
- 属于行为型模式
2)原理类图
- Handler:抽象的处理者;定义了一个处理请求的接口,同时包含另外 Handler
- ConcreteHandler:A 、B是具体的处理者,处理它自己负责的请求,可以访问它的后继者(即下一个处理者),如果可以处理当前请求,则处理,否则就将该请求交给后继者去处理,从而形成一个职责链
- Request:表示一个请求,含义很多属性
3)案例分析
学校 OA 系统的采购审批项目:
需求采购员采购教学器材
- 如果金额小于等于 5000,由教学主任审批
- 如果金额小于等于 10000,由院长审批
- 如果金额小于等于 30000,由副校长审批
- 如果金额超过 30000,有校长审批
采购请求 PurchaseRequest
package chain;
/**
* @author 土味儿
* Date 2021/8/1
* @version 1.0
* 采购请求
*/
public class PurchaseRequest {
// 请求编号
private int id;
// 请求类型
private int type;
// 请求金额
private float money;
public PurchaseRequest(int id, int type, float money) {
this.id = id;
this.type = type;
this.money = money;
}
public int getId() {
return id;
}
public int getType() {
return type;
}
public float getMoney() {
return money;
}
}
抽象批准人 Approver
package chain.approver;
import chain.PurchaseRequest;
/**
* @author 土味儿
* Date 2021/8/1
* @version 1.0
* 抽象批准人
*/
public abstract class Approver {
// 批准人名称
protected String name;
// 下一个批准人
protected Approver approver;
/**
* 构造器
* @param name
*/
public Approver(String name) {
this.name = name;
}
/**
* 设置下一下批准人
* @param approver
*/
public void setApprover(Approver approver) {
this.approver = approver;
}
/**
* 处理请求
* 抽象方法:由子类实现
* @param purchaseRequest
*/
public abstract void processRequest(PurchaseRequest purchaseRequest);
}
具体批准人
部门、学院、副校长、校长
public class DepartmentApprover extends Approver {
/**
* 构造器
*
* @param name
*/
public DepartmentApprover(String name) {
super(name);
}
/**
* 处理请求
*
* @param purchaseRequest
*/
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getMoney() <= 5000) {
System.out.println("请求编号 id= " + purchaseRequest.getId() + " 被 " + name + "处理了!");
}else{
// 超出自已处理范围,传给下一个处理者
approver.processRequest(purchaseRequest);
}
}
}
public class CollegeApprover extends Approver {
/**
* 构造器
*
* @param name
*/
public CollegeApprover(String name) {
super(name);
}
/**
* 处理请求
*
* @param purchaseRequest
*/
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
float money = purchaseRequest.getMoney();
if (money > 5000 && money <= 10000) {
System.out.println("请求编号 id= " + purchaseRequest.getId() + " 被 " + name + "处理了!");
}else{
// 超出自已处理范围,传给下一个处理者
approver.processRequest(purchaseRequest);
}
}
}
public class ViceSchoolMasterApprover extends Approver {
/**
* 构造器
*
* @param name
*/
public ViceSchoolMasterApprover(String name) {
super(name);
}
/**
* 处理请求
*
* @param purchaseRequest
*/
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
float money = purchaseRequest.getMoney();
if (money > 1000 && money <= 30000) {
System.out.println("请求编号 id= " + purchaseRequest.getId() + " 被 " + name + "处理了!");
}else{
// 超出自已处理范围,传给下一个处理者
approver.processRequest(purchaseRequest);
}
}
}
public class SchoolMasterApprover extends Approver {
/**
* 构造器
*
* @param name
*/
public SchoolMasterApprover(String name) {
super(name);
}
/**
* 处理请求
*
* @param purchaseRequest
*/
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getMoney() > 30000) {
System.out.println("请求编号 id= " + purchaseRequest.getId() + " 被 " + name + "处理了!");
}else{
// 超出自已处理范围,传给下一个处理者
approver.processRequest(purchaseRequest);
}
}
}
测试客户端 Client
package chain;
import chain.approver.CollegeApprover;
import chain.approver.DepartmentApprover;
import chain.approver.SchoolMasterApprover;
import chain.approver.ViceSchoolMasterApprover;
/**
* @author 土味儿
* Date 2021/8/1
* @version 1.0
* 测试客户端
*/
public class Client {
public static void main(String[] args) {
// 创建采购请求
PurchaseRequest purchaseRequest = new PurchaseRequest(2, 3, 50100);
// 创建批准人
DepartmentApprover departmentApprover = new DepartmentApprover("张主任");
CollegeApprover collegeApprover = new CollegeApprover("刘院长");
ViceSchoolMasterApprover viceSchoolMasterApprover = new ViceSchoolMasterApprover("李副校长");
SchoolMasterApprover schoolMasterApprover = new SchoolMasterApprover("王校长");
// 给批准人设置下一个批准人 (形成闭环)
departmentApprover.setApprover(collegeApprover);
collegeApprover.setApprover(viceSchoolMasterApprover);
viceSchoolMasterApprover.setApprover(schoolMasterApprover);
// 校长的下一个批准人是部门批准人,形式环路
schoolMasterApprover.setApprover(departmentApprover);
// 处理请求(可以用任意一个批准人)
departmentApprover.processRequest(purchaseRequest);
}
}
运行结果
请求编号 id= 2 被 王校长处理了!
4)SpringMVC 中的应用
SpringMVC-HandlerExecutionChain 类就使用到职责链模式
- Springmvc 请求的流程图中,执行了拦截器相关方法 interceptor.preHandler 等等
- 在处理 SpringMvc 请求时,使用到 职责链模式 和 适配器模式
- HandlerExecutionChain 主要负责的是请求拦截器的执行和请求分配,它本身不处理请求,只是将请求分配给链上注册处理器执行,这是职责链实现方式,减少 职责链本身 与 处理逻辑 之间的耦合,规范了处理流程
- HandlerExecutionChain 维护了 HandlerInterceptor 的集合,可以向其中注册相应的拦截器
5)注意事项和细节
- 将请求和处理分开,实现解耦,提高系统的灵活性
- 简化了对象,使对象不需要知道链的结构
- 性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般通过在 Handler 中设置一个最大节点数量,在 setNext() 方法中判断是否已经超过阀值,超过则不允许注册,避免出现超长链无意识地破坏系统性能
- 调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂
- 最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假/加薪等审批流程、Java Web中 Tomcat 对 Encoding 的处理、拦截器