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

责任链模式让我的代码精简10倍?

目录

  • 什么是责任链
  • 使用场景
  • 结语
0e353d417fb38e23f8467b6b783a9d69.jpeg

前言

最近,我让团队内一位成员写了一个导入功能。他使用了责任链模式,代码堆的非常多,bug 也多,没有达到我预期的效果。实际上,针对导入功能,我认为模版方法更合适!为此,隔壁团队也拿出我们的案例,进行了集体 code review。学好设计模式,且不要为了练习,强行使用!让原本 100 行就能实现的功能,写了 3000 行!对错暂且不论,我们先一起看看责任链设计模式吧!


什么是责任链


责任链模式是一种行为设计模式, 允许你将请求沿着处理者链进行发送。收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。 a1cb8426c536de4a160ec318824d0f48.jpeg


使用场景


责任链的使用场景还是比较多的:
  • 多条件流程判断:权限控制
  • ERP 系统流程审批:总经理、人事经理、项目经理
  • Java 过滤器的底层实现 Filter
如果不使用该设计模式,那么当需求有所改变时,就会使得代码臃肿或者难以维护,例如下面的例子。

反例

假设现在有一个闯关游戏,进入下一关的条件是上一关的分数要高于 xx:
  • 游戏一共 3 个关卡
  • 进入第二关需要第一关的游戏得分大于等于 80
  • 进入第三关需要第二关的游戏得分大于等于 90
那么代码可以这样写: //第一关
public  class FirstPassHandler {
     public int handler(){
        System.out.println( "第一关-->FirstPassHandler");
         return  80;
    }
}

//第二关
public  class SecondPassHandler {
     public int handler(){
        System.out.println( "第二关-->SecondPassHandler");
         return  90;
    }
}


//第三关
public  class ThirdPassHandler {
     public int handler(){
        System.out.println( "第三关-->ThirdPassHandler,这是最后一关啦");
         return  95;
    }
}


//客户端
public  class HandlerClient {
     public static void main(String[] args) {

        FirstPassHandler firstPassHandler =  new FirstPassHandler(); //第一关
        SecondPassHandler secondPassHandler =  new SecondPassHandler(); //第二关
        ThirdPassHandler thirdPassHandler =  new ThirdPassHandler(); //第三关

         int firstScore = firstPassHandler.handler();
         //第一关的分数大于等于80则进入第二关
         if(firstScore >=  80){
             int secondScore = secondPassHandler.handler();
             //第二关的分数大于等于90则进入第二关
             if(secondScore >=  90){
                thirdPassHandler.handler();
            }
        }
    }
}
那么如果这个游戏有 100 关,我们的代码很可能就会写成这个样子: if(第 1关通过){
     // 第2关 游戏
     if(第 2关通过){
         // 第3关 游戏
         if(第 3关通过){
            // 第4关 游戏
             if(第 4关通过){
                 // 第5关 游戏
                 if(第 5关通过){
                     // 第6关 游戏
                     if(第 6关通过){
                         //...
                    }
                }
            } 
        }
    }
}
这种代码不仅冗余,并且当我们要将某两关进行调整时会对代码非常大的改动,这种操作的风险是很高的,因此,该写法非常糟糕。

初步改造

如何解决这个问题,我们可以通过链表将每一关连接起来,形成责任链的方式,第一关通过后是第二关,第二关通过后是第三关....这样客户端就不需要进行多重 if 的判断了: public  class FirstPassHandler {
     /**
     * 第一关的下一关是 第二关
     */

     private SecondPassHandler secondPassHandler;

     public void setSecondPassHandler(SecondPassHandler secondPassHandler) {
         this.secondPassHandler = secondPassHandler;
    }

     //本关卡游戏得分
     private int play(){
         return  80;
    }

     public int handler(){
        System.out.println( "第一关-->FirstPassHandler");
         if(play() >=  80){
             //分数>=80 并且存在下一关才进入下一关
             if( this.secondPassHandler !=  null){
                 return  this.secondPassHandler.handler();
            }
        }

         return  80;
    }
}

public  class SecondPassHandler {

     /**
     * 第二关的下一关是 第三关
     */

