解放代码:识别与消除循环依赖的实战指南

news2024/11/24 20:31:39

目录

一、对循环依赖的基本认识

(一)代码中形成循环依赖的说明

(二)无环依赖的原则

二、识别和消除循环依赖的方法

(一)使用JDepend识别循环依赖

使用 Maven 集成 JDepend

分析报告识别循环依赖

(二)消除循环依赖的三大方法思考

提取中介者

转移业务逻辑

采用回调接口

三、案件实战分析

(一)具体案列介绍

HealthRecord 类

HealthTask 类

循环依赖的产生

(二)具体解决方案一:提取中介者

提取中介者的实现

简化的 HealthTask 类

测试用例示例

(三)具体解决方案二:转移业务逻辑

转移业务逻辑的实现

改造后的 HealthRecord 类

改造后的 HealthTask 类

测试用例示例

(四)具体解决方案三:采用回调接口

使用回调接口的实现

HealthLevelHandler 接口的定义

改造后的 HealthTask 类

改造后的 HealthRecord 类

测试用例示例

参考书籍、文献和链接等


本文讨论软件开发中常见的循环依赖问题及其解决方法。首先介绍了循环依赖在代码中的形成原因,并提出了避免循环依赖的基本原则。其次,详细介绍了使用工具如JDepend来识别项目中的循环依赖,并通过具体案例分析了三种消除循环依赖的方法:提取中介者、转移业务逻辑和采用回调接口。每种方法都结合了实际的代码改造示例和测试用例,帮助读者理解和应用这些技术以优化自己的软件架构和设计。主要思想的编排思路来自极客时间《如何有效识别和解决代码中存在的循环依赖问题?》,当然也有其他的参考和自身的一些思考和优化。

一、对循环依赖的基本认识

循环依赖是指两个或多个模块或对象彼此之间相互依赖形成闭环,如 A 依赖 B,B 又依赖 A。这就像是两个人互相拉着对方,无法确定谁先开始走。

循环依赖最大的问题是,在运行时,可能会导致无限递归、栈溢出或者对象创建循环等问题,因为每个类的构造函数都试图创建依赖的对象,形成了一个死锁。所以一般情况下我们都希望在编译时,编译器就可能会报告循环依赖错误或无法解析的问题。

(一)代码中形成循环依赖的说明

循环依赖是指两个或多个模块、类、或组件之间相互依赖形成闭环。这种情况可能导致编译错误、运行时错误,甚至导致系统崩溃或无法正常工作。随着系统复杂性的增加,循环依赖问题可能会变得更加严重和难以检测。

简单来说,假设当前只有存在两个类A和类B,这个时候的识别还是较简单的,但如果此时在增加C,那么这个时候其可能产生依赖的情况就会很多了,比如如下的三种情况:

显然我们平时的系统中不会只是三个类这么简单,在多类情况下这种依赖问题就会不断放大(随着业务发展只会不断扩大),比如简单的依赖会变为如下依赖:

现实中,随着类和模块数量的增加,系统中的依赖关系会变得复杂,容易形成循环依赖,不仅增加了系统的复杂性和维护难度,还可能导致运行时错误和系统不稳定。因此,在设计和实现系统时,应尽量避免循环依赖,并采取合适的解决策略来管理依赖关系。

(二)无环依赖的原则

无环依赖原则(ADP)规定,在系统的依赖关系图中,不应该存在任何依赖环。换句话说,一个模块或组件的依赖链中不应出现环形依赖结构。这样,任何一个模块的变更不会导致循环依赖的问题,从而避免了系统的复杂性和难以维护性。

也就是基本原则要求如下:

  • 在软件设计中,模块之间的依赖关系可以用图来表示,节点表示模块,边表示依赖关系。无环依赖原则要求这个图必须是一个有向无环图(DAG)。
  • 通过确保依赖关系是无环的,可以防止系统中出现依赖的闭环,避免模块之间的强耦合。这样,修改一个模块不会引发对其依赖模块的连锁反应。

二、识别和消除循环依赖的方法

