
AI编程在OOP场景下探索
背景
我们Spring AI工程引用代码如下,由于基于Mock单元测试下ChatClient总是为空,异常是
Cannot invoke “org.springframework.ai.chat.client.ChatClient.prompt(String)” because “this.chatClient” is null
Spring AI 1.0.0的正式发布时间为2025年5月20日
业务逻辑代码
@Service
public class TaskMcpCallServerServices implements AiService, ChatService {
private final ChatClient chatClient;
/**
* Constructs a new TaskMcpCallServerServices instance.
* @param aiClientBuilder the ChatClient builder used to create the chat client
*/
public TaskMcpCallServerServices(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build();
}
@Override
public ChatClient getChatClient() {
return chatClient;
}
@Override
/**
* Gets AI response for the given prompt by calling the MCP server.
* @param prompt the input prompt to send to the AI
* @return ResponseEntity containing the AI response
*/
public ResponseEntity<String> getAiResponse(String prompt) {
String response = this.chatClient
.prompt(prompt)
.call()
.content();
return ResponseEntity.ok(response);
}
}
单元测试代码
@ExtendWith(MockitoExtension.class)
class TaskMcpCallServerServicesTest {
@Mock
private ChatClient.Builder chatClientBuilder;
@Mock
private ChatClient chatClient;
@InjectMocks
private TaskMcpCallServerServices taskMcpCallServerServices;
@Test
void testGetAiResponse() {
// Given
String testPrompt = "test prompt";
String expectedResponse = "test response";
when(chatClientBuilder.build()).thenReturn(chatClient);
// When
ResponseEntity<String> actualResponse = taskMcpCallServerServices.getAiResponse(testPrompt);
// Then
assertEquals(expectedResponse, actualResponse.getBody());
}
}
实践
Qwen3-Thinking模型
看上去解决问题思路没有问题,但实际还是不能解决ChatClient空指针问题。
基于Trae+Germin2.5 Flash模型对话如下:
我们查看修改后代码,并不理想
@RestController
@RequestMapping(“/tools”)
public class ToolController {
private final AiService aiService;
public ToolController(AiService aiService, ChatService chatService) {
this.aiService = aiService;
}
/**
* 使用阿里云对话服务(AMAP)进行文本对话
*
* @param prompt 用户输入的对话内容
* @return ResponseEntity 包含对话结果的HTTP响应
*/
@GetMapping(“”)
public ResponseEntity<String> amap(String prompt) {
return aiService.getAiResponse(prompt);
}
}
Claude4
存在如下问题,大模型的知识还是跟不上组件更新
java: 找不到符号
符号: 类 CallPromptSpec
位置: 接口 org.springframework.ai.chat.client.ChatClient
TongYi Lingma-Qwen3-thinking再次尝试
请对当前类引用类型ChatClient进行完全隔离,ChatClient来自Spring AI框架,我们期望隔离它,并且可以替换为其他AI类库的实现,请基于OOP编程原则修改相关关联代码。
单元测试没有修复,第二轮对话修复单元测试问题
最终Qwen3达到期望的结果
/**
* 聊天客户端适配器接口
* 提供统一的方法调用规范,可扩展支持不同AI框架的实现
*/
public interface ChatAdapter {
/**
* 执行AI对话请求
* @param prompt 用户输入内容
* @return 返回处理结果
*/
String chat(String prompt);
}
/**
* Spring AI框架适配器实现
*/
@Component
public class SpringAiChatAdapter implements ChatAdapter {
private final ChatClient chatClient;
public SpringAiChatAdapter(ChatClient chatClient) {
this.chatClient = chatClient;
}
@Override
public String chat(String prompt) {
return chatClient.prompt(prompt).call().content();
}
}
@Service
public class TaskMcpCallServerServices implements AiService {
private final ChatAdapter chatAdapter;
/**
* Constructs a new TaskMcpCallServerServices instance.
* @param chatAdapter the ChatAdapter instance to use
*/
public TaskMcpCallServerServices(ChatAdapter chatAdapter) {
this.chatAdapter = chatAdapter;
}
@Override
/**
* Gets AI response for the given prompt by calling the MCP server.
* @param prompt the input prompt to send to the AI
* @return ResponseEntity containing the AI response
*/
public ResponseEntity<String> getAiResponse(String prompt) {
String response = this.chatAdapter.chat(prompt);
return ResponseEntity.ok(response);
}
}
单元测试代码
@ExtendWith(MockitoExtension.class)
class TaskMcpCallServerServicesTest {
@Mock
private ChatAdapter chatAdapter;
@InjectMocks
private TaskMcpCallServerServices taskMcpCallServerServices;
@BeforeEach
public void before()
{
MockitoAnnotations.openMocks(this);
taskMcpCallServerServices = new TaskMcpCallServerServices(chatAdapter);
}
@Test
void testGetAiResponse() {
// Given
String testPrompt = "test prompt";
String expectedResponse = "test response";
when(chatAdapter.chat(testPrompt)).thenReturn(expectedResponse);
// When
ResponseEntity<String> actualResponse = taskMcpCallServerServices.getAiResponse(testPrompt);
// Then
assertEquals(expectedResponse, actualResponse.getBody());
}
}
Trae中Germin 2.5 Flash
新生成文件,编译不通过,并且多次修复未果
Trae基于DeepSeek V3 0324实践
存在单元测试问题,第二轮修复单元测试成功
/**
* Interface defining the contract for AI client implementations.
* Provides abstraction for different AI service providers.
*/
public interface AiClient {
/**
* Gets AI-generated response for the given prompt.
* @param prompt the input text to send to the AI service
* @return the AI response content
*/
String getResponse(String prompt);
}
/**
* Service class for handling MCP server calls and AI responses.
* Implements AiService interface to provide AI response generation functionality.
*/
@Service
public class TaskMcpCallServerServices implements AiService {
private final AiClient aiClient;
/**
* Constructs a new TaskMcpCallServerServices instance.
* @param aiClient the AI client implementation
*/
public TaskMcpCallServerServices(AiClient aiClient) {
this.aiClient = aiClient;
}
@Override
/**
* Gets AI response for the given prompt by calling the MCP server.
* @param prompt the input prompt to send to the AI
* @return ResponseEntity containing the AI response
*/
public ResponseEntity<String> getAiResponse(String prompt) {
String response = this.aiClient.getResponse(prompt);
return ResponseEntity.ok(response);
}
}
直接让Claude4 重构
重构基本是成功的,还使用简单工厂,但新生成UnitTest依赖的API存在版本问题,最终效果如下
总结
一、AI编程在OOP场景下的探索意义
1. 代码生成与模式识别的范式突破
- 自动化模式实现:AI可通过分析海量开源代码库,自动生成符合设计模式的类结构(如工厂模式、单例模式),减少开发者对模式记忆的依赖。例如,GitHub Copilot已能根据注释生成完整的策略模式实现。
- 语义化代码补全:基于上下文的代码生成超越了传统IDE的语法补全,能理解“实现一个可序列化的订单对象”这类自然语言需求,直接生成符合OOP原则的类定义。
2. 重构与演化的智能辅助
- 架构漂移检测:AI可分析类之间的耦合度、继承层次深度等指标,量化代码异味(Code Smell),辅助决策是否需要引入依赖注入或抽象层。
- API演进预测:通过学习类库的历史更新日志,AI能预测未来版本可能弃用的方法,提前建议开发者使用适配器模式进行兼容性封装。
3. 领域特定语言(DSL)的生成
- OOP到DSL的映射:AI可将通用OOP结构转化为特定领域的DSL(如金融风控规则引擎),通过组合策略模式与状态模式,自动生成可配置的业务规则类。
二、版本API知识更新滞后的挑战
1. 知识衰减的典型场景
- 方法弃用链:例如,Java中
java.util.Date
到java.time
包的迁移,涉及SimpleDateFormat
到DateTimeFormatter
的替换,但AI可能因训练数据滞后继续推荐旧API。 - 语义化变更:Python 3.10中
collections.abc
模块的调整,导致直接继承collections.MutableSequence
的类需要修改导入路径。
2. 上下文提示词的局限性
- 局部最优陷阱:当开发者询问“如何实现一个线程安全的队列”,AI可能仅推荐
queue.Queue
而忽略项目已依赖的第三方库(如deque
的线程安全封装)。 - 隐式依赖缺失:若类库A的v2版本移除了对类库B的兼容层,AI可能无法从代码上下文中推断出这种跨版本依赖关系。
三、专家级综合判断的必要性
1. 领域知识的不可替代性
- 业务逻辑映射:例如,在金融系统中,专家能判断某个API变更是否影响交易回滚逻辑,而AI可能仅关注语法正确性。
- 历史债务权衡:专家可评估重构成本与收益,决定是立即迁移到新API还是通过适配器模式暂时兼容。
2. 认知推理链的构建
- 因果推断:当AI建议使用新API时,专家会追问“该API在分布式环境下的线程安全性如何?”“是否有已知的性能回退案例?”
- 反事实分析:专家可模拟“如果采用备选方案B,未来升级到C版本时是否需要二次重构?”
四、人机协同的进化路径
1. 增强型提示工程
- 多模态输入:将API文档、提交历史、Issue跟踪系统数据融入上下文,例如提示词中包含“此方法在v2.1中标记为@Deprecated,但Issue #1234显示v3.0将恢复”。
- 动态知识注入:通过插件机制实时更新本地知识库,如将Maven仓库的最新版本元数据作为上下文补充。
2. AI与专家的协作范式
- 建议-验证循环:AI生成候选方案,专家通过批判性思维筛选(如“此方案是否违反里氏替换原则?”)。
- 可解释性增强:要求AI输出决策树(如“选择该API是因为其支持泛型,而旧版本仅处理Object类型”)。
3. 持续学习的工程实践
- 自动化测试用例生成:针对API变更,AI可生成覆盖边界条件的单元测试,专家只需验证测试用例的有效性。
- 金丝雀发布策略:在开发分支先部署AI建议的代码,通过监控日志和性能指标,由专家决定是否合并到主分支。
五、未来展望:自适应OOP系统
最终,OOP与AI的融合将走向自适应软件系统:
- 动态类加载:根据运行时环境自动选择API版本(如开发环境用最新版,生产环境用稳定版)。
- 演化式设计:类结构不再静态定义,而是通过AI持续优化继承链和组合关系,类似生物体的自然选择过程。
这一进程要求开发者从“代码编写者”转型为“系统架构师+AI训练师”,在OOP的抽象层次与AI的生成能力之间构建新的认知桥梁。