当前位置: 首页 > news >正文

JAVA设计模式之职责链模式详解

职责链模式

1 职责链模式介绍

职责链模式(chain of responsibility pattern) 定义: 避免将一个请求的发送者与接收者耦合在一起,让多个对象都有机会处理请求.将接收请求的对象连接成一条链,并且沿着这条链传递请求,直到有一个对象能够处理它为止.

在职责链模式中,多个处理器(也就是刚刚定义中说的“接收对象”)依次处理同一个请 求。一个请求先经过 A 处理器处理,然后再把请求传递给 B 处理器,B 处理器处理完后再 传递给 C 处理器,以此类推,形成一个链条。链条上的每个处理器各自承担各自的处理职 责,所以叫作职责链模式。

在这里插入图片描述

2 职责链模式原理

职责链模式结构

在这里插入图片描述

职责链模式主要包含以下角色:

  • 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接(链上的每个处理者都有一个成员变量来保存对于下一处理者的引用,比如上图中的successor) 。
  • 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
  • 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

3 职责链模式实现

责任链模式的实现非常简单,每一个具体的处理类都会保存在它之后的下一个处理类。当处理完成后,就会调用设置好的下一个处理类,直到最后一个处理类不再设置下一个处理类,这时处理链条全部完成。

public class RequestData {private String data;public RequestData(String data) {this.data = data;}public String getData() {return data;}public void setData(String data) {this.data = data;}
}/*** 抽象处理者类**/
public abstract class Handler {protected Handler successor = null;public void setSuccessor(Handler successor){this.successor = successor;}public abstract void handle(RequestData requestData);
}public class HandlerA extends Handler {@Overridepublic void handle(RequestData requestData) {System.out.println("HandlerA 执行代码逻辑! 处理: " + requestData.getData());requestData.setData(requestData.getData().replace("A",""));if(successor != null){successor.handle(requestData);}else{System.out.println("执行中止!");}}
}public class HandlerB extends Handler {@Overridepublic void handle(RequestData requestData) {System.out.println("HandlerB 执行代码逻辑! 处理: " + requestData.getData());requestData.setData(requestData.getData().replace("B",""));if(successor != null){successor.handle(requestData);}else{System.out.println("执行中止!");}}
}public class HandlerC extends Handler {@Overridepublic void handle(RequestData requestData) {System.out.println("HandlerC 执行代码逻辑! 处理: " + requestData.getData());requestData.setData(requestData.getData());if(successor != null){successor.handle(requestData);}else{System.out.println("执行中止!");}}
}public class Client {public static void main(String[] args) {Handler h1 = new HandlerA();Handler h2 = new HandlerB();Handler h3 = new HandlerC();h1.setSuccessor(h2);h2.setSuccessor(h3);RequestData requestData = new RequestData("请求数据ABCDE");h1.handle(requestData);}}

4 职责链模式应用实例

接下来我们模拟有一个双11期间,业务系统审批的流程,临近双十一公司会有陆续有一些新的需求上线,为了保证线上系统的稳定,我们对上线的审批流畅做了严格的控制.审批的过程会有不同级别的负责人加入进行审批(平常系统上线只需三级负责人审批即可,双十一前后需要二级或一级审核人参与审批),接下来我们就使用职责链模式来设计一下此功能.

在这里插入图片描述

  1. 不使用设计模式