识别和消除循环依赖是软件开发中的一个重要任务,可以通过重构代码、引入中介者模式、使用接口和依赖注入,以及遵循设计原则等方法来实现。借助 Maven Enforcer Plugin、Madge、Deptrac、JDepend、PyDepGraph 和 SonarQube 等开源工具,可以更高效地检测和管理项目中的依赖关系,从而提高代码的可维护性和可扩展性。

(一)使用JDepend识别循环依赖

JDepend 是一个用于分析 Java 包之间依赖关系的工具。它能够生成报告,帮助开发者理解项目的架构和依赖关系,并识别可能的设计问题,如循环依赖。JDepend 提供了关于包的耦合度、内聚性、抽象度和稳定性等方面的信息,从而帮助改进代码质量和系统结构。可以从 JDepend 的官方网站 下载 JDepend,并将 JDepend 集成到构建工具中,例如 Maven 或 Gradle,以便在构建过程中自动分析依赖关系。

使用 Maven 集成 JDepend

在Maven 项目的 pom.xml 文件中添加 JDepend 插件:

<project>
  <!-- 其他配置 -->
  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>jdepend-maven-plugin</artifactId>
        <version>2.0-beta-2</version>
        <executions>
          <execution>
            <goals>
              <goal>generate</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

然后,运行以下命令来执行 JDepend 分析:

mvn jdepend:generate

分析报告识别循环依赖

JDepend 生成的报告包括以下几个方面:

  • 包的稳定性:度量包的变化频率,越不稳定的包越容易发生变化。
  • 包的抽象度:度量包的抽象程度,抽象包通常包含更多的接口和抽象类。
  • 包的耦合度:度量包之间的依赖关系。
  • 循环依赖:检测包之间是否存在循环依赖。

一旦生成 JDepend 报告就可以在报告中查找是否存在循环依赖,循环依赖通常会被标记为 “Cycles” 或 “Dependency Cycles”:

(二)消除循环依赖的三大方法思考

提取中介者

通过引入一个独立的中介者类来管理和协调其他类之间的交互,从而避免直接的循环依赖。

适用于需要集中管理复杂依赖关系的场景,可以有效地降低类之间的耦合度,增强系统的可维护性和灵活性。

转移业务逻辑

将涉及循环依赖的业务逻辑转移到一个独立的类或模块中,从而解除类之间的直接依赖关系。

适用于可以重构业务逻辑的场景,通过模块化设计提高系统的可理解性和可维护性。

采用回调接口

通过定义接口来抽象依赖关系,使类之间的依赖通过接口进行交互,从而避免直接的循环依赖。

适用于需要解耦具体实现的场景,可以增强系统的扩展性和灵活性。

三、案件实战分析

(一)具体案列介绍

在医疗健康类系统中,每个用户都有一份健康档案,记录着他们当前的健康状况(以健康等级表示)和一系列健康任务。用户每天可以通过完成医生指定的任务来获得健康积分。健康积分的计算取决于用户的当前等级,不同等级下完成同一个任务获得的积分不同。同时,用户的健康等级也取决于他们当前需要完成的任务数量,任务越多说明越不健康,等级也就越低。

针对这个场景,我们可以抽象出两个类:HealthRecordHealthTask

HealthRecord

HealthRecord 类代表健康档案,包含一个 HealthTask 列表以及添加 HealthTask 的方法。这个类还包含一个获取健康等级的方法,该方法根据任务数量来判断用户的健康等级。

import java.util.ArrayList;
import java.util.List;

public class HealthRecord {
    private List<HealthTask> tasks = new ArrayList<HealthTask>();

    public Integer getHealthLevel() {
        // 根据健康任务数量来判断健康等级
        // 任务越多说明越不健康,健康等级就越低
        if (tasks.size() > 5) {
            return 1;
        }
        if (tasks.size() < 2) {
            return 3;
        }
        return 2;
    }

    public void addTask(String taskName, Integer initialHealthPoint) {
        HealthTask task = new HealthTask(this, taskName, initialHealthPoint);
        tasks.add(task);
    }

    public List<HealthTask> getTasks() {
        return tasks;
    }
}

HealthTask

