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

文章目录

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


前言

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


一、状态模式

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

  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;}/*** 扣除积分*/@Overridepublic void deduceMoney() {System.out.println("扣除50积分,可以抽奖");raffleActivity.setState(raffleActivity.getCanRaffleState());}/*** 抽奖** @return*/@Overridepublic boolean raffle() {System.out.println("扣除积分方能抽奖");return false;}/*** 发放奖品*/@Overridepublic void dispensePrice() {System.out.println("不能发放奖品");}
}

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

  • 未抽中奖品,重新回到不可抽奖状态
  • 抽中了奖品,流转到发送奖品状态
/*** 可以抽奖的状态*/
public class CanRaffleState implements State{RaffleActivity raffleActivity;public CanRaffleState(RaffleActivity raffleActivity) {this.raffleActivity = raffleActivity;}/*** 扣除积分*/@Overridepublic void deduceMoney() {System.out.println("已经扣除积分,可以抽奖");}/*** 抽奖** @return*/@Overridepublic 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;}}/*** 发放奖品*/@Overridepublic void dispensePrice() {System.out.println("没中奖,不能发放奖品");}
}

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

  • 奖品数量充足,流转到奖品发送完成状态。
  • 奖品数量不足,改变状态为不能抽奖
/*** 发放奖品的状态*/
public class DispenseState implements State{RaffleActivity raffleActivity;public DispenseState(RaffleActivity raffleActivity) {this.raffleActivity = raffleActivity;}/*** 扣除积分*/@Overridepublic void deduceMoney() {System.out.println("不能扣除积分");}/*** 抽奖** @return*/@Overridepublic boolean raffle() {System.out.println("不能抽奖");return false;}/*** 发放奖品*/@Overridepublic 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;}/*** 扣除积分*/@Overridepublic void deduceMoney() {System.out.println("奖品发送完了,请下次再参加");}/*** 抽奖** @return*/@Overridepublic boolean raffle() {System.out.println("奖品发送完了,请下次再参加");return false;}/*** 发放奖品*/@Overridepublic 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{/*** 处理业务* 根据传入的参数进行分发*/@Overridepublic 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{/*** 处理具体的业务逻辑*/@Overridepublic void handleBusiness() {System.out.println("处理业务逻辑1");}
}
public class Business2 implements BusinessStrategy{/*** 处理具体的业务逻辑*/@Overridepublic void handleBusiness() {System.out.println("处理业务逻辑2");}
}
public class Business3 implements BusinessStrategy{/*** 处理具体的业务逻辑*/@Overridepublic 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*/@Overridevoid 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*/@Overridevoid 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*/@Overridevoid 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);}
}

小结
责任链模式的优缺点

优点:

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

缺点:

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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【设计模式】 基本原则、设计模式分类

设计模式 设计模式是软件工程中的一种通用术语&#xff0c;指的是针对特定问题的经过实践验证的解决方案。设计模式并不是最终的代码实现&#xff0c;而是描述了如何解决某一类问题的思路和方法。 如果熟悉了设计模式&#xff0c;当遇到类似的场景&#xff0c;我们可以快速地…

二、github基础

Github基础 备用github.com网站一、用户界面-Overview&#xff08;概览&#xff09;1用户信息2 导航栏3 热门仓库4 贡献设置5贡献活动6搜索和筛选7自定义收藏8贡献统计9最近活动10其他链接 二、用户界面-Repositories&#xff08;仓库&#xff09;1 libusb_stm322 savedata3 Fi…

nature reviews genetics | 需要更多的针对不同种族的癌症基因组图谱研究,促进精准治疗和维护治疗公平权益

–https://doi.org/10.1038/s41576-024-00796-w Genomic landscape of cancer in racially and ethnically diverse populations 研究团队和单位 Ulrike Peters–Public Health Sciences Division, Fred Hutchinson Cancer Center Claire E. Thomas–Public Health Scienc…

选择器(结构伪类选择器,伪元素选择器),PxCook软件,盒子模型