/*** 审核信息**/
public class AuthInfo {private String code;private String info ="";public AuthInfo(String code, String... infos) {this.code = code;for (String str : infos) {info = this.info.concat(str +" ");}}public String getCode() {return code;}public void setCode(String code) {this.code = code;}public String getInfo() {return info;}public void setInfo(String info) {this.info = info;}@Overridepublic String toString() {return "AuthInfo{" +"code='" + code + '\'' +", info='" + info + '\'' +'}';}
}/*** 模拟审核服务**/
public class AuthService {//审批信息 审批人Id+申请单Idprivate static Map<String,Date> authMap = new HashMap<String, Date>();/*** 审核流程* @param uId    审核人id* @param orderId  审核单id*/public static void auth(String uId, String orderId){System.out.println("进入审批流程,审批人ID: " + uId);authMap.put(uId.concat(orderId),new Date());}//查询审核结果public static Date queryAuthInfo(String uId, String orderId){return authMap.get(uId.concat(orderId)); //key=审核人id+审核单子id}
}public class AuthController {//审核接口public AuthInfo doAuth(String name, String orderId, Date authDate) throws ParseException {//三级审批Date date = null;//查询是否存在审核信息,查询条件: 审核人ID+订单ID,返回Map集合中的Datedate = AuthService.queryAuthInfo("1000013", orderId);//如果为空,封装AuthInfo信息(待审核)返回if(date == null){return new AuthInfo("0001","单号: "+orderId,"状态: 等待三级审批负责人进行审批");}//二级审批SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 时间格式化//二级审核人主要审核双十一之前, 11-01 ~ 11-10号的请求,所以要对传入的审核时间进行判断//审核时间 大于 2022-11-01 并且  小于 2022-11-10,Date1.after(Date2),当Date1大于Date2时,返回TRUE,Date1.before(Date2),当Date1小于Date2时,返回TRUEif(authDate.after(f.parse("2022-11-01 00:00:00")) && authDate.before(f.parse("2022-11-10 00:00:00"))){//条件成立,查询二级审核的审核信息date = AuthService.queryAuthInfo("1000012",orderId);//如果为空,还是待二级审核人审核状态if(date == null){return new AuthInfo("0001","单号: "+orderId,"状态: 等待二级审批负责人进行审批");}}//一级审批//审核范围是在11-11日 ~ 11-31日if(authDate.after(f.parse("2022-11-11 00:00:00")) && authDate.before(f.parse("2022-11-31 00:00:00"))){date = AuthService.queryAuthInfo("1000011",orderId);if(date == null){return new AuthInfo("0001","单号: "+orderId,"状态: 等待一级审批负责人进行审批");}}return new AuthInfo("0001","单号: "+orderId,"申请人:"+ name +", 状态: 审批完成!");}
}public class Client {public static void main(String[] args) throws ParseException {AuthController controller = new AuthController();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date date = sdf.parse("2022-11-12 00:00:00");//设置申请流程//三级审核//1.调用doAuth方法,模拟发送申请人相关信息AuthInfo info1 = controller.doAuth("研发小周", "100001000010000", date);System.out.println("当前审核状态:  " + info1.getInfo());/*** 2.模拟进行审核操作, 虚拟审核人ID: 1000013* 调用auth() 方法进行审核操作, 就是向Map中添加一个 审核人ID和申请单ID*/AuthService.auth("1000013", "100001000010000");System.out.println("三级负责人审批完成,审批人: 王工");System.out.println("===========================================================================");//二级审核//1.调用doAuth方法,模拟发送申请人相关信息AuthInfo info2 = controller.doAuth("研发小周", "100001000010000", date);System.out.println("当前审核状态:  " + info2.getInfo());/*** 2.模拟进行审核操作, 虚拟审核人ID: 1000012* 调用auth() 方法进行审核操作, 就是向Map中添加一个 审核人ID和申请单ID*/AuthService.auth("1000012", "100001000010000");System.out.println("二级负责人审批完成,审批人: 张经理");System.out.println("===========================================================================");//一级审核//1.调用doAuth方法,模拟发送申请人相关信息AuthInfo info3 = controller.doAuth("研发小周", "100001000010000", date);System.out.println("当前审核状态:  " + info3.getInfo());/*** 2.模拟进行审核操作, 虚拟审核人ID: 1000012* 调用auth() 方法进行审核操作, 就是向Map中添加一个 审核人ID和申请单ID*/AuthService.auth("1000011", "100001000010000");System.out.println("一级负责人审批完成,审批人: 罗总");}
}
  1. 职责链模式重构代码

下图是为当前业务设计的责任链结构,统一抽象类AuthLink 下 有三个子类,将三个子类的执行通过编排,模拟出一条链路,这个链路就是业务中的责任链.

在这里插入图片描述

/*** 抽象审核链类*/
public abstract class AuthLink {protected Logger logger = LoggerFactory.getLogger(AuthLink.class);protected SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");protected String levelUserId;      //审核人IDprotected String levelUserName;   //审核人姓名protected AuthLink next;          //持有下一个处理类的引用public AuthLink(String levelUserId, String levelUserName) {this.levelUserId = levelUserId;this.levelUserName = levelUserName;}//获取下一个处理类public AuthLink getNext() {return next;}//责任链中添加处理类public AuthLink appendNext(AuthLink next) {this.next = next;return this;}//抽象审核方法public abstract AuthInfo doAuth(String uId, String orderId, Date authDate);
}/** 一级负责人*/
public class Level1AuthLink extends AuthLink {private Date beginDate = f.parse("2020-11-11 00:00:00");private Date endDate = f.parse("2020-11-31 23:59:59");public Level1AuthLink(String levelUserId, String levelUserName) throws ParseException {super(levelUserId, levelUserName);}@Overridepublic AuthInfo doAuth(String uId, String orderId, Date authDate) {Date date = AuthService.queryAuthInfo(levelUserId, orderId);if (null == date) {return new AuthInfo("0001", "单号:", orderId, " 状态:待一级审批负责人 ", levelUserName);}AuthLink next = super.getNext();if (null == next) {return new AuthInfo("0000", "单号:", orderId, " 状态:一级审批完成", " 时间:", f.format(date), " 审批人:", levelUserName);}if (authDate.before(beginDate) || authDate.after(endDate)) {return new AuthInfo("0000", "单号:", orderId, " 状态:一级审批完成", " 时间:", f.format(date), " 审批人:", levelUserName);}return next.doAuth(uId, orderId, authDate);}
}/*** 二级负责人*/
public class Level2AuthLink extends AuthLink {private Date beginDate = f.parse("2020-11-11 00:00:00");private Date endDate = f.parse("2020-11-31 23:59:59");public Level2AuthLink(String levelUserId, String levelUserName) throws ParseException {super(levelUserId, levelUserName);}public AuthInfo doAuth(String uId, String orderId, Date authDate) {Date date = AuthService.queryAuthInfo(levelUserId, orderId);if (null == date) {return new AuthInfo("0001", "单号:", orderId, " 状态:待二级审批负责人 ", levelUserName);}AuthLink next = super.getNext();if (null == next) {return new AuthInfo("0000", "单号:", orderId, " 状态:二级审批完成", " 时间:", f.format(date), " 审批人:", levelUserName);}if (authDate.before(beginDate) || authDate.after(endDate) ) {return new AuthInfo("0000", "单号:", orderId, " 状态:二级审批完成", " 时间:", f.format(date), " 审批人:", levelUserName);}return next.doAuth(uId, orderId, authDate);}}/*** 三级负责人*/
public class Level3AuthLink extends AuthLink {public Level3AuthLink(String levelUserId, String levelUserName) {super(levelUserId, levelUserName);}public AuthInfo doAuth(String uId, String orderId, Date authDate) {Date date = AuthService.queryAuthInfo(levelUserId, orderId);if (null == date) {return new AuthInfo("0001", "单号:", orderId, " 状态:待三级审批负责人 ", levelUserName);}AuthLink next = super.getNext();if (null == next) {return new AuthInfo("0000", "单号:", orderId, " 状态:三级审批完成", " 时间:", f.format(date), " 审批人:", levelUserName);}return next.doAuth(uId, orderId, authDate);}}

测试

public class Client {private Logger logger = LoggerFactory.getLogger(ApiTest.class);@Testpublic void test_AuthLink() throws ParseException {AuthLink authLink = new Level3AuthLink("1000013", "王工").appendNext(new Level2AuthLink("1000012", "张经理").appendNext(new Level1AuthLink("1000011", "段总")));SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date currentDate = f.parse("2020-11-18 23:49:46");logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("研发牛马", "1000998004813441", currentDate)));// 模拟三级负责人审批AuthService.auth("1000013", "1000998004813441");logger.info("测试结果:{}", "模拟三级负责人审批,王工");logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("研发牛马", "1000998004813441", currentDate)));// 模拟二级负责人审批AuthService.auth("1000012", "1000998004813441");logger.info("测试结果:{}", "模拟二级负责人审批,张经理");logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("研发牛马", "1000998004813441", currentDate)));// 模拟一级负责人审批AuthService.auth("1000011", "1000998004813441");logger.info("测试结果:{}", "模拟一级负责人审批,段总");logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("研发牛马", "1000998004813441", currentDate)));}
}