HealthTask 类代表健康任务,它包含对 HealthRecord 的引用,并实现了一个计算任务所能获得积分的方法,这个方法需要使用 HealthRecord 中的健康等级信息。

public class HealthTask {
    private HealthRecord record;
    private String taskName;
    private Integer initialHealthPoint;

    public HealthTask(HealthRecord record, String taskName, Integer initialHealthPoint) {
        this.record = record;
        this.taskName = taskName;
        this.initialHealthPoint = initialHealthPoint;
    }

    public Integer calculateHealthPointForTask() {
        // 计算该任务所能获取的积分需要等级信息
        // 等级越低积分越高,以鼓励多做任务
        Integer healthPointFromHealthLevel = 12 / record.getHealthLevel();
        // 最终积分为初始积分加上与等级相关的积分
        return initialHealthPoint + healthPointFromHealthLevel;
    }

    public String getTaskName() {
        return taskName;
    }

    public int getInitialHealthPoint() {
        return initialHealthPoint;
    }
}

循环依赖的产生

从代码中可以看出,HealthRecordHealthTask 之间存在循环依赖:

  • HealthRecord 需要知道 HealthTask 列表来确定健康等级。
  • HealthTask 需要引用 HealthRecord 来计算任务的健康积分。

这种循环依赖在系统扩展和维护时会带来很多问题,例如增加新的功能可能导致意想不到的耦合和复杂度。

(二)具体解决方案一:提取中介者

提取中介者的核心思想是把两个相互依赖的组件中的交互部分抽象出来形成一个新的组件,而新组件同时包含着原有两个组件的引用,这样就把循环依赖关系剥离出来并提取到一个专门的中介者组件中。

在医疗健康类系统中,我们面对 HealthRecordHealthTask 两个类之间的循环依赖问题:HealthRecord 类负责管理健康档案和健康任务列表,同时根据任务数量确定用户的健康等级。而 HealthTask 类则依赖于 HealthRecord 的健康等级信息来计算任务的健康积分,这导致了双向的依赖关系。

提取中介者的实现

为消除 HealthRecordHealthTask 的直接依赖,引入中介者类 HealthPointMediator。该中介者类负责协调 HealthRecordHealthTask 的交互,并提供了计算任务健康积分的方法。

public class HealthPointMediator {
    private HealthRecord record;

    public HealthPointMediator(HealthRecord record) {
        this.record = record;
    }

    public Integer calculateHealthPointForTask(HealthTask task) {
        Integer healthLevel = record.getHealthLevel();
        Integer initialHealthPoint = task.getInitialHealthPoint();
        Integer healthPoint = 12 / healthLevel + initialHealthPoint;
        return healthPoint;
    }
}

简化的 HealthTask

为了减少 HealthTaskHealthRecord 的依赖,可简化 HealthTask 类的实现。现在它专注于任务的描述和初始化积分,不再直接引用 HealthRecord

public class HealthTask {
    private String taskName;
    private Integer initialHealthPoint;

    public HealthTask(String taskName, Integer initialHealthPoint) {
        this.taskName = taskName;
        this.initialHealthPoint = initialHealthPoint;
    }

    public String getTaskName() {
        return taskName;
    }

    public Integer getInitialHealthPoint() {
        return initialHealthPoint;
    }
}

测试用例示例

编写了一个简单的测试用例来验证 HealthPointMediator 的功能,确保它能正确计算每个任务的健康积分。

public class HealthPointTest {
    public static void main(String[] args) {
        HealthRecord record = new HealthRecord();
        record.addTask("忌烟酒", 5);
        record.addTask("一周慢跑三次", 4);
        record.addTask("一天喝两升水", 2);
        record.addTask("坐1小时起来活动5分钟", 2);
        record.addTask("晚上10点按时睡觉", 3);
        record.addTask("晚上8点之后不再饮食", 1);

        HealthPointMediator mediator = new HealthPointMediator(record);
        List<HealthTask> tasks = record.getTasks();

        for (HealthTask task : tasks) {
            Integer healthPoint = mediator.calculateHealthPointForTask(task);
            System.out.println("任务:" + task.getTaskName() + ",积分:" + healthPoint);
        }
    }
}

