时间依赖变量最佳实践:多时长单位值对象优化方案咨询
现有实现的缺陷
- 同时维护
duration/interval/frequency三个冗余字段,所有setter都要手动同步三个字段的值,一旦某个setter漏写同步逻辑、或者后续新增字段时没改全所有setter,就会出现字段值不一致的脏数据 - getter中带懒加载缓存逻辑,会在读取时修改对象内部状态,多线程共享VO实例时存在并发风险,而时间单位转换本身开销极低,完全没必要做缓存
- 毫秒转秒的四舍五入逻辑
(xxx + 500)/1000在多处重复编写,后续如果要调整取整规则(比如改成向上取整、向下取整)需要修改多个位置,容易漏改 - 如果序列化框架通过反射直接读取字段而非调用getter,会因为字段未初始化拿到null值,兼容性差
优化实现方案
核心原则:只保留一个权威的底层存储字段,其余所有时间单位的表示都通过getter实时计算,不做冗余存储,从根源上避免多字段同步问题。
结合你的场景,选择毫秒级Long类型值作为唯一的内部存储是最优选择:它本身就是序列化要求的Long类型,和Duration的转换是JDK原生支持的无额外开销,转秒的逻辑也可以统一收敛。
优化后的代码如下:
import java.time.Duration; public class PeriodicCommandVO { private String id; private String command; // 唯一权威存储:毫秒级时间间隔,所有其他时间维度的取值都从该字段计算 private Long intervalMillis; private boolean active; // 给实时引擎提供的Duration类型视图 public Duration getDuration() { return intervalMillis == null ? null : Duration.ofMillis(intervalMillis); } public void setDuration(Duration duration) { this.intervalMillis = duration == null ? null : duration.toMillis(); } // 给序列化逻辑提供的毫秒值视图 public Long getInterval() { return intervalMillis; } public void setInterval(Long interval) { this.intervalMillis = interval; } // 给JSP配置页提供的秒级整数视图,四舍五入逻辑统一在这里处理 public Long getFrequency() { if (intervalMillis == null) { return null; } return (intervalMillis + 500) / 1000; } public void setFrequency(Long frequencySeconds) { this.intervalMillis = frequencySeconds == null ? null : frequencySeconds * 1000; } // 其余普通字段的getter/setter保持原有逻辑不变 public String getCommand() { return command; } public void setCommand(String command) { this.command = command; } public boolean isActive() { return active; } public void setActive(boolean active) { this.active = active; } public String getId() { return id; } public void setId(String id) { this.id = id; } }
这个实现的优势:
- 无冗余字段,不存在多字段值不一致的可能,不管通过哪个setter传入时间值,底层只有一个数据源
- 所有转换逻辑完全收敛,比如要调整毫秒转秒的取整规则、新增分钟/小时等其他时间单位的视图,只需要修改对应getter/setter,不需要动现有逻辑
- 读操作无副作用,不存在懒加载带来的隐式状态修改,多线程场景下安全性更高
- 完全兼容原有使用逻辑:实时引擎调用
getDuration()、序列化逻辑读interval字段、JSP读frequency字段的行为和原来完全一致,上层代码不需要做任何修改
如果你用Jackson等JSON序列化框架,不需要序列化
duration/frequency字段的话,直接在这两个字段对应的getter上加@JsonIgnore注解即可,不会影响其他场景的取值。
内容的提问来源于stack exchange,提问作者xrags
相关产品推荐
相关产品推荐

