
本文介绍在无法修改类继承结构的前提下,通过泛型抽象基类或函数式组合两种专业方案,统一 reada()/readb()/readc() 等重复逻辑,显著提升可维护性与可测试性。
本文介绍在无法修改类继承结构的前提下,通过泛型抽象基类或函数式组合两种专业方案,统一 reada()/readb()/readc() 等重复逻辑,显著提升可维护性与可测试性。
当面对 A、B、C 三个互不相关(无法共享父类或接口)的类,却需复用大量读取逻辑(如初始化、校验、资源准备等)时,传统多方法冗余实现(readA/readB/readC)会导致严重的代码腐化——不仅 readX() 层级存在重复,其调用链深处的 computeX() 及辅助函数也持续复制粘贴,难以维护和扩展。
此时,强行使用运行时类型判断的泛型方法(如 read(Class
✅ 推荐方案一:泛型抽象模板类(Template Method + Generics)
通过定义泛型抽象基类,将公共流程固化在 read() 模板方法中,而将类型特异逻辑延迟至子类实现,既保持类型安全,又彻底消除重复:
public abstract class AbstractReader<t> {
protected void doLotsOfCommonThings() {
// 所有读取操作共用的前置逻辑:日志、连接初始化、缓存清理等
System.out.println("Executing common setup...");
}
protected abstract T compute(); // 子类提供具体构造逻辑
public final T read() { // 模板方法:不可重写,保证流程一致性
doLotsOfCommonThings();
return compute();
}
}
// 具体实现类 —— 轻量、专注、无重复
public class AReader extends AbstractReader<a> {
@Override
protected A compute() {
// 仅关注 A 的构造细节,复用基类公共逻辑
return new A(); // 或调用专有 helper 方法
}
}
public class BReader extends AbstractReader<b> {
@Override
protected B compute() {
return new B();
}
}</b></a></t>
✅ 优势:编译期类型安全、开销为零、符合开闭原则;若 compute() 内部仍有共用逻辑(如解析 JSON 字段),可进一步提取为 AbstractReader 中的 protected 辅助方法供子类复用。
✅ 推荐方案二:函数式组合(Composition + Lambda)
当需要更高灵活性(如动态组合不同策略)或强调可测试性时,采用「职责分离」思想:将通用能力封装为独立服务,再由具体读取器通过函数式接口注入行为:
public class ThingDoer {
// 封装所有跨类型的通用能力
public CommonResult doCommonPreparation() {
System.out.println("Doing universal prep...");
return new CommonResult();
}
// 核心组合点:接受任意类型转换函数
public <t> T read(Function<commonresult t> computeFn) {
CommonResult result = doCommonPreparation();
return computeFn.apply(result);
}
}
// 具体读取器仅负责自身领域逻辑,完全解耦
public class AReader {
private final ThingDoer thingDoer;
public AReader(ThingDoer thingDoer) {
this.thingDoer = thingDoer;
}
public A read() {
return thingDoer.read(this::computeA); // 方法引用,类型推导精准
}
private A computeA(CommonResult result) {
// 仅实现 A 特有的构建逻辑
return new A();
}
}</commonresult></t>
✅ 优势:ThingDoer 可独立单元测试;AReader/BReader 彼此隔离,互不影响;易于替换 compute 行为(如测试时注入 mock 函数);天然支持策略模式演进。
? 关键注意事项
-
避免泛型滥用:不要为“统一入口”而牺牲类型安全——read(Class
) 是反模式,应让编译器而非运行时决定类型。 - 优先组合,慎用继承:若 A/B/C 语义无关,强制继承抽象类可能违反里氏替换原则;此时组合方案更自然。
- 共用逻辑下沉:无论选择哪种方案,将 doLotsOfCommonThings() 及其依赖的底层 helper 方法统一抽取到共享层(基类或工具类),是消除深层重复的根本。
- 构造方式对齐业务:new AReader(new ThingDoer()) 显式依赖注入利于测试;生产环境可配合 Spring 等容器管理生命周期。
最终,重构目标不是“减少方法数量”,而是将变化点(type-specific logic)与稳定点(common workflow)清晰分离。两种方案均达成此目标,开发者可根据团队技术栈偏好与架构约束灵活选用。











