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

Java设计模式:四、行为型模式-06:观察者模式

文章目录

  • 一、定义:观察者模式
  • 二、模拟场景:观察者模式
    • 2.1 观察者模式
    • 2.2 引入依赖
    • 2.3 工程结构
    • 2.4 模拟摇号
      • 2.4.1 摇号服务接口
      • 2.4.2 摇号返回结果类
  • 三、违背方案:观察者模式
    • 3.0 引入依赖
    • 3.1 工程结构
    • 3.2 添加摇号接口和实现
      • 3.2.1 摇号服务接口
      • 3.2.2 摇号服务接口实现类
    • 3.3 单元测试
  • 四、改善代码:观察者模式
    • 4.0 引入依赖
    • 4.1 工程结构
    • 4.2 观察者模式结构图
    • 4.3 添加事件监听和管理器
      • 4.3.1 定义事件监听接口
      • 4.3.2 短信息事件监听接口实现
      • 4.3.3 MQ发送事件监听接口实现
      • 4.3.4 事件处理器
    • 4.4 摇号抽象类及其实现
      • 4.4.1 业务抽象类
      • 4.4.2 业务接口实现类
    • 4.5 单元测试
  • 五、总结:观察者模式

一、定义:观察者模式

在这里插入图片描述

  • **观察者模式:**当一个行为发生时传递信息给另外一个用户接收做出相应的处理,两者之间没有直接的耦合关联。
  • 除了生活中的场景外,在我们编程开发中也会常用到一些观察者的模式或者组件。例如:
    • 经常使用的 MQ 服务:虽然 MQ 服务是有一个通知中心并不是每一个类服务进行通知,但整体上也可以算作是观察者模式的思路设计。
    • 类似事件监听总线:让主线服务与其他辅线业务分离,为了使系统降低耦合和增强扩展性,也会使用观察者模式进行处理。

二、模拟场景:观察者模式

2.1 观察者模式

请添加图片描述

  • 模拟每次小客车指标摇号事件通知场景
  • 可能大部分人看到这个案例一定会想到自己每次摇号都不中的场景,收到一个遗憾的短信通知。当然目前的摇号系统不会给你发短信,而是由百度或者一些其他插件发的短信。
  • 那么假如这个类似的摇号功能由你来开发,并且需要对外部的用户做一些事件通知以及需要在主流外再添加一些额外的辅助流程时该如何处理呢?
    • 基本很多人对于这样的通知事件类的实现往往比较粗犷,直接在类里面添加就可以了。
      • ①考虑这可能不会怎么扩展,②是压根就没考虑过扩展。
    • 但如果你有仔细思考过你的核心类功能会发现,这里面有一些核心主链路,还有一部分是辅助功能。
      • 比如完成了某个行为后需要触发 MQ 给外部,以及做一些消息 PUSH 给用户等。
      • 这些都不算做是核心流程链路,是可以通过事件通知的方式进行处理。

2.2 引入依赖

pom.xml

<dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.62</version></dependency><!-- LOGGING begin --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.5</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>jcl-over-slf4j</artifactId><version>1.7.5</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.0.9</version><exclusions><exclusion><artifactId>slf4j-api</artifactId><groupId>org.slf4j</groupId></exclusion></exclusions></dependency>
</dependencies>

2.3 工程结构

design-19.0-0
|——src|——main|--java|--com.lino.design|-LotteryResult.java|-MinibusTargetService.java

2.4 模拟摇号

2.4.1 摇号服务接口

MinibusTargetService.java

package com.lino.design;/*** @description: 小客车指标调控服务*/
public class MinibusTargetService {/*** 模拟摇号** @param uId 用户编号* @return 结果*/public String lottery(String uId) {return Math.abs(uId.hashCode()) % 2 == 0 ?"恭喜你,编码".concat(uId).concat("在本次摇号中签"): "很遗憾,编码".concat(uId).concat("在本次摇号未中签或摇号资格已过期");}
}

2.4.2 摇号返回结果类

LotteryResult.java