从上面的代码结果看,我们的责任链已经生效,按照责任链的结构一层一层审批.当工作流程发生变化,可以动态地改变链内的成员或者修改它们的次序,也可动态地新增或者删除责任。并且每个类只需要处理自己该处理的工作,不能处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

5 职责链模式总结

  1. 职责链模式的优点:
  • 降低了对象之间的耦合度

    该模式降低了请求发送者和接收者的耦合度。

  • 增强了系统的可扩展性

    可以根据需要增加新的请求处理类,满足开闭原则。

  • 增强了给对象指派职责的灵活性

    当工作流程发生变化,可以动态地改变链内的成员或者修改它们的次序,也可动态地新增或者删除责任。

  • 责任链简化了对象之间的连接

    一个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。

  • 责任分担

    每个类只需要处理自己该处理的工作,不能处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

  1. 职责链模式的缺点:
  • 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
  • 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
  • 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
  1. 使用场景分析

责任链模式常见的使用场景有以下几种情况。

  • 在运行时需要动态使用多个关联对象来处理同一次请求时。比如,请假流程、员工入职流程、编译打包发布上线流程等。
  • 不想让使用者知道具体的处理逻辑时。比如,做权限校验的登录拦截器。
  • 需要动态更换处理对象时。比如,工单处理系统、网关 API 过滤规则系统等。
  • 职责链模式常被用在框架开发中,用来实现框架的过滤器、拦截器功能,让框架的使用者在不修改源码的情况下,添加新的过滤拦截功能.

