设计模式の状态策略责任链模式

文章目录

  • 前言
  • 一、状态模式
  • 二、策略模式
  • 三、责任链模式


前言

  本篇是关于设计模式中的状态模式、策略模式、以及责任链模式的学习笔记。


一、状态模式

  状态模式是一种行为设计模式,核心思想在于,使某个对象在其内部状态改变时,改变该对象的行为,使对象看起来像是改变了其类。将状态的相关行为封装到独立的状态类中,并通过在运行时切换状态对象来改变对象的行为。实现方式在于,将状态抽取成单独的类,并由上下文类统一管理。
  状态模式的结构包括以下几个主要角色:

  1. 上下文类:它维护一个当前状态的实例,并且对客户端公开其接口。
  2. 抽象状态类:定义了所有具体状态类需要实现的接口,抽取出所有状态下共有的方法。
  3. 具体状态类:实现了抽象状态类,根据自身状态的特点,有选择性第重写父类方法的逻辑。

  假设需要设计一个抽奖系统,有四种状态:

  • 不能抽奖的状态
  • 可以抽奖的状态
  • 发放奖品的状态
  • 奖品发放完成的状态

  状态流转方式如下:
在这里插入图片描述  体现在代码上,首先设计一个抽象状态接口,该接口将各种状态下可能的操作进行聚合:

/**
 * 状态接口
 * 将各种状态下可能的操作聚合成一个接口
 * 实现类根据各自状态可能的业务去重写其中的方法
 */
public interface State {

    /**
     * 扣除积分
     */
    void deduceMoney();

    /**
     * 抽奖
     * @return
     */
    boolean raffle();

    /**
     * 发放奖品
     */
    void dispensePrice();
}

  抽象接口具体的实现
  不能抽奖状态,在此状态下只能去做出扣减积分的操作,扣减完成积分后,状态会变更为可抽奖状态

/**
 * 不能抽奖的状态
 */
public class NoRaffleState implements State{

    RaffleActivity raffleActivity;

    public NoRaffleState(RaffleActivity raffleActivity) {
        this.raffleActivity = raffleActivity;
    }

    /**
     * 扣除积分
     */
    @Override
    public void deduceMoney() {
        System.out.println("扣除50积分,可以抽奖");
        raffleActivity.setState(raffleActivity.getCanRaffleState());
    }

    /**
     * 抽奖
     *
     * @return
     */
    @Override
    public boolean raffle() {
        System.out.println("扣除积分方能抽奖");
        return false;
    }

    /**
     * 发放奖品
     */
    @Override
    public void dispensePrice() {
        System.out.println("不能发放奖品");
    }
}

  可抽奖状态,只能进行抽奖操作:

  • 未抽中奖品,重新回到不可抽奖状态
  • 抽中了奖品,流转到发送奖品状态
/**
 * 可以抽奖的状态
 */
public class CanRaffleState implements State{

    RaffleActivity raffleActivity;

    public CanRaffleState(RaffleActivity raffleActivity) {
        this.raffleActivity = raffleActivity;
    }


    /**
     * 扣除积分
     */
    @Override
    public void deduceMoney() {
        System.out.println("已经扣除积分,可以抽奖");
    }

    /**
     * 抽奖
     *
     * @return
     */
    @Override
    public boolean raffle() {
        System.out.println("正在抽奖,请稍等");
        int num = new Random().nextInt(10);
        if (num == 0){
            //中奖了,发放奖品
            raffleActivity.setState(raffleActivity.getDispenseState());
            return true;
        }else {
            System.out.println("很遗憾,您没有中奖");
            //回到不能抽奖的状态
            raffleActivity.setState(raffleActivity.getNoRaffleState());
            return false;
        }
    }

    /**
     * 发放奖品
     */
    @Override
    public void dispensePrice() {
        System.out.println("没中奖,不能发放奖品");
    }
}

  发放奖品状态,只能进行发放奖品的操作:

  • 奖品数量充足,流转到奖品发送完成状态。
  • 奖品数量不足,改变状态为不能抽奖
/**
 * 发放奖品的状态
 */
