buguge - Keep it simple,stupid

知识就是力量,但更重要的,是运用知识的能力why buguge?

导航

别滥用面向对象设计(OOD)中的继承:从一则简单的回调工具类的重构案例,来看OOP/OOD原则

博客园支持 mermaid 语法了,赞一个👍🏻@博客园团队💯


案例背景:支付回调系统的设计

在支付网关系统中,回调处理是核心功能之一。我们分析以下三个关键类:

classDiagram class LevyCallback { <<interface>> +rechargeCallBack() +paymentCallBack() +signCallBack() ... } class LevyCallbackCommon { +readString() static +render() static } class LevyCallbackImpl { -levyWithdrawalFlowSuccessApi -levyPaymentSuccessApi ... +rechargeCallBack() +paymentCallBack() ... } LevyCallbackImpl --|> LevyCallbackCommon LevyCallbackImpl ..|> LevyCallback

1. 接口定义:LevyCallback

public interface LevyCallback {
    void rechargeCallBack(Long levyId, HttpServletRequest request, HttpServletResponse response);
    void paymentCallBack(Long levyId, HttpServletRequest request, HttpServletResponse response);
    // 其他方法省略...
}

2. 工具类:LevyCallbackCommon

public class LevyCallbackCommon {
    protected LevyCallbackCommon() {}
    
    public static String readString(HttpServletRequest request) {
        try {
            return CallBackKit.readReqData(request);
        } catch (IOException e) {
            throw new ParameterException("解析请求报文失败");
        }
    }
    
    public static void render(HttpServletResponse response, String text) {
        // 实现省略...
    }
}

3. 核心实现类:LevyCallbackImpl

@Service
public class LevyCallbackImpl extends LevyCallbackCommon implements LevyCallback {
    @Override
    public void rechargeCallBack(Long levyId, HttpServletRequest request, HttpServletResponse response) {
        final String rawReqData = readString(request); // 继承自LevyCallbackCommon
        // 业务处理逻辑...
        render(response, result.getResult()); // 继承自LevyCallbackCommon
    }
    // 其他方法实现省略...
}

LevyCallbackCommon的设计缺陷

1. 违反继承关系的本质

// 问题代码示例
public class LevyCallbackImpl extends LevyCallbackCommon 
    implements LevyCallback {
    // 通过继承获得工具方法
    public void rechargeCallBack(...) {
        final String rawReqData = readString(request);
        render(response, result.getResult());
    }
}

设计问题

  • 工具类被错误地作为基类使用
  • 继承关系不满足"is-a"原则
  • 静态方法通过继承引入,破坏封装性

2. 破坏单一职责原则

LevyCallbackCommon承担了双重角色:

  • 作为工具类提供静态方法
  • 作为父类被继承(但未提供可扩展行为)

3. 错误使用继承而非组合

LevyCallbackImpl需要工具方法。但错误地继承了LevyCallbackCommon,导致紧耦合。正确的方式是,组合调用LevyCallbackCommon,从而实现松耦合。


重构方案:遵循OOP原则的改造

方案一:独立工具类(推荐)

LevyCallbackCommonrename为CallbackUtilsLevyCallbackImpl依赖调用CallbackUtils提供的工具方法。

public final class CallbackUtils {
    private CallbackUtils() {}
    
    public static String readString(HttpServletRequest request) {
        // 实现不变...
    }
    
    public static void render(HttpServletResponse response, String text) {
        // 实现不变...
    }
}

// 修改后的实现类
@Service
public class LevyCallbackImpl implements LevyCallback {
    @Override
    public void rechargeCallBack(...) {
        String data = CallbackUtils.readString(request);
        // 业务逻辑...
        CallbackUtils.render(response, result.getResult());
    }
}

方案二:内联工具方法

@Service
public class LevyCallbackImpl implements LevyCallback {
    // 私有静态工具方法
    private static String readString(HttpServletRequest request) {
        // 原实现...
    }
    
    private static void render(HttpServletResponse response, String text) {
        // 原实现...
    }
    
    @Override
    public void rechargeCallBack(...) {
        String data = readString(request);
        // 业务逻辑...
        render(response, result.getResult());
    }
}

OOP设计原则实践总结

1. 里氏替换原则(LSP)的启示

重构前问题

  • LevyCallbackImpl无法替代LevyCallbackCommon
  • 工具类作为父类没有提供可替换行为

合法的OOP继承关系示例

public abstract class AbstractCallback {
    protected abstract void process();
}

public class PaymentCallback extends AbstractCallback {
    @Override
    protected void process() {
        // 支付特定逻辑
    }
}

2. 组合优于继承(Composition over Inheritance)原则

重构本质

  • has-a(拥有工具方法)替代is-a(是工具类)
  • 通过依赖注入实现灵活组合

3. 单一职责原则(SRP)

职责划分

  • LevyCallback:定义回调契约
  • CallbackUtils:处理HTTP数据
  • LevyCallbackImpl:实现业务逻辑

4. 开闭原则(OCP)保障

// 扩展新回调处理器
public class RefundCallbackImpl implements LevyCallback {
    public void rechargeCallBack(...) {
        String data = CallbackUtils.readString(request);
        // 新的退款处理逻辑...
    }
}

最佳实践指南

  1. 工具类设计规范
  • final类
  • 私有化构造器
  • 所有方法必须是静态的
public final class XxxUtils {
    private XxxUtils() {} // 关键:私有构造器
    
    // 所有方法必须是静态的
    public static returnType methodName(params) {...}
}
  1. 继承适用性评估准则

结论:OOP思想的本质体现

通过本案例的重构,我们得到以下启示:

  1. 继承的本质建立类型层级关系。继承可以实现代码复用,但不要为了代码复用而滥用继承
  2. 多态的基础是接口契约,不是实现继承
  3. 工具类的定位应该是无状态的独立助手
  4. 类关系的设计需要严格遵循SOLID原则

Robert C. Martin在《Clean Code》中指出:

"继承是面向对象系统中最强大的工具之一,但也是最容易被滥用的工具。除非你真正需要多态行为,否则应该优先选择组合。"

本次重构不仅解决了具体的技术债务,更演示了如何通过OOP原则构建高内聚、低耦合的系统。这种设计思维可以帮助开发者创建更健壮、更易维护的应用系统。

posted on 2025-09-08 23:14  buguge  阅读(8)  评论(0)    收藏  举报