【设计模式七】行为型模式(策略/职责链)

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 的处理、拦截器
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

土味儿~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值