如何实现含可变更数据类的OOP设计?Java CLI待办应用问询
首先明确:新增priority字段不需要修改所有与Task交互的逻辑,关键是在设计和扩展时遵循一些简单的实践,下面是适合新手的具体做法:
1. 给新增字段设置合理默认值,保留旧构造方法
在Task类中为新增的priority字段设置默认值(比如默认MEDIUM),同时保留原本的构造方法。这样旧代码在创建Task对象时,即使没有传入priority参数,也能正常运行,无需修改所有构造调用。
示例代码:
public class Task { private String title; private LocalDate dueDate; private boolean isDone; private Priority priority; // 新增字段 // 保留旧构造方法,兼容原有代码 public Task(String title, LocalDate dueDate, boolean isDone) { this(title, dueDate, isDone, Priority.MEDIUM); // 传入默认优先级 } // 新增带优先级参数的构造方法 public Task(String title, LocalDate dueDate, boolean isDone, Priority priority) { this.title = title; this.dueDate = dueDate; this.isDone = isDone; this.priority = priority; } // 省略getter/setter,仅需为priority新增getter,旧代码无需调用 } enum Priority { LOW, MEDIUM, HIGH }
2. 用构建者模式避免构造方法膨胀
如果后续还要添加更多字段(比如description、category),构造方法会越来越臃肿。使用构建者模式可以让对象创建更灵活,扩展字段时完全不需要修改旧的对象创建逻辑。
示例代码:
public class Task { private String title; private LocalDate dueDate; private boolean isDone; private Priority priority; // 新增字段 // 私有构造方法,仅允许通过Builder创建实例 private Task(Builder builder) { this.title = builder.title; this.dueDate = builder.dueDate; this.isDone = builder.isDone; this.priority = builder.priority != null ? builder.priority : Priority.MEDIUM; } public static class Builder { // 必填字段,在Builder构造时传入 private final String title; private final LocalDate dueDate; // 可选字段,自带默认值 private boolean isDone = false; private Priority priority = Priority.MEDIUM; public Builder(String title, LocalDate dueDate) { this.title = title; this.dueDate = dueDate; } // 可选字段的链式设置方法 public Builder isDone(boolean isDone) { this.isDone = isDone; return this; } public Builder priority(Priority priority) { this.priority = priority; return this; } // 生成Task实例 public Task build() { return new Task(this); } } }
旧代码创建Task的方式依然是new Task.Builder("买牛奶", LocalDate.now()).isDone(false).build();,只有需要用到优先级的场景,才需要添加.priority(Priority.HIGH),原有代码完全无需改动。
3. 隔离数据类的操作逻辑
把直接操作Task字段的逻辑集中到少数专门类中,比如负责CLI显示的TaskRenderer、负责存储的TaskRepository,不要让所有业务逻辑都直接访问Task的字段。这样新增字段后,只需要修改这些专门类,其他逻辑不受影响。
比如原本所有地方都自己拼接字符串展示任务,现在改成:
public class TaskRenderer { public String renderTask(Task task) { // 新增priority后,仅需修改这里的渲染逻辑 return String.format("[%s] %s (截止日期: %s) 优先级: %s", task.isDone() ? "X" : " ", task.getTitle(), task.getDueDate(), task.getPriority()); } }
其他业务逻辑只需要调用taskRenderer.renderTask(task),完全不用关心Task有没有新增字段。
4. 遵循开放-封闭原则(OCP)
对扩展开放,对修改关闭。新增字段属于扩展操作,尽量通过新增方法、新增类来支持,而非修改现有代码。比如新增priority后,不要去修改所有遍历任务的循环,而是新增一个filterByPriority方法,放在专门的TaskFilter类中。
总结
作为新手,不用一开始追求完美设计,先从保留旧构造方法+设置默认值入手,这是最简单的兼容方案;后续字段增多时,再引入构建者模式;同时尽量把数据类的操作逻辑集中到辅助类中,降低代码耦合度。这样后续扩展字段时,需要修改的范围会非常小。
内容的提问来源于stack exchange,提问作者albert_guy