通过提取中介者的方法,我们成功地消除了 HealthRecordHealthTask 之间的循环依赖关系。中介者模式使得系统更加灵活和可扩展,同时降低了类之间的耦合度,提高了代码的可维护性和可读性。这种设计方式不仅符合面向对象设计的原则,也使得系统更易于理解和维护。

(三)具体解决方案二:转移业务逻辑

这种方法的实现思路在于提取一个专门的业务组件来完成对等级的计算过程。这样,HealthTask原有的对HealthRecord的依赖就转移到了对这个业务组件的依赖,而这个业务组件本身不需要依赖任何对象。

转移业务逻辑的实现

提取一个专门的业务组件 HealthLevelHandler,负责计算健康等级而不依赖于任何其他对象。这样一来,HealthTask 类的原有对 HealthRecord 的依赖转移到了对 HealthLevelHandler 的依赖,从而消除了循环依赖。

public class HealthLevelHandler {
    private Integer taskCount;

    public HealthLevelHandler(Integer taskCount) {
        this.taskCount = taskCount;
    }

    public Integer getHealthLevel() {
        if (taskCount > 5) {
            return 1;
        } else if (taskCount < 2) {
            return 3;
        } else {
            return 2;
        }
    }
}

改造后的 HealthRecord

HealthRecord 类现在封装了对 HealthLevelHandler 的创建过程,并提供了获取健康任务列表的方法。

public class HealthRecord {
    private List<HealthTask> tasks = new ArrayList<>();

    public void addTask(String taskName, Integer initialHealthPoint) {
        HealthTask task = new HealthTask(taskName, initialHealthPoint);
        tasks.add(task);
    }

    public HealthLevelHandler getHealthPointHandler() {
        return new HealthLevelHandler(tasks.size());
    }

    public List<HealthTask> getTasks() {
        return tasks;
    }
}

改造后的 HealthTask

HealthTask 类改为接受 HealthLevelHandler 对象作为参数,并利用其计算任务的健康积分。

public class HealthTask {
    private String taskName;
    private Integer initialHealthPoint;

    public HealthTask(String taskName, Integer initialHealthPoint) {
        this.taskName = taskName;
        this.initialHealthPoint = initialHealthPoint;
    }

    public Integer calculateHealthPointForTask(HealthLevelHandler handler) {
        Integer healthPointFromHealthLevel = 12 / handler.getHealthLevel();
        return initialHealthPoint + healthPointFromHealthLevel;
    }

    public String getTaskName() {
        return taskName;
    }
}

测试用例示例

编写一个简单的测试用例来验证改造后的 HealthRecordHealthTask 的功能。现在,系统中不存在任何循环依赖,且代码更易于理解和维护。

public class HealthPointTest {
    public static void main(String[] args) {
        HealthRecord record = new HealthRecord();
        record.addTask("忌烟酒", 5);
        record.addTask("一周慢跑三次", 4);
        record.addTask("一天喝两升水", 2);
        record.addTask("坐1小时起来活动5分钟", 2);
        record.addTask("晚上10点按时睡觉", 3);
        record.addTask("晚上8点之后不再饮食", 1);

        HealthLevelHandler handler = record.getHealthPointHandler();
        List<HealthTask> tasks = record.getTasks();

        for (HealthTask task : tasks) {
            Integer healthPoint = task.calculateHealthPointForTask(handler);
            System.out.println("任务:" + task.getTaskName() + ",积分:" + healthPoint);
        }
    }
}

通过转移业务逻辑的方法,我们成功消除了 HealthRecordHealthTask 之间的循环依赖。通过引入 HealthLevelHandler 业务组件,我们将等级计算的责任集中在一个地方,简化了系统的设计并提高了其灵活性和可维护性。这种设计方式符合单一职责原则,使得各个类的功能更加清晰和独立。

(四)具体解决方案三:采用回调接口

