在日常开发中,我们经常会遇到「需要根据不同的业务逻辑选择不同实现类」的场景。比如:文件操作可能有读取、写入、修改、删除 等多种方式,如果我们在业务代码中使用大量的 if-else
来区分,会导致代码臃肿且难以维护。
本文将结合 工厂模式(Factory Pattern) 和 策略模式(Strategy Pattern) ,通过一个 工具管理器(ToolManager) 的案例,来展示如何优雅地管理和调用不同的工具。
一、模式介绍
1. 工厂模式
工厂模式的核心思想是:将对象的创建与使用解耦 。
调用方不需要关心对象的具体实现,而只需要通过工厂方法获取对象即可。
在本文中,ToolManager
就是一个工厂,负责 统一注册和管理所有工具实例。
2. 策略模式
策略模式强调:定义一系列算法(策略),并且可以在运行时自由切换 。
调用方只需要依赖抽象接口,而不需要知道具体的实现细节。
在本文中,每个工具(如 FileReadTool
、FileWriteTool
)就是一个具体策略,统一实现了 BaseTool
抽象类。
二、使用场景
这种「工厂模式 + 策略模式」的组合,非常适合以下场景:
-
多实现类管理
比如支付系统中的 微信支付 / 支付宝支付 / 银行卡支付,都可以作为不同的策略,由工厂统一管理。
-
动态扩展
新增一种工具(比如
FileUploadTool
),只需要实现BaseTool
并交给工厂管理,而不需要改动原有逻辑,符合 开闭原则。 -
业务逻辑解耦
调用方只依赖抽象接口(
BaseTool
),而不依赖具体实现,业务逻辑更加清晰。
三、代码示例
下面给出完整的示例代码,使用 Spring + Hutool 结合实现。
1. 定义工具基类(抽象类)
java
package com.ming.mingaicode.ai.tools;
import cn.hutool.json.JSONObject;
/**
* 工具基类,定义所有工具的通用接口
*/
public abstract class BaseTool {
public abstract String getToolName();
public abstract String getDisplayName();
public String generateToolRequestResponse() {
return String.format("\n\n[选择工具🔧] %s\n\n", getDisplayName());
}
public abstract String generateToolExecutedResult(JSONObject arguments);
}
2. 定义多个工具实现类(具体策略)
文件目录读取工具
java
@Slf4j
@Component
public class FileDirReadTool extends BaseTool{
@Override
public String getToolName() { return "readDir"; }
@Override
public String getDisplayName() { return "读取目录"; }
@Override
public String generateToolExecutedResult(JSONObject arguments) {
String relativeDirPath = arguments.getStr("relativeDirPath");
if (StrUtil.isEmpty(relativeDirPath)) {
relativeDirPath = "根目录";
}
return String.format("[工具调用] %s %s", getDisplayName(), relativeDirPath);
}
}
文件修改工具
java
@Slf4j
@Component
public class FileModifyTool extends BaseTool{
@Override
public String getToolName() { return "modifyFile"; }
@Override
public String getDisplayName() { return "修改文件"; }
@Override
public String generateToolExecutedResult(JSONObject arguments) {
String relativeFilePath = arguments.getStr("relativeFilePath");
String oldContent = arguments.getStr("oldContent");
String newContent = arguments.getStr("newContent");
return String.format("""
[工具调用] %s %s
替换前:
```
%s
```
替换后:
```
%s
```
""", getDisplayName(), relativeFilePath, oldContent, newContent);
}
}
文件写入工具
java
@Slf4j
@Component
public class FileWriteTool extends BaseTool{
@Override
public String getToolName() { return "writeFile"; }
@Override
public String getDisplayName() { return "写入文件"; }
@Override
public String generateToolExecutedResult(JSONObject arguments) {
String relativeFilePath = arguments.getStr("relativeFilePath");
String suffix = FileUtil.getSuffix(relativeFilePath);
String content = arguments.getStr("content");
return String.format("""
[工具调用] %s %s
```%s
%s
```
""", getDisplayName(), relativeFilePath, suffix, content);
}
}
(其他工具如 FileReadTool
、FileDeleteTool
也类似,不再赘述)
3. 工厂管理器(ToolManager)
java
@Slf4j
@Component
public class ToolManager {
@Resource
private BaseTool[] tools;
private final Map<String, BaseTool> toolMap = new HashMap<>();
@PostConstruct
public void initTools() {
for (BaseTool tool : tools) {
toolMap.put(tool.getToolName(), tool);
log.info("注册工具: {} -> {}", tool.getToolName(), tool.getDisplayName());
}
log.info("工具管理器初始化完成,共注册 {} 个工具", toolMap.size());
}
public BaseTool getTool(String toolName) {
return toolMap.get(toolName);
}
public BaseTool[] getAllTools() {
return tools;
}
}
4. 使用方式
java
// 处理工具请求
case TOOL_REQUEST -> {
BaseTool tool = toolManager.getTool(toolRequestMessage.getName());
return tool.generateToolRequestResponse();
}
// 处理工具执行结果
case TOOL_EXECUTED -> {
BaseTool tool = toolManager.getTool(toolExecutedMessage.getName());
String result = tool.generateToolExecutedResult(jsonObject);
return String.format("\n\n%s\n\n", result);
}
四、方法解析
-
抽象基类
BaseTool
-
统一了所有工具的接口(名字、展示名、执行结果格式化)。
-
保证扩展新工具时,调用方无需修改原有逻辑。
-
-
具体工具类(策略)
- 每个工具都实现了自己的
generateToolExecutedResult
方法,遵循 策略模式。
- 每个工具都实现了自己的
-
ToolManager 工厂
-
自动扫描所有
BaseTool
的子类(Spring 注入)。 -
通过
Map
保存工具名称和实例的映射,支持快速查找和调用。
-
五、总结
本文通过 工厂模式 + 策略模式 结合的方式,构建了一个可扩展、可维护的 工具管理器:
-
工厂模式负责统一管理实例。
-
策略模式负责封装不同的业务逻辑。
-
新增功能时,只需要 新增一个实现类 ,无需修改现有代码,真正做到 开闭原则。
这种设计思想在 支付系统、权限系统、AI 工具调用 等场景下都有广泛应用,能够显著提升代码的可维护性和扩展性。