     private ThirdPassHandler thirdPassHandler;

     public void setThirdPassHandler(ThirdPassHandler thirdPassHandler) {
         this.thirdPassHandler = thirdPassHandler;
    }

     //本关卡游戏得分
     private int play(){
         return  90;
    }

     public int handler(){
        System.out.println( "第二关-->SecondPassHandler");

         if(play() >=  90){
             //分数>=90 并且存在下一关才进入下一关
             if( this.thirdPassHandler !=  null){
                 return  this.thirdPassHandler.handler();
            }
        }

         return  90;
    }
}

public  class ThirdPassHandler {

     //本关卡游戏得分
     private int play(){
         return  95;
    }

     /**
     * 这是最后一关,因此没有下一关
     */

     public int handler(){
        System.out.println( "第三关-->ThirdPassHandler,这是最后一关啦");
         return play();
    }
}

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

        FirstPassHandler firstPassHandler =  new FirstPassHandler(); //第一关
        SecondPassHandler secondPassHandler =  new SecondPassHandler(); //第二关
        ThirdPassHandler thirdPassHandler =  new ThirdPassHandler(); //第三关

        firstPassHandler.setSecondPassHandler(secondPassHandler); //第一关的下一关是第二关
        secondPassHandler.setThirdPassHandler(thirdPassHandler); //第二关的下一关是第三关

         //说明:因为第三关是最后一关,因此没有下一关
         //开始调用第一关 每一个关卡是否进入下一关卡 在每个关卡中判断
        firstPassHandler.handler();

    }
}

缺点

现有模式的缺点:
  • 每个关卡中都有下一关的成员变量并且是不一样的,形成链很不方便
  • 代码的扩展性非常不好

责任链改造

既然每个关卡中都有下一关的成员变量并且是不一样的,那么我们可以在关卡上抽象出一个父类或者接口,然后每个具体的关卡去继承或者实现。有了思路,我们先来简单介绍一下责任链设计模式的基本组成:
  • 抽象处理者(Handler)角色: 定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
  • 具体处理者(Concrete Handler)角色: 实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
  • 客户类(Client)角色: 创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
bfb1d7f534bdcf6be32041dc624f4989.jpeg
public  abstract  class AbstractHandler {

     /**
     * 下一关用当前抽象类来接收
     */

     protected AbstractHandler next;

     public void setNext(AbstractHandler next) {
         this.next = next;
    }

     public abstract int handler();
}

public  class FirstPassHandler extends AbstractHandler{

     private int play(){
         return  80;
    }

     @Override
     public int handler(){
        System.out.println( "第一关-->FirstPassHandler");
         int score = play();
         if(score >=  80){
             //分数>=80 并且存在下一关才进入下一关
             if( this.next !=  null){
                 return  this.next.handler();
            }
        }
         return score;
    }
}

public  class SecondPassHandler extends AbstractHandler{

     private int play(){
         return  90;
    }

     public int handler(){
        System.out.println( "第二关-->SecondPassHandler");

         int score = play();
         if(score >=  90){
             //分数>=90 并且存在下一关才进入下一关
             if( this.next !=  null){
                 return  this.next.handler();
            }
        }

         return score;
    }
}

public  class ThirdPassHandler extends AbstractHandler{

     private int play(){
         return  95;
    }

     public int handler(){
        System.out.println( "第三关-->ThirdPassHandler");
         int score = play();
         if(score >=  95){
             //分数>=95 并且存在下一关才进入下一关
             if( this.next !=  null){
                 return  this.next.handler();
            }
        }
         return score;
    }
}

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

        FirstPassHandler firstPassHandler =  new FirstPassHandler(); //第一关
        SecondPassHandler secondPassHandler =  new SecondPassHandler(); //第二关
        ThirdPassHandler thirdPassHandler =  new ThirdPassHandler(); //第三关

         // 和上面没有更改的客户端代码相比,只有这里的set方法发生变化,其他都是一样的
        firstPassHandler.setNext(secondPassHandler); //第一关的下一关是第二关
        secondPassHandler.setNext(thirdPassHandler); //第二关的下一关是第三关

         //说明:因为第三关是最后一关,因此没有下一关

         //从第一个关卡开始
        firstPassHandler.handler();

    }
}