所谓回调本质上就是一种双向调用模式,也就是说,被调用方在被调用的同时也会调用对方。在实现上,我们可以提取一个用于计算等级的业务接口,然后让HealthRecord去实现这个接口。这样,HealthTask在计算积分时只需要依赖这个业务接口,而不需要关心这个接口的具体实现类。

使用回调接口的实现

首先定义了一个名为 HealthLevelHandler 的接口,该接口包含了计算健康等级的方法声明,作为回调接口使用。接着,HealthRecord 类实现了这个接口,并提供了具体的等级计算逻辑。在创建 HealthTask 对象时,我们将 HealthRecord 对象作为参数传入 HealthTask 的构造函数,并在 HealthTask 中使用 HealthLevelHandler 接口来获取健康等级信息,从而消除了对 HealthRecord 的直接依赖。

HealthLevelHandler 接口的定义
public interface HealthLevelHandler {
    Integer getHealthLevel();
}
改造后的 HealthTask

HealthTask 类不再直接依赖 HealthRecord,而是依赖 HealthLevelHandler 接口,并通过该接口获取健康等级信息来计算任务的健康积分。

public class HealthTask {
    private String taskName;
    private Integer initialHealthPoint;
    private HealthLevelHandler handler;

    public HealthTask(String taskName, Integer initialHealthPoint, HealthLevelHandler handler) {
        this.taskName = taskName;
        this.initialHealthPoint = initialHealthPoint;
        this.handler = handler;
    }

    public Integer calculateHealthPointForTask() {
        Integer healthPointFromHealthLevel = 12 / handler.getHealthLevel();
        return initialHealthPoint + healthPointFromHealthLevel;
    }

    public String getTaskName() {
        return taskName;
    }
}
改造后的 HealthRecord

HealthRecord 类实现了 HealthLevelHandler 接口,并提供了具体的健康等级计算逻辑。在创建 HealthTask 对象时,将 this 作为 HealthLevelHandler 的实例传入 HealthTask 的构造函数。

public class HealthRecord implements HealthLevelHandler {
    private List<HealthTask> tasks = new ArrayList<>();

    @Override
    public Integer getHealthLevel() {
        if (tasks.size() > 5) {
            return 1;
        } else if (tasks.size() < 2) {
            return 3;
        } else {
            return 2;
        }
    }

    public void addTask(String taskName, Integer initialHealthPoint) {
        HealthTask task = new HealthTask(taskName, initialHealthPoint, this);
        tasks.add(task);
    }

    public List<HealthTask> getTasks() {
        return tasks;
    }
}

测试用例示例

编写一个简单的测试用例来验证改造后的 HealthRecordHealthTask 的功能。现在,系统中不存在任何循环依赖,且代码更易于理解和维护。

public class HealthRecord implements HealthLevelHandler {
    private List<HealthTask> tasks = new ArrayList<>();

    @Override
    public Integer getHealthLevel() {
        if (tasks.size() > 5) {
            return 1;
        } else if (tasks.size() < 2) {
            return 3;
        } else {
            return 2;
        }
    }

    public void addTask(String taskName, Integer initialHealthPoint) {
        HealthTask task = new HealthTask(taskName, initialHealthPoint, this);
        tasks.add(task);
    }

    public List<HealthTask> getTasks() {
        return tasks;
    }
}

通过引入 HealthLevelHandler 接口,HealthTask 类不再直接依赖于 HealthRecord,而是依赖于一个通用的接口,提高了系统的灵活性和可维护性。这种设计方式符合面向接口编程的思想,使得各个组件之间的耦合度降低,同时也使得代码更具扩展性和测试性。

参考书籍、文献和链接等

如何有效识别和解决代码中存在的循环依赖问题?-极客时间

JDepend 的官方网站

GitHub - clarkware/jdepend: A Java package dependency analyzer that generates design quality metrics.

JDepend Task

Managing Your Dependencies with JDepend – Craftsmanship

六、如何解决循环依赖_循环依赖解决方式-CSDN博客

https://jiapan.me/2020/circular-dependence/

https://blog.51cto.com/u_16038001/6159153

中介者模式解决Spring循环依赖_哔哩哔哩_bilibili

