抽象类在重构中用于集中管理重复、稳定、可复用逻辑,共性归父类、个性留子类;需先识别三处以上一致代码(如校验、日志、字段声明等)再提取;善用final流程+钩子方法、protected构造器、清晰命名的抽象方法,避免构造器调用可重写方法。

抽象类在重构中不是用来“删代码”的,而是把重复、稳定、可复用的部分集中管理,让子类只专注差异点。关键不在于行数多少,而在于逻辑归属是否合理——共性归父类,个性留子类。
识别并提取真正重复的逻辑
别一上来就建 abstract 类。先找三处以上完全一致的代码块:字段声明、校验流程、日志记录、构造初始化、通用工具方法(如格式化时间、拼接参数)。这些就是可下沉的共性。
- 比如多个订单处理器都做「校验用户状态 → 检查库存 → 记录操作日志」,前两步固定,第三步因渠道不同而异,那就把校验和库存检查写进 abstract 类,只把「记录日志」定义为 abstract void logOperation()
- 字段如 protected String orderId;、protected LocalDateTime createTime; 直接声明在 abstract 类里,子类不用再重复声明
- 通用 getter/setter 或工具方法(如 isExpired()、formatAmount())直接实现,不 abstract
用 final + 钩子方法控制流程,防误覆盖
主业务流程要锁定,但允许子类在关键节点插入手脚。比如「创建订单」流程固定为「校验→锁库存→生成单号→保存→发消息」,其中只有「发消息」方式各异。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 把整个流程封装成 public final void createOrder(),内部调用 validate()、lockInventory()(已实现)、generateOrderNo()(已实现)、saveToDb()(已实现),最后调用 notifyAfterCreate()
- protected void notifyAfterCreate() { } 是钩子方法——子类按需重写,不重写就什么也不做,不强制实现
- 避免写 public void save() { db.insert(this); notifyEvent(); } 这种带副作用的具体方法,否则某个子类想跳过通知就得重写全部,反而破坏复用
构造器里只做安全初始化,不调用可重写方法
抽象类必须有构造函数,且推荐 protected。它负责初始化共用字段、设置默认状态,但绝不能在构造过程中调用 abstract 或可能被重写的 default 方法。
立即学习“Java免费学习笔记(深入)”;
- 正确做法:protected AbstractOrderProcessor(String channel) { this.channel = channel; this.status = INIT; }
- 错误做法:在构造器里调用 this.initConfig();(若该方法是 abstract 或 default,子类尚未完成初始化,this 处于半成品状态,容易读到 null 或 0)
- 子类构造器第一行必须显式 super(channel),确保父类字段被正确初始化
命名与契约要清晰,避免伪共性
抽象方法名不是占位符,而是子类必须履行的行为契约。如果多个子类实现里都在方法内做 if-else 分支判断,说明这个方法粒度太粗,本该由 abstract 类统一分发。
- 好名字:calculateDiscount()、serializeToXml()、buildPaymentRequest() —— 动作明确,意图清晰
- 坏名字:handle()、process()、doSomething() —— 模糊,导致子类各自加类型判断,违背抽象初衷
- 如果子类实现总要传额外参数(如 Context、Config),说明这个依赖应该提前放到 abstract 类的 protected 字段或初始化方法里,而不是塞进方法签名

