package com.lino.design;import java.util.Date;/*** @description: 摇号结果类*/
public class LotteryResult {/*** 用户ID*/private String uId;/*** 摇号信息*/private String msg;/*** 业务时间*/private Date dateTime;public LotteryResult(String uId, String msg, Date dateTime) {this.uId = uId;this.msg = msg;this.dateTime = dateTime;}public String getuId() {return uId;}public void setuId(String uId) {this.uId = uId;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public Date getDateTime() {return dateTime;}public void setDateTime(Date dateTime) {this.dateTime = dateTime;}
}

三、违背方案:观察者模式

按照需求需要在原有的摇号接口中添加 MQ 消息发送以及短信息通知功能。
如果是最直接的方式那么可以直接在方法中补充功能即可。

3.0 引入依赖

<dependencies><dependency><groupId>com.lino</groupId><artifactId>design-19.0-0</artifactId><version>1.0-SNAPSHOT</version></dependency>
</dependencies>

3.1 工程结构

design-19.0-1
|——src|——main|--java|--com.lino.design|-LotteryService.java|-LotteryServiceImpl.java|--test|--com.lino.design.test|-ApiTest.java

3.2 添加摇号接口和实现

3.2.1 摇号服务接口

LotteryService.java

package com.lino.design;/*** @description: 摇号接口*/
public interface LotteryService {/*** 摇号** @param uId 用户编号* @return 摇号结果*/LotteryResult doDraw(String uId);
}

3.2.2 摇号服务接口实现类

LotteryServiceImpl.java

package com.lino.design;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;/*** @description: 摇号接口实现类*/
public class LotteryServiceImpl implements LotteryService {private Logger logger = LoggerFactory.getLogger(LotteryServiceImpl.class);private MinibusTargetService minibusTargetService = new MinibusTargetService();@Overridepublic LotteryResult doDraw(String uId) {// 摇号String lottery = minibusTargetService.lottery(uId);// 发短信logger.info("给用户 {} 发送短信通知(短信):{}", uId, lottery);// 发MQ信息logger.info("记录用户 {} 摇号结果(MQ):{}", uId, lottery);return new LotteryResult(uId, lottery, new Date());}
}
  • 从上面的方法实现中可以看到,整体过程包括三部分:摇号、发短信、发 MQ 消息,而这部分都是顺序调用的。
  • 除了 摇号 接口调用外,后面的两部分都是非核心主链路功能,而且会随着后续的业务需求发展而不断的调整和扩充,在这样的开发方式下就非常不利于维护。

3.3 单元测试

ApiTest.java

package com.lino.design.test;import com.alibaba.fastjson.JSON;
import com.lino.design.LotteryResult;
import com.lino.design.LotteryService;
import com.lino.design.LotteryServiceImpl;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** @description: 单元测试*/
public class ApiTest {private Logger logger = LoggerFactory.getLogger(ApiTest.class);@Testpublic void test() {LotteryService lotteryService = new LotteryServiceImpl();LotteryResult result = lotteryService.doDraw("2765789109876");logger.info("测试结果:{}", JSON.toJSONString(result));}
}
  • 测试过程中提供对摇号服务接口的调用

测试结果

17:06:14.931 [main] INFO  com.lino.design.LotteryServiceImpl - 给用户 2765789109876 发送短信通知(短信):很遗憾,编码2765789109876在本次摇号未中签或摇号资格已过期
17:06:14.931 [main] INFO  com.lino.design.LotteryServiceImpl - 记录用户 2765789109876 摇号结果(MQ):很遗憾,编码2765789109876在本次摇号未中签或摇号资格已过期
17:06:15.122 [main] INFO  com.lino.design.test.ApiTest - 测试结果:{"dateTime":1675760774946,"msg":"很遗憾,编码2765789109876在本次摇号未中签或摇号资格已过期","uId":"2765789109876"}

四、改善代码:观察者模式

4.0 引入依赖

<dependencies><dependency><groupId>com.lino</groupId><artifactId>design-19.0-0</artifactId><version>1.0-SNAPSHOT</version></dependency>
</dependencies>

4.1 工程结构

design-19.0-2
|——src|——main|--java|--com.lino.design|--event|		|--listener|		|		|--EventListener.java|		|		|--MessageEventListener.java|		|		|--MQEventListener.java|		|--EventManager.java|-LotteryService.java|-LotteryServiceImpl.java|--test|--com.lino.design.test|-ApiTest.java

4.2 观察者模式结构图

请添加图片描述

