Java OOP设计如何避免canAttendEvent重复调用且符合编码最佳实践?
问题解答
1. 当前写法是否符合OOP最佳实践?
不符合,主要存在两个核心问题:
- 违反Tell, Don't Ask原则:外部调用方需要先主动查询
canAttendEvent的状态,再决定是否调用attendEvent,将本应属于Person类内部的控制逻辑暴露给了外部,也破坏了Person类的封装性。 - 存在不必要的性能损耗与并发安全隐患:两次
canAttendEvent遍历是冗余开销,更严重的是如果是多线程场景,两次调用之间可能有其他线程修改了eventsToAttend列表,导致校验结果失效,最终加入冲突活动。
2. 用try-catch包裹attendEvent是否属于不良实践?
要看冲突场景的出现频率:
- 如果日程冲突属于极罕见的异常情况(比如只有系统bug才会触发,正常用户操作不可能遇到),那抛出非受检异常、用try-catch处理是合理的,符合异常的设计语义。
- 如果日程冲突是正常业务场景下的常见情况(比如用户手动选择了和已有日程冲突的活动),那这种用法就属于“用异常做流程控制”的不良实践:异常本身的栈生成会带来额外性能开销,也不符合“正常业务分支用返回值处理”的约定。
3. 推荐的实现方案
方案1:修改attendEvent返回布尔值,符合Tell Don't Ask原则
最推荐的方案,内部只做一次校验,外部不需要提前查询,直接调用方法即可,代码示例如下:
public class Person { private ArrayList<Event> eventsToAttend = new ArrayList<>(); // 仅在需要提前给用户做提示的场景保留该方法,不需要可以删掉 public boolean canAttendEvent(Event newEvent) { for (Event existing : eventsToAttend) { if (newEvent.isSameDayAndTime(existing)) { return false; } } return true; } // 直接返回添加结果,内部仅校验一次 public boolean attendEvent(Event newEvent) { if (canAttendEvent(newEvent)) { eventsToAttend.add(newEvent); return true; } return false; } public static void main(String[] args) { Person person = somePersonWithEventsAlready; Event event = new Event(); // 直接调用,一次校验即可 boolean attendSuccess = person.attendEvent(event); if (!attendSuccess) { // 处理冲突逻辑 } } }
方案2:需要提前校验场景的优化
如果确实有需要提前判断能否参加的场景(比如前端选活动时实时提示冲突),可以保留canAttendEvent方法,不需要刻意优化两次调用的开销:普通用户的日程列表最多也就几百条,遍历的性能损耗可以忽略不计。注意attendEvent内部的校验是为了保证类的状态合法性,必须保留,不能为了省一次遍历去掉内部校验导致类的状态出错。
内容的提问来源于stack exchange,提问作者hkcode
相关产品推荐
相关产品推荐

