Telegram Bot API项目类依赖设计合理性及优化方案咨询
一、当前实现是否正确?会引发递归异常吗?
当前实现不会触发初始化阶段的递归异常。实例化顺序是先创建BotService对象,完成自身核心属性初始化后,再通过new MessageService(this, userService)把已创建好的BotService实例传入MessageService构造器,两个对象的初始化流程能正常完成,不存在构造器互相调用导致的递归死循环。
至于运行时是否会出现递归异常,取决于MessageService调用botService.execute()后,BotService是否会再次调用MessageService的方法形成循环。如果execute()仅调用Telegram Bot API的底层操作(比如发送消息),不会触发MessageService的业务逻辑,就不会有递归问题。
二、当前设计模式是否合理?
这种双向依赖的设计耦合度偏高,存在两个明显问题:
- 可维护性差:后续修改
BotService或MessageService的实现时,很可能需要同步调整另一个类的代码; - 可测试性差:测试
MessageService时必须Mock完整的BotService实例,增加了测试复杂度。
三、更优方案建议
结合你的需求(逻辑集中在MessageService、仅调用BotService.execute()、单例、避免Spring注入高负载下的顺序问题),可以参考以下方案:
1. 接口解耦,依赖抽象
通过定义抽象接口降低类间耦合:
- 定义仅包含
execute()方法的接口:
public interface BotExecutor { void execute(/* 所需参数 */); }
- 让
BotService实现该接口:
public class BotService extends TelegramBot implements BotExecutor { private final MessageService messageService; private final UserService userService; public BotService(@Value("${token}") String token, UserService userService) { super(token); this.userService = userService; this.messageService = new MessageService(this, userService); } @Override public void execute(/* 参数 */) { // 原有execute逻辑 } }
- 修改
MessageService依赖抽象接口而非具体类:
public class MessageService { private final BotExecutor botExecutor; private final UserService userService; public MessageService(BotExecutor botExecutor, UserService userService) { this.botExecutor = botExecutor; this.userService = userService; } // 业务逻辑中调用botExecutor.execute() }
这种方式降低了耦合度,后续替换BotExecutor实现或测试MessageService时,仅需Mock接口即可。
2. 事件驱动,完全解耦
采用事件驱动彻底消除双向依赖:
- 定义业务事件类,携带
execute()所需参数:
public class BotExecutionEvent extends ApplicationEvent { private final /* 所需参数 */; public BotExecutionEvent(Object source, /* 参数 */) { super(source); // 初始化参数 } // 参数getter方法 }
MessageService处理完逻辑后发布事件:
public class MessageService { private final ApplicationEventPublisher eventPublisher; private final UserService userService; public MessageService(ApplicationEventPublisher eventPublisher, UserService userService) { this.eventPublisher = eventPublisher; this.userService = userService; } public void handleMessage(/* 参数 */) { // 处理业务逻辑 eventPublisher.publishEvent(new BotExecutionEvent(this, /* 参数 */)); } }
BotService监听事件并执行execute():
public class BotService extends TelegramBot { private final UserService userService; public BotService(@Value("${token}") String token, UserService userService) { super(token); this.userService = userService; } @EventListener public void handleBotExecutionEvent(BotExecutionEvent event) { // 获取事件参数并执行execute execute(/* 事件参数 */); } }
此方案下两个类完全解耦,各自专注自身职责,同时天然支持Spring单例管理,也能避免注入顺序问题。
补充:Spring环境下的单例优化
若想用Spring管理单例,可通过@Lazy注解解决循环依赖:
@Service public class BotService extends TelegramBot { private final MessageService messageService; private final UserService userService; public BotService(@Value("${token}") String token, UserService userService, @Lazy MessageService messageService) { super(token); this.userService = userService; this.messageService = messageService; } } @Service public class MessageService { private final BotService botService; private final UserService userService; public MessageService(BotService botService, UserService userService) { this.botService = botService; this.userService = userService; } }
@Lazy会延迟初始化MessageService,避免初始化阶段的循环依赖问题,同时由Spring保证单例特性。
内容的提问来源于stack exchange,提问作者Psyjewnaut