相关文章:

JAVA设计模式之职责链模式详解

职责链模式 1 职责链模式介绍 职责链模式(chain of responsibility pattern) 定义: 避免将一个请求的发送者与接收者耦合在一起,让多个对象都有机会处理请求.将接收请求的对象连接成一条链,并且沿着这条链传递请求,直到有一个对象能够处理它为止. 在职责链模式中&#xff0c…...

CSP-201912-1-报数

CSP-201912-1-报数 知识点总结 整数转化为字符串#include <string> string str_num to_string(num);字符串中查找是否包含字符‘7’&#xff1a;str_num.find(7) 未找到返回-1找到返回返回该字符在字符串中的位置&#xff08;即第一次出现的索引位置&#xff09; #i…...

前后端分离好处多多,怕就怕分工不分人,哈哈

前后端分离倡导多年了&#xff0c;现在基本成为了开发的主流模式了&#xff0c;贝格前端工场承接的前端项目只要不考虑seo的&#xff0c;都采用前后端分离模式&#xff0c;这篇文章就来介绍一下前后端分离模式。 一、什么是前后端分离开发模式 前后端分离是一种软件开发的架构…...

机器学习:Softmax介绍及代码实现

Softmax原理 Softmax函数用于将分类结果归一化&#xff0c;形成一个概率分布。作用类似于二分类中的Sigmoid函数。 对于一个k维向量z&#xff0c;我们想把这个结果转换为一个k个类别的概率分布p(z)。softmax可以用于实现上述结果&#xff0c;具体计算公式为&#xff1a; 对于…...