结构为类选择器 伪元素选择器 PxCook 盒子模型 (内外边距&#xff0c;边框&#xff09; 内外边距合并&#xff0c;塌陷问题 元素溢出 圆角 阴影: 模糊半径&#xff1a;越大越模糊&#xff0c;也就是越柔和 案例一&#xff1a;产品卡片 <!DOCTYPE html> <html lang&q…

vue2+echarts实现水球+外层动效

实现效果 安装echarts-liquidfill 需要安装echarts-liquidfill&#xff01;&#xff01;&#xff01;需要安装echarts-liquidfill&#xff01;&#xff01;&#xff01;需要安装echarts-liquidfill&#xff01;&#xff01;&#xff01; 安装命令 npm install echarts-liqui…

OpenStack的核心组件、主要特点和使用场景

OpenStack 是一个开源的云计算平台&#xff0c;主要用于构建和管理公共及私有云环境。它由多个模块组成&#xff0c;提供虚拟化资源管理、存储管理、网络配置等功能&#xff0c;旨在为数据中心提供自动化的、灵活的云基础设施服务。OpenStack最初由NASA和Rackspace共同开发&…

Java 代码编译和解析方法信息

使用 javassist 可以操作字节码文件&#xff0c;我分享一下一个简单的编译和类方法解析代码。 什么是 Javassist&#xff1f; Javassist 是一个强大的字节码操作工具&#xff0c;它提供了在运行时编辑 Java 字节码的能力。通过Javassist&#xff0c;开发人员可以动态地创建和…

SpringCloud源码分析-Lettue Redis

redis connection异步发送 底层是nio channel

ELK入门教程(超详细)

什么是ELK&#xff1f; ELK是Elasticsearch、Logstash、Kibana三大开源框架首字母大写简称(后来出现的filebeat属于beats家族中的一员&#xff0c;可以用来替代logstash的数据收集功能&#xff0c;比较轻量级)&#xff0c;也被称为Elastic Stack。 Filebeat Filebeat是用于转…

Wireshark和科来网络分析系统

Wireshark 是一款功能强大的网络协议分析工具&#xff0c;主要用于捕获和分析网络流量&#xff0c;帮助用户排查网络问题、进行安全分析和学习网络协议。以下是 Wireshark 的基础使用指南&#xff1a; 1. 安装 Wireshark 访问 Wireshark 官网 下载并安装适合你操作系统的版本…

机器学习之逻辑回归算法、数据标准化处理及数据预测和数据的分类结果报告

逻辑回归算法、数据标准化处理及数据预测和数据的分类结果报告 目录 逻辑回归算法、数据标准化处理及数据预测和数据的分类结果报告1 逻辑回归算法1.1 概念理解1.2 算法导入1.3 算法优缺点 2 LogisticRegression理解2.1查看参数定义2.2 参数理解2.3 方法2.4基本格式 3 数据标准…

家政上门小程序如何创建?家政服务怎么能少了小程序帮手

在如今这个“忙到没时间打扫”的时代&#xff0c;家政服务变得越来越受欢迎。为了提高效率、减少沟通成本&#xff0c;很多家政公司都已经开始借助小程序的力量。那么&#xff0c;家政上门小程序到底该如何创建呢?小程序又是如何帮助家政服务更好地满足客户需求的呢?本文将为…

机器学习-感知机-神经网络-激活函数-正反向传播-梯度消失-dropout

文章目录 感知机工作流程 神经网络区别各种各样的神经网络 激活函数激活函数类型Sigmoid 函数ReLU函数Leaky ReLU 函数Tanh 函数 正向传播反向传播梯度消失(gradient vanish)如何解决 Dropout使用 PyTorch实战神经网络算法(手写MNIST数字识别)viewsoftmax和log-softmaxcross-en…

生态碳汇涡度相关监测与通量数据分析实践技术应用

1.以涡度通量塔的高频观测数据为例&#xff0c;基于MATLAB开展上机操作&#xff1a; 2.涡度通量观测基本概况&#xff1a;观测技术方法、数据获取与预处理等 3.涡度通量数据质量控制&#xff1a;通量数据异常值识别与剔除等 4.涡度通量数据缺失插补&#xff1a;结合气象数据…

Win11电脑Cursor默认打开markdown文件,如何修改markdown文件默认打开方式为Typora?

问题 Windows 11电脑上最近新装了cursor&#xff0c;导致我的markdown文件的默认打开方式被自动设置为cursor&#xff0c;那么我想将默认打开方式设置为Typora&#xff0c;应该怎么做呢&#xff1f; 解决方法 选中一个markdown文件&#xff0c;右击&#xff0c;选择属性。 …

基本算法——回归

目录 创建工程 加载数据 分析属性 创建与评估回归模型 线性回归 回归树 评估 完整代码 结论 本节将通过分析能源效率数据集&#xff08;Tsanas和Xifara&#xff0c;2012&#xff09;学习基本的回归算法。我们将基 于建筑的结构特点&#xff08;比如表面、墙体与屋顶面…

PP模块部分BAPI函数

工艺路线 BAPI_ROUTING_CREATE 创建工艺路线 BAPI_ROUTING_EXISTENCE_CHECK 检查工艺路线是否存在 参考操作集 BAPI_REFSETOFOPERATIONS_CREATE 创建参考操作集 BAPI_REFSETOFOPR_EXISTENCE_CHK 检查参考操作集是否存在 计划订单 BAPI_PLANNEDORDER_CREATE 创建计划订单 BAPI…

【SpringBoot】多数据源事务卡死@DSTransactional,当某一个数据库挂掉了,系统卡死问题解决

记录最近发生并解决的一个问题 原因 在一个事务内&#xff0c;操作多个数据库&#xff0c;当其中一个数据库挂掉后&#xff0c;默认无限重连&#xff0c;导致事务无法正常结束&#xff0c;导致系统卡死 解决 将无限重连改成有限次数即可 datasource:db1:driver-class-name…

迅为RK3568开发板编译Android12源码包-设置屏幕配置

在源码编译之前首先要确定自己想要使用的屏幕并修改源码&#xff0c;在编译镜像&#xff0c;烧写镜像。如下图所示&#xff1a; 第一步&#xff1a;确定要使用的屏幕种类&#xff0c;屏幕种类选择如下所示&#xff1a; iTOP-3568 开发板支持以下种类屏幕&#xff1a; 迅为 LV…

重装操作系统后 Oracle 11g 数据库数据还原

场景描述&#xff1a; 由于SSD系统盘损坏&#xff0c;更换硬盘后重装了操作系统&#xff0c;Oracle数据库之前安装在D盘(另一个硬盘)&#xff0c;更换硬盘多添加一个盘符重装系统后盘符从D变成E&#xff0c;也就是之前的D:/app/... 变成了现在的 E:/app/...&#xff0c;重新安装…