  • 从上图可以分为三大块:事件监听、事件处理、具体的业务流程。
    • 另外在业务流程中 LotteryService 定义的是抽象类,因为这样可以通过抽象类将事件功能屏蔽,外部业务流程开发者不需要知道具体的通知操作。
  • 右下角圆圈图表示的是核心流程与非核心流程的结构。
    • 一般在开发中会把主线流程开发完成后,再使用通知的方式处理辅助流程。他们可以是异步的,在 MQ 以及定时任务的处理下,保证最终一致性。

4.3 添加事件监听和管理器

4.3.1 定义事件监听接口

EventListener.java

package com.lino.design.event.listener;import com.lino.design.LotteryResult;/*** @description: 事件监听接口*/
public interface EventListener {/*** 监听事件** @param result 摇号结果*/void doEvent(LotteryResult result);
}
  • 接口定义了基本的事件类,这里如果方法的入参信息类型是变化的,可以使用泛型 T

4.3.2 短信息事件监听接口实现

MessageEventListener.java

package com.lino.design.event.listener;import com.lino.design.LotteryResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** @description: 短消息事件监听实现*/
public class MessageEventListener implements EventListener {private Logger logger = LoggerFactory.getLogger(MessageEventListener.class);@Overridepublic void doEvent(LotteryResult result) {logger.info("给用户 {} 发送短信通知(短信):{}", result.getuId(), result.getMsg());}
}

4.3.3 MQ发送事件监听接口实现

MQEventListener.java

package com.lino.design.event.listener;import com.lino.design.LotteryResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** @description: MQ事件监听实现*/
public class MQEventListener implements EventListener {private Logger logger = LoggerFactory.getLogger(MQEventListener.class);@Overridepublic void doEvent(LotteryResult result) {logger.info("记录用户 {} 摇号结果(MQ):{}", result.getuId(), result.getMsg());}
}

4.3.4 事件处理器

EventManager.java

package com.lino.design.event;import com.lino.design.LotteryResult;
import com.lino.design.event.listener.EventListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @description: 事件处理器*/
public class EventManager {Map<Enum<EventType>, List<EventListener>> listeners = new HashMap<>();public EventManager(Enum<EventType>... operations) {for (Enum<EventType> operation : operations) {this.listeners.put(operation, new ArrayList<>());}}public enum EventType {/*** 事件类型*/MQ, Message}/*** 订阅** @param eventType 事件类型* @param listener  监听*/public void subscribe(Enum<EventType> eventType, EventListener listener) {List<EventListener> users = listeners.get(eventType);users.add(listener);}/*** 取消订阅** @param eventType 事件类型* @param listener  监听*/public void unsubscribe(Enum<EventType> eventType, EventListener listener) {List<EventListener> users = listeners.get(eventType);users.remove(listener);}/*** 通知** @param eventType 事件类型* @param result    结果*/public void notify(Enum<EventType> eventType, LotteryResult result) {List<EventListener> users = listeners.get(eventType);for (EventListener listener : users) {listener.doEvent(result);}}
}
  • 整个处理的实现上提供了三个主要方法:订阅 subscribe 、取消订阅 unsubscribe 、通知 notify 。这三个方法分别用于对监听事件的添加和使用。
  • 另外因为事件有不同的类型,这里使用率枚举的方式进行处理,也方便让外部在规定下使用事件,而不至于乱传信息。
    • 枚举:EventType.MQEventType.Message

4.4 摇号抽象类及其实现

4.4.1 业务抽象类

LotteryService.java

package com.lino.design;import com.lino.design.event.EventManager;
import com.lino.design.event.listener.MQEventListener;
import com.lino.design.event.listener.MessageEventListener;/*** @description: 摇号抽象类*/
public abstract class LotteryService {private EventManager eventManager;public LotteryService() {eventManager = new EventManager(EventManager.EventType.MQ, EventManager.EventType.Message);eventManager.subscribe(EventManager.EventType.MQ, new MQEventListener());eventManager.subscribe(EventManager.EventType.Message, new MessageEventListener());}public LotteryResult draw(String uId) {LotteryResult lotteryResult = doDraw(uId);eventManager.notify(EventManager.EventType.MQ, lotteryResult);eventManager.notify(EventManager.EventType.Message, lotteryResult);return lotteryResult;}/*** 执行摇号** @param uId 用户编号* @return 结果*/protected abstract LotteryResult doDraw(String uId);
}
  • 使用抽象类的方式定义实现方法,可以在方法中扩展需要的额外调用。
    • 并提供抽象类 abstract LotteryResult doDraw(String uId) ,让类的继承者实现。
    • 同时,方法的定义使用的是 protected ,也就是保证将来外部的调用方不会调用到此方法,只有调用到 draw(String uId) 才能完成事件通知。
  • 此种方式到实现是在抽象类中写好一个基本的方法,在方法中完成新增逻辑到同时,再增加抽象类的使用,而这个抽象的定义会由继承者实现。
  • 另外,在构造函数中提供了对事件的定义:
    • eventManager.subscribe(EventManager.EventType.MQ, new MQEventListener())
    • 在使用时也采用枚举的方式通知使用者,传了哪些类型 eventManager.EventType.Message,就执行哪些事件通知,按需添加。

4.4.2 业务接口实现类

LotteryServiceImpl.java

package com.lino.design;import java.util.Date;/*** @description: 摇号服务实现* @author: lingjian* @createDate: 2023/2/6 17:02*/
public class LotteryServiceImpl extends LotteryService {private MinibusTargetService minibusTargetService = new MinibusTargetService();@Overrideprotected LotteryResult doDraw(String uId) {// 摇号String lottery = minibusTargetService.lottery(uId);// 结果return new LotteryResult(uId, lottery, new Date());}
}
  • 业务流程的实现,没有额外的辅助流程,只有核心流程的处理。

4.5 单元测试

ApiTest.java

package com.lino.design.test;import com.alibaba.fastjson.JSON;
import com.lino.design.LotteryResult;
import com.lino.design.LotteryService;
import com.lino.design.LotteryServiceImpl;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** @description: 单元测试*/
public class ApiTest {private Logger logger = LoggerFactory.getLogger(ApiTest.class);@Testpublic void test_draw() {LotteryService lotteryService = new LotteryServiceImpl();LotteryResult result = lotteryService.draw("1000000101010019");logger.info("测试结果:{}", JSON.toJSONString(result));}
}

测试结果

19:45:45.314 [main] INFO  c.l.d.event.listener.MQEventListener - 记录用户 1000000101010019 摇号结果(MQ):恭喜你,编码1000000101010019在本次摇号中签
19:45:45.319 [main] INFO  c.l.d.e.l.MessageEventListener - 给用户 1000000101010019 发送短信通知(短信):恭喜你,编码1000000101010019在本次摇号中签
19:45:45.398 [main] INFO  com.lino.design.test.ApiTest - 测试结果:{"dateTime":1675770345311,"msg":"恭喜你,编码1000000101010019在本次摇号中签","uId":"1000000101010019"}

五、总结:观察者模式