python基于flask的网上订餐系统769b9-django+vue

课题主要分为两大模块&#xff1a;即管理员模块和用户模块&#xff0c;主要功能包括个人中心、用户管理、菜品类型管理、菜品信息管理、留言反馈、在线交流、系统管理、订单管理等&#xff1b; 如果用户想要交换信息&#xff0c;他们需要满足双方交换信息的需要。由于时间有限…...

jenkins 发布远程服务器并部署项目

安装参考另一个文章 配置maven 和 jdk 和 git 注意jdk的安装目录&#xff0c;是jenkins 安装所在服务器的jdk目录 注意maven的目录 是jenkins 安装所在服务器的maven目录 注意git的目录 是jenkins 安装所在服务器的 git 目录 安装 Publish Over SSH 插件 配置远程服务器 创…...

【数学建模】【2024年】【第40届】【MCM/ICM】【D题 五大湖的水位控制问题】【解题思路】

一、题目 &#xff08;一&#xff09; 赛题原文 2024 ICM Problem D: Great Lakes Water Problem Background The Great Lakes of the United States and Canada are the largest group of freshwater lakes in the world. The five lakes and connecting waterways const…...

【开源】JAVA+Vue+SpringBoot实现公司货物订单管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 客户管理模块2.2 商品维护模块2.3 供应商管理模块2.4 订单管理模块 三、系统展示四、核心代码4.1 查询供应商信息4.2 新增商品信息4.3 查询客户信息4.4 新增订单信息4.5 添加跟进子订单 五、免责说明 一、摘要 1.1 项目…...

###C语言程序设计-----C语言学习(12)#进制间转换,十进制,二进制,八进制,十六进制

前言&#xff1a;感谢您的关注哦&#xff0c;我会持续更新编程相关知识&#xff0c;愿您在这里有所收获。如果有任何问题&#xff0c;欢迎沟通交流&#xff01;期待与您在学习编程的道路上共同进步。 计算机处理的所有信息都以二进制形式表示&#xff0c;即数据的存储和计算都采…...

锐捷设备常用命令

一、命令模式 命令行主要有用户模式、特权模式、全局模式、VLAN模式、接口模式、线程模式 switch> "用户模式"switch# "特权模式"switch(config) "全局模式"switch(conf…...

python:lxml 读目录.txt文件,用 xmltodict 转换为json数据,生成jstree所需的文件

请参阅&#xff1a;java : pdfbox 读取 PDF文件内书签 请注意&#xff1a;书的目录.txt 编码&#xff1a;UTF-8&#xff0c;推荐用 Notepad 转换编码。 pip install lxml ; lxml-5.1.0-cp310-cp310-win_amd64.whl (3.9 MB) pip install xmltodict ; lxml 读目录.txt文件&…...

【Spring】Spring 对 Ioc 的实现

一、Ioc 控制反转 控制反转是一种思想 控制反转是为了降低程序耦合度&#xff0c;提高程序扩展力&#xff0c;达到 OCP 原则&#xff0c;达到 DIP 原则 控制反转&#xff0c;反转的是什么&#xff1f; 将对象的创建权利交出去&#xff0c;交给第三方容器负责 将对象和对象之…...

QT学习文件操作类 QFile

&#xff08;一&#xff09;QFile QFile 是 Qt 框架中用于文件处理的一个类。它提供了读取和写入文件的功能&#xff0c;支持文本和二进制文件。QFile 继承自 QIODevice &#xff0c;因此它可以像其他 IO 设备一样使用。 &#xff08;1&#xff09;主要功能 1. 文件读写…...

VOL_常用记录!!