责任链工厂改造

对于上面的请求链,我们也可以把这个关系维护到配置文件中或者一个枚举中。我将使用枚举来教会大家怎么动态的配置请求链并且将每个请求者形成一条调用链。 fd5ecb4668bd2dab2ca35a282876c041.jpeg
public  enum GatewayEnum {
     // handlerId, 拦截者名称,全限定类名,preHandlerId,nextHandlerId
    API_HANDLER( new GatewayEntity( 1,  "api接口限流",  "cn.dgut.design.chain_of_responsibility.GateWay.impl.ApiLimitGatewayHandler",  null,  2)),
    BLACKLIST_HANDLER( new GatewayEntity( 2,  "黑名单拦截",  "cn.dgut.design.chain_of_responsibility.GateWay.impl.BlacklistGatewayHandler",  1,  3)),
    SESSION_HANDLER( new GatewayEntity( 3,  "用户会话拦截",  "cn.dgut.design.chain_of_responsibility.GateWay.impl.SessionGatewayHandler",  2,  null)),
    ;

    GatewayEntity gatewayEntity;

     public GatewayEntity getGatewayEntity() {
         return gatewayEntity;
    }

    GatewayEnum(GatewayEntity gatewayEntity) {
         this.gatewayEntity = gatewayEntity;
    }
}

public  class GatewayEntity {

     private String name;

     private String conference;

     private Integer handlerId;

     private Integer preHandlerId;

     private Integer nextHandlerId;
}


public  interface GatewayDao {

     /**
     * 根据 handlerId 获取配置项
     * @param handlerId
     * @return
     */

     GatewayEntity getGatewayEntity(Integer handlerId);

     /**
     * 获取第一个处理者
     * @return
     */

     GatewayEntity getFirstGatewayEntity();
}

public  class GatewayImpl implements GatewayDao {

     /**
     * 初始化,将枚举中配置的handler初始化到map中,方便获取
     */

&nbsp;&nbsp;&nbsp;&nbsp; private&nbsp; static&nbsp;Map<Integer,&nbsp;GatewayEntity>&nbsp;gatewayEntityMap&nbsp;=&nbsp; new&nbsp;HashMap<>();

&nbsp;&nbsp;&nbsp;&nbsp; static&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GatewayEnum[]&nbsp;values&nbsp;=&nbsp;GatewayEnum.values();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for&nbsp;(GatewayEnum&nbsp;value&nbsp;:&nbsp;values)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GatewayEntity&nbsp;gatewayEntity&nbsp;=&nbsp;value.getGatewayEntity();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gatewayEntityMap.put(gatewayEntity.getHandlerId(),&nbsp;gatewayEntity);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp; @Override
&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;GatewayEntity&nbsp;getGatewayEntity(Integer&nbsp;handlerId)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;gatewayEntityMap.get(handlerId);
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp; @Override
&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;GatewayEntity&nbsp;getFirstGatewayEntity()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for&nbsp;(Map.Entry<Integer,&nbsp;GatewayEntity>&nbsp;entry&nbsp;:&nbsp;gatewayEntityMap.entrySet())&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GatewayEntity&nbsp;value&nbsp;=&nbsp;entry.getValue();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp;没有上一个handler的就是第一个
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if&nbsp;(value.getPreHandlerId()&nbsp;==&nbsp; null)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;value;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp; null;
&nbsp;&nbsp;&nbsp;&nbsp;}
}

