当前位置: 首页 > 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&…...

zTasker—简洁易用强大的定时热键一体自动化工具,效率倍增器

软件名称 zTasker 应用平台 PC Windows7及以上 一句简介 市面上定时类软件很多&#xff0c;但无一例外功能都很单一&#xff0c;要完成不同的任务&#xff0c;需要不同的软件 市面上的热键软件&#xff0c;要么功能少&#xff0c;要么像是AutoHotKey这样对于一般用户太专业…...

惊艳时装界!AIGC风暴来袭,从设计到生产的全新体验

时尚是一个不断演进的领域&#xff0c;充满创新和独创性&#xff0c;但现在&#xff0c;创新迈入了一个崭新的境界——人工智能生成内容&#xff08;AIGC&#xff09;。这个革命性的技术&#xff0c;改变了时装设计的游戏规则。在过去的几年里&#xff0c;人工智能已经深刻地改…...

element -ui table表格内容无限滚动 使用插件vue-seamless-scroll

使用插件 一、安装组件依赖 npm install vue-seamless-scroll 二、引入组件 import vueSeamlessScroll from "vue-seamless-scroll"; components: { vueSeamlessScroll }, <div class"table-list "><vue-seamless-scroll :class-option"…...

如何在windows环境下编译T

一&#xff0c; 安装MYSYS2 1. 去https://www.msys2.org下载 msys2-x86_64-xxxxx.exe; 2. 按照msys2.org主页提示的步骤安装; 3.安装完默认起来的是 UCRT的&#xff0c; 可以根据环境的需要选择&#xff0c; 我选择的 MSYS2 MINGW64 4. 搭建编译环境&#xff0c; 安装对应的软…...

USB接口针式打印机

1 针式打印机原理 - 针式打印机16针是纵向排列&#xff0c;每次打印垂直的16bit&#xff0c;然后右移一bit&#xff0c;继续下列打印&#xff1b;字节的MSB表示最上面的点&#xff0c;字节LSB表示最下面的点 - 由于汉字字模的点阵是横向排列的&#xff0c;而提供给打印头的信息…...

外贸建站教程步骤有哪些?独立站怎么搭建?

推荐的外贸建站教程&#xff1f;制作国际贸易网站的流程&#xff1f; 对于那些希望将产品或服务推向全球市场的企业来说&#xff0c;建立一个专业、具有吸引力的网站是至关重要的。下面115SHOP将介绍外贸建站教程的关键步骤&#xff0c;帮助您更好地了解如何在国际市场上建立您…...

useGetState自定义hooks解决useState 异步回调获取不到最新值

setState 的两种传参方式 1、直接传入新值 setState(options); const [state, setState] useState(0); setState(state 1); 2、传入回调函数 setState(callBack); const [state, setState] useState(0); setState((prevState) > prevState 1); // prevState 是改变之…...

input子系统框架、外设驱动开发

一、input子系统基本框架 Linux内核为了两个目的&#xff1a; 简化纯输入类外设&#xff08;如&#xff1a;键盘、鼠标、游戏杆、轨迹球、触摸屏。。。等等&#xff09;的驱动开发统一输入类外设产生的数据格式&#xff08;struct input_event&#xff09;&#xff0c;更加方…...

Google Chrome 浏览器以全屏模式打开

目录 前言以全屏模式打开禁止弹出无法更新的提示窗禁止翻译网页Chrome设置禁止翻译网页可能1可能2可能3 网页添加指令禁止Chrome翻译网页 禁用脚本气泡浏览器解决办法html解决办法方法1&#xff1a;鼠标滑过超链接时&#xff0c;使状态栏不出现超链接方法2&#xff1a;方法3&am…...

安装torch113、cuda116并运行demo【Transformer】

文章目录 01. 导读02. 显卡驱动版本03. 创建环境、下载安装必要包04. 运行参考代码&#xff1a; 01. 导读 安装torch113、cuda116并运行demo【Transformer】 02. 显卡驱动版本 C:\Users\Administrator>nvidia-smi -l 10 Wed Sep 13 23:35:08 2023 ----------------------…...

餐饮加盟培训网站建设/怎样制作网站

在图像处理中&#xff0c;通常会用到一些经典的卷积滤波器&#xff0c;如低通滤波器、高斯滤波器、锐化滤波器、边缘检测、浮雕滤波器等。这些滤波器会产生不同的效果。下面用opencv实现这些出来看看效果&#xff1a; 原图低通滤波器高斯滤波器锐化滤波器边缘检测**浮雕滤波器*…...

wp做的网站打开域名会跳转到其他网站/新公司做网站多少钱

近日&#xff0c;一篇名为《罗振宇的骗局&#xff01;大部分的知识付费其实都是大忽悠》的文章在朋友圈疯转&#xff0c;这件事被牵扯出来的不仅是罗胖本人以及“得到”&#xff0c;更是整个尚处在疯狂成长阶段的知识付费行业。 虽然罗振宇对此事做出的回应是&#xff1a;“那…...

微信小程序如何生成二维码/化工seo顾问

对于创新&#xff0c;有很多偏见和误解。德鲁克在《创新与企业家精神》中指出&#xff0c;最常见的误解是认为大企业无法创新。大企业成了官僚作风和保守主义的代名词&#xff0c;事实上官僚和保守在小企业中同样普遍&#xff0c;只是因为企业太小&#xff0c;没有引起太多关注…...

社区门户网站建设招标公告/整合营销策划名词解释

要求&#xff1a; R4为ISP&#xff0c;其上只能配置IP地址&#xff1b;R4与其他所有直连设备间使用公有IP&#xff1b;R3–R5/R6/R7为MGRE环境&#xff0c;R3为中心站点&#xff1b;整个OSPF环境IP地址为172.16.0.0/16&#xff1b;所有设备均可以访问R4的环回&#xff1b;减少…...

网站建设价格gxjzdrj/安徽网站关键字优化

循环的理解&#xff1a; 这里介绍的是浏览器的执行机制&#xff0c;在node或ringo等执行机制会不同。运行机制大多指js解析引擎&#xff0c;是统一的。 主程序执行完后&#xff0c;就一直在等待任务队列的任务&#xff0c;settimeout等异步任务会直接拿出来执行&#xff0c;on…...

网站推广合作/广州网络科技有限公司

最近用虚拟机安装了centos7&#xff0c;但发现用yum install nginx 命令来安装nginx提示找不到相关的包&#xff0c;最后到nginx官网上找到了解决办法 步骤&#xff1a; cd /etc/yum.repos.d/然后建立 nginx.repo 文件 vi nginx.repo输入以下内容&#xff1a; [nginx] nameng…...