
本文介绍在无法修改类 a/b/c 继承结构的前提下,通过泛型抽象基类或函数式组合两种专业方案,彻底消除多类型读取器中 readx() 和 computex() 方法的重复逻辑,提升可维护性与可测试性。
本文介绍在无法修改类 a/b/c 继承结构的前提下,通过泛型抽象基类或函数式组合两种专业方案,彻底消除多类型读取器中 readx() 和 computex() 方法的重复逻辑,提升可维护性与可测试性。
在遗留系统中,当多个不相关类(如 A、B、C)需共用一套读取逻辑,但又无法统一继承父类或实现共同接口时,直接编写 readA()/readB()/readC() 会导致严重代码重复——尤其当 computeA()/computeB()/computeC() 内部还存在深层调用链和相似分支逻辑时,维护成本急剧上升。本文提供两种经过生产验证的重构路径:泛型抽象模板模式与函数式组合模式,均避免运行时类型检查,杜绝类型转换风险,并支持单元测试解耦。
✅ 方案一:泛型抽象基类(推荐用于逻辑高度一致场景)
通过定义泛型抽象类封装公共流程,将差异化计算逻辑延迟至子类实现,既保持类型安全,又消除重复:
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();
}
}
// 具体实现类(无需修改 A/B/C 定义)
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() {
// 仅关注 B 的特有构建逻辑
return new B();
}
}
// 使用方式(类型安全,无强制转换)
AReader aReader = new AReader();
A a = aReader.read(); // 编译期确定返回类型
BReader bReader = new BReader();
B b = bReader.read();关键优势:read() 方法复用率 100%,doLotsOfCommonThings() 可进一步提取为 protected 工具方法供子类复用;子类仅需专注领域逻辑,符合单一职责原则。
✅ 方案二:函数式组合(推荐用于逻辑差异较大或需深度解耦场景)
将公共行为抽离为独立服务类,通过 Function 注入具体计算策略,实现关注点分离与可测试性:
public class CommonReaderService {
// 封装所有跨类型通用操作(可独立测试)
public CommonContext doLotsOfCommonThings() {
System.out.println("Executing shared context setup...");
return new CommonContext(); // 返回上下文对象,供后续计算使用
}
// 泛型读取入口:接收类型专属计算函数
public <T> T read(Function<CommonContext, T> computeFn) {
CommonContext context = doLotsOfCommonThings();
return computeFn.apply(context);
}
}
// 具体读取器(依赖注入 CommonReaderService)
public class AReader {
private final CommonReaderService service;
public AReader(CommonReaderService service) {
this.service = service;
}
public A read() {
return service.read(this::computeA); // 方法引用传递计算逻辑
}
private A computeA(CommonContext context) {
// 基于 context 构建 A 实例(可调用 service 其他公共方法)
return new A();
}
}
public class BReader {
private final CommonReaderService service;
public BReader(CommonReaderService service) {
this.service = service;
}
public B read() {
return service.read(this::computeB);
}
private B computeB(CommonContext context) {
return new B();
}
}核心价值:CommonReaderService 可独立单元测试;AReader/BReader 仅负责领域逻辑,测试时可轻松 Mock CommonReaderService;计算逻辑完全解耦,支持 Lambda 表达式动态传入,灵活性更高。
⚠️ 注意事项与选型建议
- 避免泛型擦除陷阱:切勿在泛型方法中通过 instanceof 或 Class<T> 判断类型(如问题中反模式示例),这会破坏类型安全并引入运行时错误风险。
- 组合优于继承:若 A/B/C 的计算逻辑差异显著(如涉及不同外部 API 调用、数据源),优先选择方案二;若流程高度统一(如仅 DTO 字段映射差异),方案一更简洁。
- 渐进式重构:可先将 doLotsOfCommonThings() 提取为 protected 方法,再逐步迁移 computeX() 至抽象类或函数式接口,降低风险。
- 测试友好性:两种方案均天然支持 Mock 与隔离测试——方案一可 Mock 抽象基类的 compute(),方案二可直接注入 Mock 的 CommonReaderService。
通过上述任一方案,您不仅能消除 readA()/readB()/readC() 的重复,更能将深层 computeX() 链中的共性逻辑(如日志、重试、缓存)统一到基类或服务层,真正实现“一次编写,多处复用”。

