public&nbsp; class&nbsp;GatewayHandlerEnumFactory&nbsp;{

&nbsp;&nbsp;&nbsp;&nbsp; private&nbsp; static&nbsp;GatewayDao&nbsp;gatewayDao&nbsp;=&nbsp; new&nbsp;GatewayImpl();

&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;提供静态方法,获取第一个handler
&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;static&nbsp;GatewayHandler&nbsp;getFirstGatewayHandler()&nbsp;{

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GatewayEntity&nbsp;firstGatewayEntity&nbsp;=&nbsp;gatewayDao.getFirstGatewayEntity();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GatewayHandler&nbsp;firstGatewayHandler&nbsp;=&nbsp;newGatewayHandler(firstGatewayEntity);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if&nbsp;(firstGatewayHandler&nbsp;==&nbsp; null)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp; null;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GatewayEntity&nbsp;tempGatewayEntity&nbsp;=&nbsp;firstGatewayEntity;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Integer&nbsp;nextHandlerId&nbsp;=&nbsp; null;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GatewayHandler&nbsp;tempGatewayHandler&nbsp;=&nbsp;firstGatewayHandler;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;迭代遍历所有handler,以及将它们链接起来
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while&nbsp;((nextHandlerId&nbsp;=&nbsp;tempGatewayEntity.getNextHandlerId())&nbsp;!=&nbsp; null)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GatewayEntity&nbsp;gatewayEntity&nbsp;=&nbsp;gatewayDao.getGatewayEntity(nextHandlerId);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GatewayHandler&nbsp;gatewayHandler&nbsp;=&nbsp;newGatewayHandler(gatewayEntity);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tempGatewayHandler.setNext(gatewayHandler);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tempGatewayHandler&nbsp;=&nbsp;gatewayHandler;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tempGatewayEntity&nbsp;=&nbsp;gatewayEntity;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;返回第一个handler
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;firstGatewayHandler;
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp; /**
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;反射实体化具体的处理者
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;firstGatewayEntity
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@return
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/

&nbsp;&nbsp;&nbsp;&nbsp; private&nbsp;static&nbsp;GatewayHandler&nbsp;newGatewayHandler(GatewayEntity&nbsp;firstGatewayEntity)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;获取全限定类名
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;className&nbsp;=&nbsp;firstGatewayEntity.getConference();&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;根据全限定类名,加载并初始化该类,即会初始化该类的静态段
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class<?>&nbsp;clazz&nbsp;=&nbsp;Class.forName(className);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;(GatewayHandler)&nbsp;clazz.newInstance();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp; catch&nbsp;(ClassNotFoundException&nbsp;|&nbsp;IllegalAccessException&nbsp;|&nbsp;InstantiationException&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp; null;
&nbsp;&nbsp;&nbsp;&nbsp;}


}

public&nbsp; class&nbsp;GetewayClient&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetewayHandler&nbsp;firstGetewayHandler&nbsp;=&nbsp;GetewayHandlerEnumFactory.getFirstGetewayHandler();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;firstGetewayHandler.service();
&nbsp;&nbsp;&nbsp;&nbsp;}
}


结语


设计模式有很多,责任链只是其中的一种,我觉得很有意思,非常值得一学。设计模式确实是一门艺术,仍需努力呀!

相关文章:

责任链模式让我的代码精简10倍?

目录 什么是责任链使用场景结语 前言最近&#xff0c;我让团队内一位成员写了一个导入功能。他使用了责任链模式&#xff0c;代码堆的非常多&#xff0c;bug 也多&#xff0c;没有达到我预期的效果。实际上&#xff0c;针对导入功能&#xff0c;我认为模版方法更合适&#xff…...

Draw软件安装下载

Draw软件安装下载 1.软件简介2.软件下载3.安装方法 1.软件简介 Draw软件&#xff0c;全名为LibreOffice Draw&#xff0c;是一款免费、开源的2D矢量绘图软件&#xff0c;属于LibreOffice办公套件的一部分。它可以用来创建各种类型的图形&#xff0c;包括流程图、组织结构图、平…...

uniapp代码混淆ios上架43问题

参考文章&#xff1a;uniapp打包ios apk&#xff0c;混淆代码_uniapp 混淆_酸奶自由竟然重名了的博客-CSDN博客 uniapp打包ios&#xff0c;上传到ios应用市场时&#xff0c;会因为 4.3(代码重复率过高) 无法通过审核&#xff0c;此时可通过混淆代码来通过审核 1. 项目终端 安…...

Linux目录遍历函数

1.打开一个目录 #include <sys/types.h> #include <dirent.h> DIR *opendir(const char *name); 参数&#xff1a; -name:需要打开的目录的名称 返回值&#xff1a; DIR * 类型&#xff0c;理解为目录流 错误返回NULL 2.读取目录中的数据 #include <dirent.h…...

数据库-理论基础

目录 1.什么是数据库&#xff1f; 2.数据库与文件系统的区别&#xff1f; 3.常见的数据库由那些&#xff1f; 4.关系型数据库(MySQL&#xff09;的特征及组成结构介绍 1.什么是数据库&#xff1f; 数据&#xff1a;描述事物的符号记录&#xff0c;可以是数字&#xff0c;文…...

【已解决】src/spt_python.h:14:20: 致命错误:Python.h:没有那个文件或目录

src/spt_python.h:14:20: 致命错误&#xff1a;Python.h&#xff1a;没有那个文件或目录 问题 其中重点的报错信息 src/spt_python.h:14:20: fatal error: Python.h: No such file or directory 思路 sudo yum install python-devel然后重新安装需要的依赖。 解决 成功。…...

基于Face++网络爬虫+人脸融合算法智能发型推荐程序——深度学习算法应用(含Python及打包exe工程源码)+爬虫数据集

目录 前言总体设计系统整体结构图系统流程图 运行环境Python环境Pycharm 环境 模块实现1. Face.APl调用1&#xff09;Face.APl介绍2&#xff09;调用API 2. 数据爬取1&#xff09;网络数据爬取步骤2&#xff09;爬虫实现 3. 模型构建4. 用户界面设计1&#xff09;需要调用的库文…...

Jetson nano嵌入式平台配置ip记录

背景 Jetson nano平台使用千兆网和PC连接时没有ip地址&#xff0c;在ubuntu的终端输入ifconfig显示eh0未设置ip&#xff0c;需要先在nano平台上配置ip地址&#xff0c;然后PC通过千兆网远程控制该平台。 配置ip 使用终端进入到network文件夹中&#xff0c; cd /etc/network…...

前端中的跨域请求及其解决方案

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 跨域&#xff08;Cross-Origin&#xff09;⭐CORS&#xff08;跨域资源共享&#xff09;⭐JSONP&#xff08;JSON with Padding&#xff09;⭐代理服务器⭐ WebSocket⭐服务器设置响应头⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a…...

SpringBoot2.0(mybatis-plus初始使用)

目录 一&#xff0c;介绍二&#xff0c;SpringBoot2.x整合MybatisPlus Lombok2.1&#xff0c;添加依赖 pom2.2&#xff0c;配置数据库信息 application.properties2.3&#xff0c;工程结构初始化 三&#xff0c;创建接口返回统一对象四&#xff0c;创建bean五&#xff0c;创建…...

游戏视频录制软件对比,哪款最适合你的需求?

随着电子竞技和游戏直播行业的迅速崛起&#xff0c;越来越多的玩家渴望记录并分享自己在游戏中的精彩瞬间。游戏视频录制软件正是满足这一需求的关键工具。本文将针对三款优秀的游戏视频录制软件进行对比分析&#xff0c;以便为读者提供选购建议。 游戏视频录制软件1&#xff1…...

耐蚀合金连续油管最新版 学习记录

声明 本文是学习GB-T 42858-2023 耐蚀合金连续油管. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本文件规定了耐蚀合金连续油管的订货、材料、制造、检验试验、标记等。 本文件适用于油气井用耐蚀合金连续油管(以下简称"油管")…...

LoGoNet:基于局部到全局跨模态融合的精确 3D 目标检测

论文地址&#xff1a;https://arxiv.org/abs/2303.03595 论文代码&#xff1a;https://github.com/sankin97/LoGoNet 论文背景 激光雷达传感器点云通常是稀疏的&#xff0c;无法提供足够的上下文来区分远处的区域&#xff0c;从而造成性能次优。 激光雷达-摄像机融合方法在三…...

Python 多线程、线程池、进程池

线程间的通讯机制 消息队列 event 事件对象 当线程创建完成之后&#xff0c;并不会马上执行线程&#xff0c;而是等待某一事件发生&#xff0c;线程才会启动 import threading# # 创建 event 对象 # event threading.Event() # # 重置代码中的 event 对象&#xff0c;使得所…...

深入浅出了解华为端到端交付流程的概念和5个关键点

如果您或您所在的组织在学习和研究华为&#xff0c;那么对“端到端”这个词语就一点都不陌生。 今天华研荟带着您了解华为端到端的交付流程的思想和一些做法&#xff0c;如果了解了这个&#xff0c;那么对于华为在其他领域提出的端到端要求或做法就一通百通了。 一、什么是端…...

[Linux]进程信号

[Linux]进程信号 文章目录 [Linux]进程信号进程信号的定义信号的特点信号的生命过程发送信号的原理进程处理信号的方式分类使用指令查看Linux系统定义的信号信号产生使用终端按键产生信号使用指令向进程发送信号调用系统调用向进程发送信号由软件条件产生信号硬件异常产生信号 …...

PostgreSQL 数据类型

文章目录 PostgreSQL数据类型说明PostgreSQL数据类型使用单引号和双引号数据类型转换布尔类型数值类型整型浮点型序列数值的常见操作 字符串类型日期类型枚举类型IP类型JSON&JSONB类型复合类型数组类型 PostgreSQL数据类型说明 PGSQL支持的类型特别丰富&#xff0c;大多数…...

智慧港口4G+UWB+GPS/北斗RTK人员定位系统解决方案

港口人员定位系统能够帮助企业实现对港口作业人员的全面监控和管理&#xff0c;不仅可以保障人员的人身安全&#xff0c;还可以提高人员的作业效率&#xff0c;为港口的可持续发展提供有力保障。接下来为大家分享智慧港口人员定位系统解决方案。 方案背景 1、港口作业人员多&a…...

实时时钟和日历电路芯片MS85163/MS85163M

MS85163/MS85163M 是一款 CMOS 实时时钟 (RTC) 和 日历电路&#xff0c;针对低功耗进行了优化&#xff0c;内置了可编程的时钟输出、中断输出和低电压检测器。所有寄存器地址和数据都通过两线双向I 2 C 总线进行串行传输&#xff0c;最大总线传输速度为 400kbit/s 。采用SOP8…...

【Java从入门到精通】这也许就是Java火热的原因吧!

前言&#xff1a;Java是一种高级的、面向对象的、可跨平台的程序设计语言。Java根据技术类别可划分为以下几类&#xff1a;JavaSE&#xff08;Standard Edition&#xff0c;标准版&#xff09;&#xff1a;支持面向桌面、嵌入式和移动设备的应用程序开发&#xff1b;JavaEE&…...

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

椭圆曲线密码学(ECC)

一、ECC算法概述 椭圆曲线密码学&#xff08;Elliptic Curve Cryptography&#xff09;是基于椭圆曲线数学理论的公钥密码系统&#xff0c;由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA&#xff0c;ECC在相同安全强度下密钥更短&#xff08;256位ECC ≈ 3072位RSA…...

工业安全零事故的智能守护者:一体化AI智能安防平台

前言&#xff1a; 通过AI视觉技术&#xff0c;为船厂提供全面的安全监控解决方案&#xff0c;涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面&#xff0c;能够实现对应负责人反馈机制&#xff0c;并最终实现数据的统计报表。提升船厂…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战

在现代战争中&#xff0c;电磁频谱已成为继陆、海、空、天之后的 “第五维战场”&#xff0c;雷达作为电磁频谱领域的关键装备&#xff0c;其干扰与抗干扰能力的较量&#xff0c;直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器&#xff0c;凭借数字射…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。

1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj&#xff0c;再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2

每日一言 今天的每一份坚持&#xff0c;都是在为未来积攒底气。 案例&#xff1a;OLED显示一个A 这边观察到一个点&#xff0c;怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 &#xff1a; 如果代码里信号切换太快&#xff08;比如 SDA 刚变&#xff0c;SCL 立刻变&#…...

Java毕业设计:WML信息查询与后端信息发布系统开发

JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发&#xff0c;实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构&#xff0c;服务器端使用Java Servlet处理请求&#xff0c;数据库采用MySQL存储信息&#xff0…...