Spring Boot 系统学习第四天:Spring循环依赖案例分析_springboot 循环以来例子-CSDN博客

深入理解循环依赖:如何避免和解决

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++使用spdlog输出日志文件

参考博客&#xff1a; 日志记录库 spdlog 基础使用_spdlog 写日志-CSDN博客 GitHub - gabime/spdlog: Fast C logging library. 首先在github上下载spdlog源码&#xff0c;不想编译成库的话&#xff0c;可以直接使用源码&#xff0c;将include文件夹下的spdlog文件夹&#x…

CSS选择符和可继承属性

属性选择符&#xff1a; 示例&#xff1a;a[target"_blank"] { text-decoration: none; }&#xff08;选择所有target"_blank"的<a>元素&#xff09; /* 选择所有具有class属性的h1元素 */ h1[class] { color: silver; } /* 选择所有具有hre…

116.网络游戏逆向分析与漏洞攻防-邮件系统数据分析-解析结构数据的创建信息

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果&#xff0c;代码看不懂是正常的&#xff0c;只要会抄就行&#xff0c;抄着抄着就能懂了 内容…

智谱AI GLM-4V-9B视觉大模型环境搭建推理

引子 最近在关注多模态大模型&#xff0c;之前4月份的时候关注过CogVLM&#xff08;CogVLM/CogAgent环境搭建&推理测试-CSDN博客&#xff09;。模型整体表现还不错&#xff0c;不过不支持中文。智谱AI刚刚开源了GLM-4大模型&#xff0c;套餐里面包含了GLM-4V-9B大模型&…

springboot+vue+mybatis酒店房间管理系统+PPT+论文+讲解+售后

随着现在网络的快速发展&#xff0c;网络的应用在各行各业当中它很快融入到了许多商家的眼球之中&#xff0c;他们利用网络来做这个电商的服务&#xff0c;随之就产生了“酒店房间管理系统”&#xff0c;这样就让人们酒店房间管理系统更加方便简单。 对于本酒店房间管理系统的…

本地localhost与目标地址跨域问题的解决方法

场景 开发过程中遇到一个控件&#xff0c;上传图片到某cdn&#xff0c;目标地址对localhost会有跨域问题&#xff1a; 解决方法 参照此博客&#xff0c;将本地地址定义为某网址&#xff0c;如abc&#xff1a; win10修改本地host文件&#xff0c;用以增加自定义本地访问域名12…

不做题,可以通过PMP考试吗?

如果你想要避免浪费3900元并且不想再支付2500元的补考费&#xff0c;我建议你多做一些PMP考试的练习题&#xff1b;如果你不在意这些费用&#xff0c;也可以选择资助我&#xff0c;嘿嘿。不做题的话&#xff0c;通过PMP考试的几率是非常小的。因为做题是检验我们学习成果并发现…

躬行践履始玉成,行而不辍终致远 | 中创算力季度优秀员工表彰大会

蓬勃发展&#xff0c;根基在人。在中创发展的道路上&#xff0c;有初心不改的领导者、有披星戴月的业务标杆、也有默默坚守的员工&#xff0c;他们扎根中创&#xff0c;用努力、拼搏、坚持&#xff0c;在中创的历程上镌刻下 属于自己的一份印记&#xff01; 为了表彰优秀&…

电商商品项目||电商竞品分析|主流电商商品API接口在竞品分析中的重要应用