public class DispenseState implements State{

    RaffleActivity raffleActivity;

    public DispenseState(RaffleActivity raffleActivity) {
        this.raffleActivity = raffleActivity;
    }

    /**
     * 扣除积分
     */
    @Override
    public void deduceMoney() {
        System.out.println("不能扣除积分");
    }

    /**
     * 抽奖
     *
     * @return
     */
    @Override
    public boolean raffle() {
        System.out.println("不能抽奖");
        return false;
    }

    /**
     * 发放奖品
     */
    @Override
    public void dispensePrice() {
        if (raffleActivity.getCount()>0){
            System.out.println("恭喜中奖了");
            //改变状态为不能抽奖
            raffleActivity.setState(raffleActivity.getNoRaffleState());
        }else {
            System.out.println("很遗憾,奖品发送完了");
            //改变状态为奖品发送完成
            raffleActivity.setState(raffleActivity.getDispenseOutState());
        }

    }
}

  奖品发放完成,则不可以进行任何操作

/**
 * 奖品发放完成的状态
 */
public class DispenseOutState implements State{

    RaffleActivity raffleActivity;

    public DispenseOutState(RaffleActivity raffleActivity) {
        this.raffleActivity = raffleActivity;
    }

    /**
     * 扣除积分
     */
    @Override
    public void deduceMoney() {
        System.out.println("奖品发送完了,请下次再参加");
    }

    /**
     * 抽奖
     *
     * @return
     */
    @Override
    public boolean raffle() {
        System.out.println("奖品发送完了,请下次再参加");
        return false;
    }

    /**
     * 发放奖品
     */
    @Override
    public void dispensePrice() {
        System.out.println("奖品发送完了,请下次再参加");
    }
}

  创建一个上下文类,对状态进行统一管理,同时对状态进行初始化,并且上面的具体状态类,也聚合了上下文类的对象:

/**
 * 抽奖的行为
 */
public class RaffleActivity {

    /**
     * 初始的状态
     */
    State state = null;

    /**
     * 初始的数量
     */
    int count = 0;

    State canRaffleState = new CanRaffleState(this);
    State noRaffleState = new NoRaffleState(this);
    State dispenseOutState = new DispenseOutState(this);
    State dispenseState = new DispenseState(this);

    public RaffleActivity(int count) {
        //初始状态,不能抽奖
        this.state = getNoRaffleState();
        this.count = count;
    }

    /**
     * 扣除积分
     */
    public void deduceMoney(){
        state.deduceMoney();
    }

    /**
     * 抽奖
     */
    public void raffle(){
        if (state.raffle()){
            //领取奖品
            state.dispensePrice();
        }
    }


    public State getState() {
        return state;
    }

    public void setState(State state) {
        this.state = state;
    }

    public int getCount() {
        int curCount = this.count;
        count--;
        return curCount;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public State getCanRaffleState() {
        return canRaffleState;
    }

    public void setCanRaffleState(State canRaffleState) {
        this.canRaffleState = canRaffleState;
    }

    public State getNoRaffleState() {
        return noRaffleState;
    }

    public void setNoRaffleState(State noRaffleState) {
        this.noRaffleState = noRaffleState;
    }

    public State getDispenseOutState() {
        return dispenseOutState;
    }

    public void setDispenseOutState(State dispenseOutState) {
        this.dispenseOutState = dispenseOutState;
    }

    public State getDispenseState() {
        return dispenseState;
    }

    public void setDispenseState(State dispenseState) {
        this.dispenseState = dispenseState;
    }
}
public class Client {
    public static void main(String[] args) {
        RaffleActivity raffleActivity = new RaffleActivity(1);

        for (int i = 0; i < 3; i++) {
            //扣积分
            raffleActivity.deduceMoney();
            //抽奖
            raffleActivity.raffle();
        }
    }
}

小结
状态模式的优缺点
优点:

  • 遵循单一职责原则:将与状态相关的代码封装到独立类中。
  • 开闭原则:添加新状态非常方便,无需修改现有代码。
  • 消除大量条件语句:状态切换通过对象替换实现,而非复杂的条件判断。

缺点:

  • 类的数量增加:每个状态都需要创建一个类,可能导致类数量增多。
  • 状态之间的耦合:状态类之间可能需要频繁切换,会导致一定程度的耦合。

二、策略模式

  策略模式是一种行为设计模式,核心思想在于,定义了一组算法,将每个算法封装到独立的类中,使它们可以相互替换。
  策略模式通常包括以下角色:

  1. 上下文类:持有一个策略对象的引用,并且初始化具体策略。
  2. 抽象策略类:定义了所有具体策略类需要实现的接口。
  3. 具体策略类:实现抽象策略类,包含具体的算法或行为。

  假设目前有一个方法,需要根据参数中传入的不同的业务类型,执行不同业务的分派,如果使用传统方式,则会利用switch或者if-else实现,将来需要扩展的时候,需要对分支代码进行修改:

/**
 * 业务处理接口
 */
public interface BusinessClass {

    /**
     * 处理业务
     */
    void handleBusiness(int type);
}
public class BusinessClassImpl implements BusinessClass{


    /**
     * 处理业务
     * 根据传入的参数进行分发
     */
    @Override
    public void handleBusiness(int type) {

        switch (type){
            case 1:
                this.handleBusiness1();
                break;
            case 2:
                this.handleBusiness2();
                break;
            case 3:
                this.handleBusiness3();
                break;
            default:
                break;
        }
    }

    private void handleBusiness3() {
        System.out.println("业务3的具体逻辑");
    }

    private void handleBusiness2() {
        System.out.println("业务2的具体逻辑");
    }

    private void handleBusiness1() {
        System.out.println("业务1的具体逻辑");
    }
}

  使用策略模式改造,则可以抽取一个策略接口

/**
 * 业务策略接口
 */
public interface BusinessStrategy {

    /**
     * 处理具体的业务逻辑
     */
    void handleBusiness();
}

  将传统方式中,每个分支中的方法都抽取到一个单独的类中,实现策略接口

public class Business1 implements BusinessStrategy{
    /**
     * 处理具体的业务逻辑
     */
    @Override
    public void handleBusiness() {
        System.out.println("处理业务逻辑1");
    }
}
public class Business2 implements BusinessStrategy{
    /**
     * 处理具体的业务逻辑
     */
    @Override
    public void handleBusiness() {
        System.out.println("处理业务逻辑2");
    }
}
public class Business3 implements BusinessStrategy{
    /**
     * 处理具体的业务逻辑
     */
    @Override
    public void handleBusiness() {
        System.out.println("处理业务逻辑3");
    }
}

  在上下文类中,对其进行统一的管理,业务代码只需要注入上下文类,调用其中的方法即可。

public class Context {

    private Map<Integer, BusinessStrategy> strategyMap = new HashMap<>();

    /**
     * 初始化上下文,k业务类型 v 业务类型具体的逻辑
     */
    public Context(){
        strategyMap.put(1, new Business1());
        strategyMap.put(2, new Business2());
        strategyMap.put(3, new Business3());
    }

    /**
     * 处理业务
     * 根据传入的参数进行分发
     */
    public void handleBusiness(int type) {
        BusinessStrategy strategy = strategyMap.get(type);
        if (strategy != null) {
            strategy.handleBusiness();
        } else {
            System.out.println("无效的业务类型");
        }
    }
}

小结
策略模式的优缺点
优点:

  • 遵循开闭原则:添加新策略无需修改现有代码,只需扩展新策略类。
  • 消除条件语句:避免了复杂的 if-else 或 switch-case 语句。
  • 提高灵活性:客户端可以动态选择策略。

缺点:

  • 类数量增加:每种策略需要创建一个类,可能导致类的数量过多。
  • 客户端需要了解不同策略:客户端必须知道有哪些策略才能选择合适的策略。

三、责任链模式

  责任链模式是一种行为设计模式,核心目的在于,将请求的处理责任沿着一条链传递,直到有对象处理这个请求为止。责任链模式将请求的发送者和接收者解耦,且可以动态地调整链条上的处理顺序。
  责任链模式通常包含以下角色:

  1. 抽象处理者:定义了处理请求的接口,以及一个指向下一个处理者的引用。(即将自身作为属性存放在类中)
  2. 具体处理者:继承了抽象处理者,具体去实现各自处理请求的逻辑,以及无法处理时/处理完成时,应该调用的下一个处理者
  3. 客户端:创建并组装责任链,通常会将请求发送给责任链的首个处理者

  下面来看一个案例:假设某个学校,购买教材,器材,根据金额不同,需要审批的级别也不同。
  首先构建一个请求对象

public class Request {

    /**
     * 请求id
     */
    protected int id = 0;

    /**
     * 价格
     */
    protected int price = 0;

    public Request() {
    }

    public Request(int id, int price) {
        this.id = id;
        this.price = price;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }
}

  然后定义一个抽象处理者

/**
 * 抽象审批人
 */
public abstract class Approver {

    /**
     * 审批人名称
     */
    protected String name;

    /**
     * 持有自己的引用
     */
    protected Approver approver;

    public Approver() {
    }

    public Approver(String name) {
        this.name = name;
    }

    public void setApprover(Approver approver) {
        this.approver = approver;
    }

    /**
     * 子类具体去实现处理请求的逻辑
     * @param request
     */
    abstract void handleReq(Request request);

}

  定义具体处理人,继承抽象处理人,编写自己处理的判断逻辑:

public class TeachingDirector extends Approver{


    public TeachingDirector(String name){
        super(name);
    }

    /**
     * 子类具体去实现处理请求的逻辑
     *
     * @param request
     */
    @Override
    void handleReq(Request request) {
        if (request.getPrice()<= 5000){
            System.out.println("请求编号"+ request.id + "已经被" + name + "处理");
        }else {
            approver.handleReq(request);
        }
    }
}
public class Dean extends Approver{

    public Dean(String name){
        super(name);
    }

    /**
     * 子类具体去实现处理请求的逻辑
     *
     * @param request
     */
    @Override
    void handleReq(Request request) {
        if (request.getPrice()<= 10000){
            System.out.println("请求编号"+ request.id + "已经被" + name + "处理");
        }else {
            approver.handleReq(request);
        }
    }
}
public class Principal extends Approver{

    public Principal(String name){
        super(name);
    }

    /**
     * 子类具体去实现处理请求的逻辑
     *
     * @param request
     */
    @Override
    void handleReq(Request request) {
        if (request.getPrice()<= 20000){
            System.out.println("请求编号"+ request.id + "已经被" + name + "处理");
        }else {
            approver.handleReq(request);
        }
    }
}

  最后由客户端对其进行统一组装:

public class Client {
    public static void main(String[] args) {

        Request request = new Request(1, 8000);

        Dean dean = new Dean("院长");
        TeachingDirector teachingDirector = new TeachingDirector("教学主任");
        Principal principal = new Principal("校长");
        //设置下一个审批者
        teachingDirector.setApprover(dean);
        dean.setApprover(principal);
        principal.setApprover(teachingDirector);

        //8000 教学主任处理不了,交给下一级(院长)处理
        teachingDirector.handleReq(request);
    }
}

小结
责任链模式的优缺点

优点:

  • 降低耦合度:请求发送者与接收者解耦,无需关心由谁处理请求。
  • 动态灵活:可以在运行时动态调整责任链的结构。
  • 增强扩展性:新增处理者无需修改现有代码。

缺点:

  • 链条可能过长:请求可能需要经过多个处理者,性能可能受影响。
  • 调试困难:由于请求的处理流程不明确,可能导致调试复杂。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/945966.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙UI开发——使用WidthTheme实现局部深浅色

1、场景描述 在实际的应用开发中&#xff0c;我们可能需要在界面中局部应用深色或者浅色的界面样式&#xff0c;与全局的深色、亮色同时生效。场景例如&#xff1a;深/亮色预览。此时&#xff0c;我们可以使用WithTheme能力来达到我们的效果。 2、WithTheme WithTheme组件可…

20241231取消掉夸克浏览器为默认浏览器

20241231取消掉夸克浏览器为默认浏览器 2024/12/31 17:59 因为有些资源必须用夸克网盘下载&#xff01;^_ 地区特色问题。对于百度网盘&#xff0c;如果你分享BBC的纪录片合集&#xff0c;马上给你无效掉&#xff01;^_ 但是夸克有一点夜郎自大了&#xff0c;把客户的默认浏览器…

详细教程:SQL2008数据库备份与还原全流程!

数据的安全性至关重要&#xff0c;无论是操作系统、重要文件、磁盘存储&#xff0c;还是企业数据库&#xff0c;备份都是保障其安全和完整性的关键手段。拥有备份意味着即使发生误删、系统崩溃或病毒攻击等问题&#xff0c;也能迅速通过恢复功能解决&#xff0c;避免数据丢失带…

一、Hadoop概述

文章目录 一、Hadoop是什么二、Hadoop发展历史三、Hadoop三大发行版本1. Apache Hadoop2. Cloudera Hadoop3. Hortonworks Hadoop 四、Hadoop优势1. 高可靠性2. 高扩展性3. 高效性4. 高容错性 五、Hadoop 组成1. Hadoop1.x、2.x、3.x区别2. HDFS 架构概述3. YARN 架构概述4. Ma…

docker-开源nocodb,使用已有数据库

使用已有数据库 创建本地数据库 数据库&#xff1a;nocodb 用户&#xff1a;nocodb 密码&#xff1a;xxxxxx修改docker-compose.yml 默认网关的 IP 地址是 172.17.0.1&#xff08;适用于 bridge 网络模式&#xff09;version: "2.1" services:nocodb:environment:…

BetterBench的2024年终总结

回忆录 去年的年末定的2024目标是阅读300篇文献&#xff0c;发表一篇小论文&#xff0c;阅读20本的目标&#xff0c;都没有如期完成。只读了130篇论文&#xff0c;小论文还只写了初稿&#xff0c;还没有投出去&#xff0c;只读了6本书&#xff0c;上半年很浮躁&#xff0c;都没…

编辑音频的基本属性

导入音频 “文件-导入-选择音频”拖到音频轨道创建序列。选择音频&#xff0c;在效果空间可以看到音频的基本属性。 音量的设置 “效果工作区-效果控件-音量”在这里可以控制所有引导的混合音量 静音 静止所有声音 音频仪表 一般位于时间轴的后面&#xff0c;找不到可以…

SQL 基础教程 - SQL SELECT 语句

SQL SELECT DISTINCT 语句 SELECT DISTINCT 语句用于返回唯一不同的值。 在表中&#xff0c;一个列可能会包含多个重复值&#xff0c;有时您也许希望仅仅列出不同&#xff08;distinct&#xff09;的值。 DISTINCT 关键词用于返回唯一不同的值。 SQL SELECT DISTINCT 语法 …

Oracle 回归分析函数使用

Oracle 回归分析函数使用 文章目录 Oracle 回归分析函数使用什么是 回归分析函数回归分析函数示例1. 分析 SAL 和 COMM 之间的回归关系2. 按部门分析 SAL 和 COMM 的关系3. 根据 SAL 预测 COMM4. 分析员工薪资与工作年限的关5. 按部门分析工作年限与薪资的关系6. 计算 REGR_AVG…

idea项目导入gitee 码云

1、安装gitee插件 IDEA 码云插件已由 gitosc 更名为 gitee。 1 在码云平台帮助文档http://git.mydoc.io/?t153739上介绍的很清楚&#xff0c;推荐前两种方法&#xff0c; 搜索码云插件的时候记得名字是gitee&#xff0c;gitosc已经搜不到了。 2、使用码云托管项目 如果之…

Cesium 实战 27 - 三维视频融合(视频投影)

Cesium 实战 27 - 三维视频融合(视频投影) 核心代码完整代码在线示例在 Cesium 中有几种展示视频的方式,比如墙体使用视频材质,还有地面多边形使用视频材质,都可以实现视频功能。 但是随着摄像头和无人机的流行,需要视频和场景深度融合,简单的实现方式则不能满足需求。…

spring boot的各个版本介绍

Spring Boot 是一个用于创建独立、生产级别的基于 Spring 的应用程序的框架。自2014年首次发布以来&#xff0c;Spring Boot 经历了多个版本的迭代&#xff0c;每个版本都带来了新特性、性能改进和错误修复。下面是对Spring Boot一些主要版本的简要介绍&#xff1a; Spring Boo…

基于zynq在linux下的HDMI实战

ZYNQ系列文章目录 第一章&#xff1a;基于zynq在linux下的phy调试记录 第二章&#xff1a;qemu制作ubuntu文件系统 第三章&#xff1a;基于zynq在linux下的AXI-CAN实战 第四章&#xff1a;基于zynq在linux下的HDMI实战 文章目录 ZYNQ系列文章目录前言一、vivado中HDMI的配置1.…

LabVIEW 实现自动对焦的开发

自动对焦&#xff08;Autofocus, AF&#xff09;技术是通过分析图像或传感器信号&#xff0c;动态调整焦点位置以实现清晰成像或高精度定位的过程。在LabVIEW中&#xff0c;可以通过集成信号采集、数据处理、控制算法和硬件接口模块&#xff0c;实现多种自动对焦方法&#xff0…

机器人C++开源库The Robotics Library (RL)使用手册(四)

建立自己的机器人3D模型和运动学模型 这里以国产机器人天机TR8为例,使用最普遍的DH运动学模型,结合RL所需的描述文件,进行生成。 最终,需要的有两个文件,一个是.wrl三维模型描述文件;一个是.xml运动学模型描述文件。 1、通过STEP/STP三维文件生成wrl三维文件 机器人的…

直播电商系统源码搭建实战:快速开发多商户带货APP的指南

今天&#xff0c;笔者将从源码选择、功能设计、开发流程等方面&#xff0c;带你了解如何快速开发一个高效实用的直播电商系统。 一、明确需求&#xff1a;功能设计是基础 以下是一个多商户直播电商系统的核心功能模块&#xff1a; -商户管理模块 -直播带货模块 -商品管理模…

风力涡轮机缺陷检测数据集,86.6%准确识别率,11921张图片,支持yolo,PASICAL VOC XML,COCO JSON格式的标注

风力涡轮机缺陷检测数据集&#xff0c;86.6&#xff05;准确识别率&#xff0c;11921张图片&#xff0c;支持yolo&#xff0c;PASICAL VOC XML&#xff0c;COCO JSON格式的标注 数据集下载 yolov11&#xff1a; https://download.csdn.net/download/pbymw8iwm/90206849 yolov…

委外加工业务如何调整原材料的消耗-MIGO A11-后续调整

业务背景&#xff1a;用户反馈委外加工业务回收后&#xff0c;产品已经销售&#xff0c;但委外加工结算时要对原材料消耗时行调整。如果没有销售&#xff0c;准备采用收货冲销后重新收货&#xff0c;但现在已经是2024年最后一天了。。。销售业务已经做完。不可能再冲销。其实这…

ultralytics库RT-DETR代码解析

最近读了maskformer以及maskdino的分割头设计&#xff0c;于是想在RT-DETR上做一个分割的改动&#xff0c;所以选择在ultralytics库中对RTDETR进行改进。 本文内容简介&#xff1a; 1.ultralytics库中RT-DETR模型解析 2. 对ultralytics库中的RT-DETR模型增加分割头做实例分割 …

25. C++继承 1 (继承的概念与基础使用, 继承的复制兼容规则,继承的作用域)

⭐上篇模板文章&#xff1a;24. C模板 2 (非类型模板参数&#xff0c;模板的特化与模板的分离编译)-CSDN博客 ⭐本篇代码&#xff1a;c学习 橘子真甜/c-learning-of-yzc - 码云 - 开源中国 (gitee.com) ⭐标⭐是比较重要的部分 目录 一. 继承的基础使用 1.1 继承的格式 1.2 …