如何在Java项目中使用多态优化if-else

策略模式通过统一接口和运行时路由替代if-else,确保调用方不感知具体类型,遵守开闭原则;需避免getType()等类型判断、合理使用Map/枚举优化性能,并严守多态边界。

用策略模式替代 if-else 判断类型

当代码中出现大量 if (obj instanceof A) { ... } else if (obj instanceof B) { ... } 或基于字符串/枚举的分支判断时,说明行为逻辑与类型强耦合,违反开闭原则。多态的核心价值不是“能调用不同方法”,而是“让调用方完全不知道具体类型”。策略模式是最直接的落地方式。

实操建议:

  • 定义统一接口(如 Handler),声明抽象行为方法(如 handle(Request req)
  • 为每种业务场景实现该接口(AHandlerBHandler),内部封装具体逻辑
  • MapMap, Handler> 做运行时路由,避免硬编码 if-else
  • 注册入口统一收口(如 Spring 的 @PostConstruct 方法或静态初始化块),防止漏注册

避免在多态链路中暴露类型判断

常见错误是把多态当成“更优雅的 if-else”:比如在 Handler 接口里加 getType() 方法,然后外面再写 if (h.getType() == X) {...}。这等于换汤不换药,依然需要修改调用方代码来支持新类型。

关键约束:

  • 调用方只持有接口引用,不调用任何用于判断类型的 getter 或 getClass()
  • 所有分支逻辑必须下沉到具体实现类内部,例如 AHandler.handle() 自己决定怎么处理 req.getSubType()
  • 如果必须根据子字段分支,优先考虑将该字段也抽象为策略参数,而非回退到 if-else

Spring 中自动装配策略集合的坑

@Autowired private List handlers 获取所有实现类很常见,但容易忽略两个问题:顺序不确定、无法按需加载。

解决方案:

  • 给每个 Handler 实现类加 @Order 注解或实现 Ordered 接口,确保执行顺序可控(如兜底 handler 放最后)
  • 不要直接遍历 handlers 全量调用,而应先用 req.getType() 查找匹配项:
    Handler handler = handlers.stream()
        .filter(h -> h.supports(req))
        .findFirst()
        .orElseThrow(() -> new UnsupportedOperationException("no handler for " + req.getType()));
  • supports() 方法必须轻量,禁止做 IO 或复杂计算;否则应改用 Map 预注册方式

性能敏感场景下避免反射或 Map 查找

高频调用路径(如网关 filter、RPC 序列化)中,每次都要从 MapList 中查找 handler 会产生额外开销。这时可结合枚举+数组索引提升性能。

示例结构:

public enum HandlerType {
    PAY, REFUND, TRANSFE

R; private static final Handler[] HANDLERS = new Handler[values().length]; static { HANDLERS[PAY.ordinal()] = new PayHandler(); HANDLERS[REFUND.ordinal()] = new RefundHandler(); // ... } public Handler getHandler() { return HANDLERS[this.ordinal()]; } }

注意点:

  • 枚举值顺序不能随意调整,否则 ordinal() 错位会导致调用错乱
  • 新增类型必须同步更新 static 块,CI 可加单元测试校验枚举数与数组长度一致
  • 这种方式牺牲了动态扩展性,仅适用于业务模型稳定、变更频率极低的场景

真正难的不是写出多态代码,而是守住“调用方不感知实现类”这条线——一旦出现 instanceof、类型强转、或为了 debug 加的 System.out.println(obj.getClass()),就说明多态被架空了。