竞争数据采集 竞争数据是对在电子商务业务中彼此存在竞争关系的商家、品牌、产品(即竞争对手&#xff09;等各项运营数据的总称&#xff0c;在电子商务企业的经营过程中&#xff0c;对竞争对手进行分析可以帮助决策者和管理员了解竞争对手的发展势头&#xff0c;为企业成略制定…

多尺度特征提取:原理、应用与挑战

多尺度 多尺度特征提取&#xff1a;原理、应用与挑战**原理****应用****挑战****总结** 多尺度特征提取&#xff1a;原理、应用与挑战 在计算机视觉、自然语言处理和信号处理等领域&#xff0c;有效地捕捉和解析数据的多种尺度特性是至关重要的。多尺度特征提取是一种技术&…

24年下半年安徽教资认定准确时间和流程

安徽教资认定准确时间 网上报名时间&#xff1a; 第一批次&#xff1a;4月8日至4月19日17时 第二批次&#xff1a;6月17日至6月28日17时 注意&#xff1a;符合安徽省申请条件的普通大中专院校2024届全日制毕业生&#xff0c;应统一选择6月17日至6月28日17时的时间段进行网上报名…

VM4.3 二次开发02 方案加载、执行及显示

效果 这是二次开发的第二个文章&#xff0c;所以不重复说明环境配置相关的内容。如果不懂的可以看本专栏的上一个文章。 海康视觉算法平台VisionMaster 4.3.0 C# 二次开发01 加载方案并获取结果-CSDN博客 界面代码 <Window x:Class"VmTestWpf.App.MainWindow"x…

浏览器必装插件推荐:最新版Simple Allow Copy,解除网页复制限制!

经常在网上找资料的朋友&#xff0c;尤其是学生党&#xff0c;总会遇到一个问题&#xff1a;很多资料网站的文字是禁止复制的。于是大家通常会使用各种文字识别软件来图文转换&#xff0c;或者直接手打。 今天这款小工具&#xff0c;可以轻松复制各种氪金网站上的任何文字&…

爆肝整理AI Agent:在企业应用中的6种基础类型

AI Agent智能体在企业应用中落地的价值、场景、成熟度做了分析&#xff0c;并且探讨了未来企业IT基础设施与架构如何为未来Gen AI&#xff08;生成式AI&#xff09;做好准备。在这样的架构中&#xff0c;我们把最终体现上层应用能力的AI Agent从不同的技术要求与原理上分成了几…

考试系统Spring Security的配置

设置Spring Security配置类 1、设置包括认证、授权方法 protected void configure(HttpSecurity http) throws Exception {http.headers().frameOptions().disable();List<String> securityIgnoreUrls systemConfig.getSecurityIgnoreUrls();String[] ignores new Str…

重生奇迹mu圣导师介绍

出生地&#xff1a;勇者大陆 性 别&#xff1a;男 擅 长&#xff1a;统率&宠物使用 转 职&#xff1a;祭师&#xff08;3转&#xff09; 介 绍&#xff1a;当玩家账号中有一个Lv250以上角色时&#xff0c;便可以创建职业为圣导师的新角色&#xff0c;圣导师每升一级获得…

Trying to access array offset on value of type null

主要原因是版本7.4以后PHP解析器会对null类型的下标访问直接报错 背景&#xff1a; laravel框架 同时使用了扩展A和扩展B 扩展A要求 php>7.4,同时扩展B的对null类型的下标访问不兼容php7.4 修改扩展B不太现实&#xff0c;毕竟扩展B中有太多的对null类型的下标访问。 解决…

6月17日(周一),AH 股行情总结

AI手机及苹果概念股全日走强&#xff0c;领益智造、山东精密等多股涨停&#xff0c;立讯精、歌尔股份涨逾6% 。新车型秦L销售预期提振股价&#xff0c;比亚迪涨超1% &#xff1b;航运、煤炭、地产板块下跌。 文章正文 周一&#xff0c;A股低开高走&#xff0c;上证指数收跌0.…

代码随想录算法训练营第二十六天|39. 组合总和、 40.组合总和II、 131.分割回文串

39. 组合总和 题目链接&#xff1a;39. 组合总和 文档讲解&#xff1a;代码随想录 状态&#xff1a;卡了一会儿 思路&#xff1a;先排序&#xff0c;方便剪枝。允许数字重复使用&#xff0c;因此递归调用时传入当前索引i。 题解&#xff1a; public class Solution {// 用于存…

ETAS AUTOSAR工具链的作用

一、AUTOSAR是什么&#xff1f; AUTOSAR&#xff08;Automotive Open System Architecture&#xff09;是一个全球性的联盟&#xff0c;致力于制定和推广汽车电子系统的标准化解决方案。它是由汽车制造商、供应商和工程公司组成的合作伙伴网络&#xff0c;旨在解决汽车电子系统…