
本文介绍如何通过定义公共接口 WithId,消除重复的 getEntityId 和 getEntityDTOId 方法,实现类型安全、可扩展且符合 SOLID 原则的通用 ID 提取逻辑。
本文介绍如何通过定义公共接口 `withid`,消除重复的 `getentityid` 和 `getentitydtoid` 方法,实现类型安全、可扩展且符合 solid 原则的通用 id 提取逻辑。
在 Java 开发中,实体类(Entity)与数据传输对象(EntityDTO)常具有相同结构字段(如 id),但因类型不同导致业务逻辑重复——例如分别编写 getEntityId(Entity) 和 getEntityDTOId(EntityDTO) 方法。这种冗余不仅增加维护成本,还违背“DRY”(Don’t Repeat Yourself)原则。幸运的是,无需依赖复杂泛型约束或反射,仅需一个轻量级接口即可优雅解决。
✅ 推荐方案:使用标记接口 WithId
定义一个简洁的函数式接口,声明统一的 getId() 行为:
public interface WithId {
Integer getId();
}随后让相关类显式实现该接口(注意添加 @Override 提升可读性与编译期校验):
public class Entity implements WithId {
private Integer id;
public Entity(Integer id) { this.id = id; }
@Override
public Integer getId() {
return id;
}
}
public class EntityDTO implements WithId {
private Integer id;
public EntityDTO(Integer id) { this.id = id; }
@Override
public Integer getId() {
return id;
}
}此时,EntityProcessing 类可彻底重构为单一、类型安全的通用方法:
public class EntityProcessing {
// ✅ 一个方法,支持所有 WithId 实现类
public Integer getId(WithId withId) {
return withId.getId();
}
}调用示例:
Entity entity = new Entity(1001); EntityDTO dto = new EntityDTO(2002); EntityProcessing processor = new EntityProcessing(); System.out.println(processor.getId(entity)); // 输出: 1001 System.out.println(processor.getId(dto)); // 输出: 2002
⚠️ 注意事项与设计权衡
为什么优先选接口而非抽象类?
Java 单继承机制限制一个类只能 extends 一个父类,但可 implements 多个接口。若未来需同时支持 id、createdDate、version 等通用字段,只需新增 WithCreatedDate、WithVersion 等接口并组合实现,灵活且无耦合风险。-
抽象类方案(不推荐为主流)
虽然可通过抽象基类复用字段与方法(如下),但会过早锁定继承链,降低类的设计自由度:public abstract class WithId { private Integer id; public Integer getId() { return id; } } // public class Entity extends WithId { ... } —— 此时无法再继承其他业务基类 泛型不是必需解
本场景无需这类泛型签名——因为目标是行为统一而非类型参数化。直接使用接口作为参数类型更简洁、语义更清晰,且完全兼容多态调用。
✅ 总结
通过 WithId 接口统一契约,你获得的不仅是代码行数的减少,更是面向接口编程思想的落地:
? 可扩展:新增任意 XxxDTO 或 XxxVO 只需 implements WithId;
? 可测试:getId(WithId) 方法可被任意模拟对象注入,单元测试零耦合;
? 可演进:后续可配合 default 方法添加通用逻辑(如 isIdValid()),或结合 Spring 的 @Valid 进行统一校验。
这一设计小而精,是领域模型解耦与 API 层职责清晰化的典型实践。