  • 从基本的过程式开发,到使用观察者模式面向对象开发,可以看到使用设计模式改造后,拆分出来核心流程与辅助流程的代码。
    • 代码中的核心流程一般不会发生经常变化,辅助流程会随着业务的变化而变化,包括营销、裂变和促活等。
    • 因此使用设计模式编码就显得非常有必要。
  • 观察者模式从结构上满足开闭原则,当需要新增其他的监听事件或修改监听逻辑时,不需要改动事件处理类。
  • 观察者模式可能不能控制调用顺序以及需要做一些事件结果的返回操作,所以在使用的过程时需要考虑场景的适用性。

相关文章:

Java设计模式:四、行为型模式-06:观察者模式

文章目录 一、定义&#xff1a;观察者模式二、模拟场景&#xff1a;观察者模式2.1 观察者模式2.2 引入依赖2.3 工程结构2.4 模拟摇号2.4.1 摇号服务接口2.4.2 摇号返回结果类 三、违背方案&#xff1a;观察者模式3.0 引入依赖3.1 工程结构3.2 添加摇号接口和实现3.2.1 摇号服务…...

vscode中讨厌的蓝色波浪线的去除小trick和原理

问题描述 不小心“设置同步”时和远程电脑的合并&#xff08;merge&#xff09;了&#xff0c;然后就出现了这个问题&#xff01;烦死了&#xff01;&#xff01;&#xff01; 大概是这个样子&#xff1a; 解决办法 站在了巨人的肩膀上&#xff0c;在下图位置输入这样一行参数&…...

开发工具——IDE安装 / IDEA子module依赖导入失败编译提示xx找不到符号 / IDEA在Git提交时卡顿

近期换了工作电脑&#xff0c;公司的IT团队不够给力&#xff0c;不能复制电脑系统&#xff0c;所以又到了需要重装IDE配置开发环境的时候了&#xff1b;在安装和导入Java编译器IDEA的时候遇到一些"棘手"问题&#xff0c;这里整理下解决方法以备不时之需&#xff1b; …...

AcWing 787:归并排序

【题目来源】https://www.acwing.com/problem/content/789/【题目描述】 给定你一个长度为 n 的整数数列。 请你使用归并排序对这个数列按照从小到大进行排序。 并将排好序的数列按顺序输出。【输入格式】 输入共两行&#xff0c;第一行包含整数 n。 第二行包含 n 个整数&#…...

SeamlessM4T—Massively Multilingual Multimodal Machine Translation

本文是LLM系列的文章&#xff0c;针对《SeamlessM4T—Massively Multilingual & Multimodal Machine Translation》的翻译。 SeamlessM4T&#xff1a;大规模语言多模态机器翻译 摘要1 引言2 多模态翻译的社会技术维度2.12.22.3 3 SeamlessAlign&#xff1a;自动创建语音对…...

Python数据分析-Numpy

Numpy 个人笔记&#xff0c;仅供参考&#xff0c;谢谢 导入 import numpy import numpy as np from numpy import *Numpy数组对象 引入 # 让列表1 a [1,2,3,4],b [4,5,6,7] [x1 for x in a] # 实现ab a b > [1,2,3,4,5,6,7,8] [x y for (x,y) in zip(a,b)] -------…...

【真题解析】系统集成项目管理工程师 2023 年上半年真题卷(案例分析)

本文为系统集成项目管理工程师考试(软考) 2023 年上半年真题(全国卷),包含答案与详细解析。考试共分为两科,成绩均 ≥45 即可通过考试: 综合知识(选择题 75 道,75分)案例分析(问答题 4 道,75分)案例分析(问答题*4)试题一试题二试题三试题四案例分析(问答题*4) …...

【GAMES202】Real-Time Global Illumination(in 3D)—实时全局光照(3D空间)

一、SH for Glossy transport 1.Diffuse PRT回顾 上篇我们介绍了PRT&#xff0c;并以Diffuse的BRDF作为例子分析了预计算的部分&#xff0c;包括Lighting和Light transport&#xff0c;如上图所示。 包括我们还提到了SH&#xff0c;可以用SH的有限阶近似拟合球面函数&#xff…...

金蝶云星空二开,公有云执行SQL

功能背景&#xff1b; 金蝶公有云执行sql工具&#xff0c;因官方为云部署 用户无法连接数据库增删改查 天梯维护网页仅支持增删改操作 二开单据已支持根据sql动态生成单据体 与sql可视化界面操作一致 功能实现及场景&#xff1a; 1.可用于公有云执行sql类操作 2.私有云部署&am…...

JAVA String 二维的字符串数组 String[][]

String[][] 表示一个二维的字符串数组&#xff0c;也可以称为字符串矩阵。它是由多个一维的字符串数组组成的&#xff0c;每个一维数组都表示矩阵中的一行。 在 Java 中&#xff0c;可以使用如下方式声明和初始化一个二维字符串数组&#xff1a; String[][] matrix new Strin…...

【Unity3D赛车游戏优化篇】【九】Unity中如何让汽车丝滑漂移?

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…...

el-dialog设置高度、使用resetFields清除表单项无效问题

初学者容易踩坑的的el-dialog、el-form问题 1. el-dialog设置高度2. el-form中表单项对不齐3. 使用resetFields清除表单项无效 1. el-dialog设置高度 在el-dialog中里面添加一个div设置固定高度&#xff0c;或者限制最小的高度。 <el-dialogtitle"选择图标"v-mod…...

MySql切换到达梦数据库,各种问题解决记录

参考官方文档&#xff1a; https://eco.dameng.com/document/dm/zh-cn/sql-dev/practice-func.html 1. 关键字导致的报错&#xff1a;如ref,comment,top,domain等 Error -2007: 第 1 行, 第 117 列[ref]附近出现错误: 语法分析出错解决方案&#xff1a;修改关键字即可 2. 查…...

2023开学礼山东财经大学《乡村振兴战略下传统村落文化旅游设计》许少辉新财经图书馆

2023开学礼山东财经大学《乡村振兴战略下传统村落文化旅游设计》许少辉新财经图书馆...

vscode中使用eslint+prettier的配置

eslintprettiervscode自动保存用起来感觉非常爽快。 一般来说&#xff0c;安装eslintprettier插件&#xff0c;然后使用相关脚手架配套的eslintprettier&#xff0c;无法自动格式代码&#xff0c;每次都需要执行格式化命令。这里贴出保存自动格式化代码的setting.json。 // .…...

HTML 标签讲解

HTML 标签讲解 HTML 语言结构根元素元数据元素主体根元素大纲元素文本内容语义化内联文本图像与多媒体编辑标识table表格内容表单内容table表单 HTML 语言结构 Markup &#xff08;标记、标签&#xff09;用来容纳和描述内容 严格意义上&#xff0c;标签是指开始标签&#xf…...

ue5 小知识点 ue的world type,pie editor game

说明以该命令行模式启动游戏的前提下的两个问题&#xff1a; 1.WITH_EDITOR中的代码会被编译 2.由于没有在编辑器中(即没有打开虚幻编辑器)&#xff0c;所以GIsEditor为false WITH_EDITOR和WITH_EDITORONLY_DATA的区别 在论坛中找到的答案&#xff1a; WITH_EDITORONLY_DAT…...

两表union 如何保证group by 字段唯一

当要计算的指标可能来源多个表时&#xff0c;可能会使用到union all把不同的表中计算的指标合起来。关于union all使用条件&#xff1a;两个要联合的SQL语句 字段个数必须一样&#xff0c;而且字段类型要“相容”&#xff08;一致&#xff09; 另外&#xff0c;回顾union和uni…...

【⑰MySQL】 变量 | 循环 | 游标 | 处理程序

前言 ✨欢迎来到小K的MySQL专栏&#xff0c;本节将为大家带来MySQL变量 | 循环 | 游标 | 处理程序的分享✨ 目录 前言1. 变量1.1系统变量1.2 用户变量 2. 定义条件与处理程序2.1 案例分析2.2 定义条件2.3 定义处理程序2.4 案例解决 3. 流程控制3.1 分支结构3.2 循环结构3.3 跳转…...

如何在arXiv上发表一篇文章

目录 1. 初始信息确认2. 提交论文文件3. 论文编译结果4. 补充论文信息5. 总览 1. 初始信息确认 版权问题需要根据个人情况选择。 IEEE, Elsevier, BioMed Central, 这几个出版商都允许在投稿之前挂文章到arXiv下。通常是选择&#xff1a; arXiv.org perpetual, non-exclusive l…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录

ASP.NET Core 是一个跨平台的开源框架&#xff0c;用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录&#xff0c;以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法&#xff0c;用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理&#xff0c;能够自动确定一个阈值&#xff0c;将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

如何为服务器生成TLS证书

TLS&#xff08;Transport Layer Security&#xff09;证书是确保网络通信安全的重要手段&#xff0c;它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书&#xff0c;可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

VTK如何让部分单位不可见

最近遇到一个需求&#xff0c;需要让一个vtkDataSet中的部分单元不可见&#xff0c;查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行&#xff0c;是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示&#xff0c;主要是最后一个参数&#xff0c;透明度…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

Python Einops库:深度学习中的张量操作革命

Einops&#xff08;爱因斯坦操作库&#xff09;就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库&#xff0c;用类似自然语言的表达式替代了晦涩的API调用&#xff0c;彻底改变了深度学习工程…...