零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern)
欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。
- 核心思想:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
责任链模式 (Chain of Responsibility Pattern)
“使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。” (Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.)
想象一下公司里的报销审批流程。一个员工提交了报销申请:
- 如果金额较小(比如500元以下),可能直接由其直属经理(第一个处理者)审批即可。
- 如果金额稍大(比如500元到2000元),直属经理审批后,还需要部门主管(第二个处理者)审批。
- 如果金额更大(比如2000元以上),部门主管审批后,可能还需要财务总监(第三个处理者)审批。
员工只需要提交申请,申请会沿着这条“审批链”传递,直到遇到能够完全处理它(或者批准,或者拒绝)的审批人,或者链条结束。
- 请求 (Request):报销申请。
- 处理者 (Handler):直属经理、部门主管、财务总监。
- 链 (Chain):由这些审批人构成的审批顺序。
1. 目的 (Intent)
责任链模式的主要目的:
- 解耦发送者和接收者:请求的发送者不需要知道是哪个具体对象会处理它的请求。它只需要将请求发送到链的头部即可。
- 动态组合责任:可以动态地改变链中的处理者顺序或增删处理者,而无需修改客户端代码。
- 请求的灵活处理:每个处理者都可以决定自己是否处理该请求、是否将请求传递给下一个处理者,或者两者都做。
2. 生活中的例子 (Real-world Analogy)
-
客服电话系统:
- 你拨打客服电话,首先可能是一个自动语音应答系统(第一处理者)尝试解决你的问题。
- 如果解决不了,它会将你转接到初级人工客服(第二处理者)。
- 如果初级客服也无法解决,可能会转接到高级客服或技术支持(第三处理者)。
你的问题沿着这条服务链传递,直到被解决。
-
异常处理机制 (try-catch-finally):
- 在很多编程语言中,
try
块中的代码如果抛出异常,会先被最近的catch
块尝试捕获和处理。 - 如果第一个
catch
块不能处理该类型的异常,或者它选择重新抛出,异常会向上传播到外层的catch
块(如果存在的话),形成一个异常处理链。
- 在很多编程语言中,
-
事件冒泡/捕获 (Event Bubbling/Capturing in Web Development):
- 在HTML DOM中,当一个元素上发生事件(如点击),事件会从目标元素开始,逐级向上传播到其父元素,直到文档根节点(事件冒泡),或者从文档根节点向下传播到目标元素(事件捕获)。
- 每一级父元素都有机会处理这个事件。
-
中间件 (Middleware in Web Frameworks):
- 像 Express.js, Django, ASP.NET Core 等Web框架中的中间件。一个HTTP请求会依次通过一系列注册的中间件(如日志记录、身份验证、数据压缩、路由处理等)。
- 每个中间件都可以处理请求、修改请求/响应,或者将请求传递给链中的下一个中间件。
3. 结构 (Structure)
责任链模式通常包含以下角色:
- Handler (处理者接口/抽象类):定义一个处理请求的接口。通常会包含一个指向链中下一个处理者的引用(
successor
)。它声明了一个处理请求的方法(如handleRequest()
)。 - ConcreteHandler (具体处理者):实现 Handler 接口。它负责处理它感兴趣的请求。如果它可以处理该请求,就处理之;否则,它会将请求转发给它的后继者。
- Client (客户端):创建处理者链,并发起请求到链上的某个处理者(通常是链的第一个处理者)。
工作流程:
- 客户端创建一个处理者链,并设置每个处理者的后继者。
- 客户端向链的第一个处理者发送请求。
- 处理者接收到请求后:
- 判断自己是否能处理该请求。
- 如果能处理,则处理请求。此时,它可以选择是否将请求继续传递给后继者(取决于具体需求,有些链在请求被处理后即终止,有些则允许继续传递)。
- 如果不能处理,则将请求传递给其后继者(如果后继者存在)。
- 请求沿着链传递,直到被某个处理者处理,或者到达链的末尾仍未被处理。
4. 适用场景 (When to Use)
- 有多个对象可以处理同一个请求,但具体由哪个对象处理则在运行时动态确定。
- 你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
- 需要动态指定一组对象处理请求的顺序。
- 当你想让请求的发送者和接收者解耦时。
- 当处理请求的对象集合应当动态指定时。
5. 优缺点 (Pros and Cons)
优点:
- 降低耦合度:请求的发送者和接收者之间解耦。发送者不需要知道谁将处理请求,也不需要知道链的结构。
- 增强了对象的指派职责的灵活性:可以通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
- 增加新的请求处理类很方便:只需创建新的 Handler 类并将其加入链中即可,符合开闭原则。
- 每个处理者职责清晰:每个具体处理者只需要关注自己能处理的请求。
缺点:
- 请求不一定会被处理:由于请求在链上发送,如果链配置不当或者没有任何处理者能够处理该请求,请求可能会落空(到达链尾仍未被处理)。
- 不易观察运行时的特征:如果链条比较长,调试时可能不容易追踪请求的实际处理流程。
- 可能影响性能:请求需要从链头开始遍历,如果链过长,且大部分请求都需要由链尾的处理者处理,性能可能会受到影响。
- 链的创建和配置可能比较复杂:需要正确设置每个处理者的后继者。
6. 实现方式 (Implementations)
让我们以一个简单的日志系统为例。不同级别的日志消息(INFO, DEBUG, ERROR)由不同的记录器处理。
请求类 (LogRequest - 可选,或直接用参数)
这里我们直接用参数(日志级别和消息内容)传递,不单独创建请求类。
处理者接口 (Logger - Handler)
// logger.go (Handler interface)
package loggingconst (LevelInfo = 1LevelDebug = 2LevelError = 3
)// Logger 处理者接口
type Logger interface {SetNext(logger Logger) Logger // 设置下一个处理者,并返回下一个处理者方便链式调用LogMessage(level int, message string) // 处理日志消息write(message string) // 具体的写日志方法,由具体处理者实现
}
// Logger.java (Handler abstract class)
package com.example.logging;public abstract class Logger {public static final int INFO = 1;public static final int DEBUG = 2;public static final int ERROR = 3;protected int level; // 当前处理器能处理的级别protected Logger nextLogger; // 责任链中的下一个元素public Logger setNextLogger(Logger nextLogger) {this.nextLogger = nextLogger;return this.nextLogger; // 返回下一个logger,方便链式设置}public void logMessage(int level, String message) {if (this.level <= level) { // 如果当前处理器的级别允许处理该消息write(message);}if (nextLogger != null) { // 如果有下一个处理器,则传递下去nextLogger.logMessage(level, message);}}// 抽象方法,由子类实现具体的日志写入操作abstract protected void write(String message);
}
具体处理者 (InfoLogger, DebugLogger, ErrorLogger - ConcreteHandler)
// abstract_logger.go (Base for Concrete Handlers - Go doesn't have abstract classes, use struct embedding)
package logging// AbstractLogger 基础结构,包含下一个处理者的引用
type AbstractLogger struct {level intnext Logger
}func (al *AbstractLogger) SetNext(logger Logger) Logger {al.next = loggerreturn logger
}// LogMessage 默认实现:如果级别匹配则自己写,然后传递给下一个
func (al *AbstractLogger) LogMessage(level int, message string) {if al.level <= level {al.write(message) // 调用具体实现类的 write}if al.next != nil {al.next.LogMessage(level, message)}
}// write 是一个需要被具体 Logger 实现的方法,但由于 Go 的限制,
// 我们不能在这里定义一个抽象的 write。具体 Logger 需要自己实现 write,
// 并在 LogMessage 中调用它,或者像上面那样,让 AbstractLogger 的 LogMessage 调用一个 write 方法,
// 这个 write 方法必须是具体 Logger 类型的一部分。
// 为了简单,我们让具体 Logger 实现 Logger 接口,并在 LogMessage 中直接调用自己的 write。
// 或者,我们可以让具体 Logger 嵌入 AbstractLogger,并重写 LogMessage 或提供 write。// --- 具体实现 ---// info_logger.go
package loggingimport "fmt"type InfoLogger struct {AbstractLogger
}func NewInfoLogger(level int) *InfoLogger {il := &InfoLogger{}il.AbstractLogger.level = levelreturn il
}// write 实现具体的日志写入逻辑
func (il *InfoLogger) write(message string) {fmt.Println("INFO Logger: " + message)
}// LogMessage (可以重写以改变行为,例如只处理自己的级别然后停止)
// 如果不重写,则使用 AbstractLogger 的 LogMessage 行为// debug_logger.go
package loggingimport "fmt"type DebugLogger struct {AbstractLogger
}func NewDebugLogger(level int) *DebugLogger {dl := &DebugLogger{}dl.AbstractLogger.level = levelreturn dl
}func (dl *DebugLogger) write(message string) {fmt.Println("DEBUG Logger: " + message)
}// error_logger.go
package loggingimport "fmt"type ErrorLogger struct {AbstractLogger
}func NewErrorLogger(level int) *ErrorLogger {el := &ErrorLogger{}el.AbstractLogger.level = levelreturn el
}func (el *ErrorLogger) write(message string) {fmt.Println("ERROR Logger: " + message)
}
注意:在Go的实现中,由于没有传统意义的抽象方法,AbstractLogger
的 LogMessage
调用 al.write(message)
。这意味着嵌入 AbstractLogger
的具体类型必须有一个名为 write
的方法。如果具体类型也想改变 LogMessage
的传递逻辑(例如,处理后就停止),则需要重写 LogMessage
方法。
一个更符合责任链“要么处理要么传递”的Go实现方式,可能是在LogMessage
中判断是否能处理,如果不能,则直接调用next.LogMessage
。如果能处理,则处理,然后根据策略决定是否继续传递。
修改后的Go AbstractLogger
和具体实现:
// logger.go (Handler interface - 修正版)
package loggingconst (LevelInfo = 1LevelDebug = 2LevelError = 3
)type Logger interface {SetNext(logger Logger) LoggerProcessLog(level int, message string) // 改名为 ProcessLog,更清晰
}// abstract_logger.go (Base for Concrete Handlers - 修正版)
package logging// AbstractLogger 基础结构
type AbstractLogger struct {level intnext Logger
}func (al *AbstractLogger) SetNext(logger Logger) Logger {al.next = loggerreturn logger
}// ProcessLog 模板方法:如果当前级别能处理,则调用 write,否则传递
// 注意:这里的 write 仍然需要具体类型实现。我们将让具体类型重写 ProcessLog。
// 或者,我们可以让 write 成为接口的一部分,但这样每个具体实现都需要检查级别。
// 更常见的做法是,每个具体 Handler 的处理方法自己决定是否处理和是否传递。// --- 具体实现 (修正版) ---// info_logger.go
package loggingimport "fmt"type InfoLogger struct {level intnext Logger
}func NewInfoLogger(level int) *InfoLogger {return &InfoLogger{level: level}
}func (il *InfoLogger) SetNext(logger Logger) Logger {il.next = loggerreturn logger
}func (il *InfoLogger) ProcessLog(level int, message string) {if il.level <= level {fmt.Println("INFO Logger: " + message)}if il.next != nil {il.next.ProcessLog(level, message)}
}// debug_logger.go
package loggingimport "fmt"type DebugLogger struct {level intnext Logger
}func NewDebugLogger(level int) *DebugLogger {return &DebugLogger{level: level}
}func (dl *DebugLogger) SetNext(logger Logger) Logger {dl.next = loggerreturn logger
}func (dl *DebugLogger) ProcessLog(level int, message string) {if dl.level <= level {fmt.Println("DEBUG Logger: " + message)}if dl.next != nil {dl.next.ProcessLog(level, message)}
}// error_logger.go
package loggingimport "fmt"type ErrorLogger struct {level intnext Logger
}func NewErrorLogger(level int) *ErrorLogger {return &ErrorLogger{level: level}
}func (el *ErrorLogger) SetNext(logger Logger) Logger {el.next = loggerreturn logger
}func (el *ErrorLogger) ProcessLog(level int, message string) {if el.level <= level {fmt.Println("ERROR Logger: " + message)}if el.next != nil {el.next.ProcessLog(level, message)}
}
// InfoLogger.java (ConcreteHandler)
package com.example.logging;public class InfoLogger extends Logger {public InfoLogger(int level) {this.level = level;}@Overrideprotected void write(String message) {System.out.println("INFO Logger: " + message);}
}// DebugLogger.java (ConcreteHandler)
package com.example.logging;public class DebugLogger extends Logger {public DebugLogger(int level) {this.level = level;}@Overrideprotected void write(String message) {System.out.println("DEBUG Logger: " + message);}
}// ErrorLogger.java (ConcreteHandler)
package com.example.logging;public class ErrorLogger extends Logger {public ErrorLogger(int level) {this.level = level;}@Overrideprotected void write(String message) {System.out.println("ERROR Logger: " + message);}
}
客户端使用
// main.go (示例用法)
/*
package mainimport ("./logging""fmt"
)func getChainOfLoggers() logging.Logger {errorLogger := logging.NewErrorLogger(logging.LevelError)debugLogger := logging.NewDebugLogger(logging.LevelDebug)infoLogger := logging.NewInfoLogger(logging.LevelInfo)// 构建责任链: Error -> Debug -> Info// 意味着 ErrorLogger 是链的开始,如果它处理不了或选择传递,则给 DebugLogger,以此类推// 但在这个日志例子中,通常是 Info (最低级) -> Debug -> Error (最高级)// 并且每个logger都会打印它能处理的级别以及更高级别的日志// 更常见的日志链: Info -> Debug -> Error// InfoLogger 处理所有 INFO, DEBUG, ERROR// DebugLogger 处理所有 DEBUG, ERROR (如果从 InfoLogger 传递过来)// ErrorLogger 处理所有 ERROR (如果从 DebugLogger 传递过来)infoLogger.SetNext(debugLogger)debugLogger.SetNext(errorLogger)return infoLogger // 返回链的头部
}func main() {loggerChain := getChainOfLoggers()fmt.Println("--- Sending INFO message ---")loggerChain.ProcessLog(logging.LevelInfo, "This is an information.")// Expected: INFO, DEBUG, ERROR loggers might print this if their level <= LevelInfo// With our current logger logic (this.level <= level), and chain Info -> Debug -> Error:// InfoLogger (level 1 <= 1) prints.// DebugLogger (level 2 > 1) does not print itself, but passes.// ErrorLogger (level 3 > 1) does not print itself.// Corrected logic for typical logging: a logger handles messages AT OR ABOVE its configured level.// So, if chain is Info(1) -> Debug(2) -> Error(3):// LevelInfo message: InfoLogger prints. DebugLogger and ErrorLogger also print.// LevelDebug message: InfoLogger does not print. DebugLogger prints. ErrorLogger prints.// LevelError message: InfoLogger and DebugLogger do not print. ErrorLogger prints.// The example code implements: if my_handler_level <= message_level, then I print.// This means a higher level handler (e.g. ErrorLogger with level 3) will print for a lower level message (e.g. Info with level 1).// This is typical for logging frameworks where setting a log level (e.g. INFO) means you see INFO and all levels above it (DEBUG, ERROR).fmt.Println("\n--- Sending DEBUG message ---")loggerChain.ProcessLog(logging.LevelDebug, "This is a debug level information.")// Expected (with chain Info(1) -> Debug(2) -> Error(3) and handler_level <= message_level):// InfoLogger (1 <= 2) prints.// DebugLogger (2 <= 2) prints.// ErrorLogger (3 > 2) does not print itself.fmt.Println("\n--- Sending ERROR message ---")loggerChain.ProcessLog(logging.LevelError, "This is an error information.")// Expected (with chain Info(1) -> Debug(2) -> Error(3) and handler_level <= message_level):// InfoLogger (1 <= 3) prints.// DebugLogger (2 <= 3) prints.// ErrorLogger (3 <= 3) prints.// Let's clarify the Go logger logic for a more standard CoR where only one handler acts// or where a handler acts and then passes. The current Go example is more like a broadcast// to all eligible handlers based on level comparison.// A more CoR-like logger might be: each logger handles ONLY its specific level.// If so, the chain order and logic in ProcessLog would change.// Example: if level == il.level { print } else if next != nil { next.ProcessLog }
}
*/
// Main.java (示例用法)
/*
package com.example;import com.example.logging.Logger;
import com.example.logging.InfoLogger;
import com.example.logging.DebugLogger;
import com.example.logging.ErrorLogger;public class Main {private static Logger getChainOfLoggers() {// 创建不同级别的日志记录器Logger errorLogger = new ErrorLogger(Logger.ERROR);Logger debugLogger = new DebugLogger(Logger.DEBUG);Logger infoLogger = new InfoLogger(Logger.INFO);// 构建责任链// INFO logger is the first in chain, then DEBUG, then ERROR.// A message of a certain level will be handled by loggers whose level is less than or equal to the message's level.infoLogger.setNextLogger(debugLogger);debugLogger.setNextLogger(errorLogger);return infoLogger; // 返回链的头部}public static void main(String[] args) {Logger loggerChain = getChainOfLoggers();System.out.println("--- Sending INFO message ---");loggerChain.logMessage(Logger.INFO, "This is an information.");// Expected output (based on current Java Logger logic):// INFO Logger: This is an information.// (DEBUG and ERROR loggers will also be called but won't print if their level is higher than INFO)// Corrected: With `this.level <= level`, InfoLogger (1<=1) prints. DebugLogger (2>1) no, ErrorLogger (3>1) no.// The Java code's logMessage passes to nextLogger REGARDLESS of whether current logger wrote.// So, for INFO message (level 1):// InfoLogger (level 1): write() called. nextLogger.logMessage(1, ...) called.// DebugLogger (level 2): write() NOT called (2 > 1). nextLogger.logMessage(1, ...) called.// ErrorLogger (level 3): write() NOT called (3 > 1). nextLogger is null.// Output: INFO Logger: This is an information.System.out.println("\n--- Sending DEBUG message ---");loggerChain.logMessage(Logger.DEBUG, "This is a debug level information.");// For DEBUG message (level 2):// InfoLogger (level 1): write() called. nextLogger.logMessage(2, ...) called.// DebugLogger (level 2): write() called. nextLogger.logMessage(2, ...) called.// ErrorLogger (level 3): write() NOT called (3 > 2). nextLogger is null.// Output: INFO Logger: This is a debug level information.// DEBUG Logger: This is a debug level information.System.out.println("\n--- Sending ERROR message ---");loggerChain.logMessage(Logger.ERROR, "This is an error information.");// For ERROR message (level 3):// InfoLogger (level 1): write() called. nextLogger.logMessage(3, ...) called.// DebugLogger (level 2): write() called. nextLogger.logMessage(3, ...) called.// ErrorLogger (level 3): write() called. nextLogger is null.// Output: INFO Logger: This is an error information.// DEBUG Logger: This is an error information.// ERROR Logger: This is an error information.}
}
*/
关于日志示例的说明:
上面的日志示例实现了一种“广播式”的责任链,即一个日志消息会被所有级别低于或等于该消息级别的记录器处理。例如,一个 ERROR
消息会被 InfoLogger
、DebugLogger
和 ErrorLogger
都记录(如果它们都在链中且按此顺序)。
一个更“纯粹”的责任链变体可能是:
- 单一处理:一旦一个处理者处理了请求,请求就不再向下传递。
- 条件传递:处理者处理请求后,根据某些条件决定是否继续传递。
例如,在审批流程中,一旦经理批准了小额报销,就不需要再给总监看。这需要在 handleRequest
方法中加入逻辑:如果处理了,则返回或不再调用 successor.handleRequest()
。
对于日志,当前实现是合理的,因为通常希望高优先级的日志也包含低优先级日志记录器的输出(例如,设置日志级别为DEBUG,则INFO和DEBUG日志都可见)。
7. 总结
责任链模式通过将多个能够处理请求的对象连接成一条链,使得请求可以在链上动态传递,直到被处理为止。它有效地解耦了请求的发送者和接收者,并允许动态地组织和修改处理者链。这种模式在需要多个对象协同处理一个请求,并且具体处理者在运行时确定的场景中非常有用,如审批流程、事件处理、中间件管道等。
记住它的核心:请求沿链传递,逐级处理,解耦收发。
相关文章:

零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...

NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...

前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...

自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...

让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...

Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...