目录 前端1.js如何获取当前时间(yy-MM-dd HH:MM:SS)2.http请求3.grid扩展js常用 后端1.待补充 前端 1.js如何获取当前时间(yy-MM-dd HH:MM:SS) getCurrentTime() {const now new Date();return ${now.getFullYear()}-${(now.getMonth() 1).toString().padStart(2, "0&…...

解决Typora导出HTML不显示图片

解决Typora导出HTML不显示图片 产生原因 Typora导出HTML不显示图片&#xff0c;可能时图片存放在我们的硬盘中。 我们可以将markdown中的图片转化为base64格式&#xff0c;嵌入到html中。 解决步骤 首先&#xff0c;下载 TyporaToBase64.jar 密码:45jq 其次&#xff0c;将…...

React Native开发iOS实战录

文章目录 背景环境准备主要工具xcode安装安装CocoaPods 基本步骤采用Expo go运行iOS模拟器运行安装在真机上测试发布到苹果商店 常见问题ruby3在macOS上编译失败import of module ‘glog.glog.log_severity’ appears within namespace ‘google’yarn网络问题pod安装失败unabl…...

C++局部变量与全局变量

在C中&#xff0c;可以为函数的参数指定默认值。这样做的好处是在调用函数时&#xff0c;如果没有提供对应的参数&#xff0c;那么将会使用默认值。 下面是一个求2个或3个数中最大数的函数的示例&#xff0c;其中使用了默认参数&#xff1a; #include <iostream> using…...

深入理解ES的倒排索引

目录 数据写入过程 词项字典 term dictionary 倒排表 posting list FOR算法 RBM算法 ArrayContainer BitMapContainer 词项索引 term index 在Elasticsearch中&#xff0c;倒排索引的设计无疑是惊为天人的&#xff0c;下面看下倒排索引的结构。 倒排索引分为词项索引【…...

HTML世界之第一重天

一、HTML 元素 注&#xff1a;HTML 文档由 HTML 元素定义。 1.HTML 元素 开始标签 * 元素内容 结束标签 * <p> 这是一个段落 </p> <a href"default.htm"> 这是一个链接 </a> <br> 换行 开始标签常被称为起始标签&…...

docker run报 docker: Error response from daemon: no command specified.

docker run报 docker: Error response from daemon: no command specified. 1. export出mysql的container为tar, 拷贝到另一台虚拟机, import该tar为image, docker run该image时报 docker: Error response from daemon: no command specified. 时间240211 export出mysql的con…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

MongoDB学习和应用(高效的非关系型数据库)

一丶 MongoDB简介 对于社交类软件的功能&#xff0c;我们需要对它的功能特点进行分析&#xff1a; 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具&#xff1a; mysql&#xff1a;关系型数据库&am…...

基于服务器使用 apt 安装、配置 Nginx

&#x1f9fe; 一、查看可安装的 Nginx 版本 首先&#xff0c;你可以运行以下命令查看可用版本&#xff1a; apt-cache madison nginx-core输出示例&#xff1a; nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中&#xff0c;从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备&#xff08;如专用硬件设备&#xff09;&#xff0c;从而消除了直接物理连接的需要。USB over IP的…...

【Java学习笔记】BigInteger 和 BigDecimal 类

BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点&#xff1a;传参类型必须是类对象 一、BigInteger 1. 作用&#xff1a;适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

AI病理诊断七剑下天山,医疗未来触手可及

一、病理诊断困局&#xff1a;刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断"&#xff0c;医生需通过显微镜观察组织切片&#xff0c;在细胞迷宫中捕捉癌变信号。某省病理质控报告显示&#xff0c;基层医院误诊率达12%-15%&#xff0c;专家会诊…...

push [特殊字符] present

push &#x1f19a; present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中&#xff0c;push 和 present 是两种不同的视图控制器切换方式&#xff0c;它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

Caliper 负载(Workload)详细解析

Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...