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

用策略模式替代 if-else 判断类型
当代码中出现大量 if (obj instanceof A) { ... } else if (obj instanceof B) { ... } 或基于字符串/枚举的分支判断时,说明行为逻辑与类型强耦合,违反开闭原则。多态的核心价值不是“能调用不同方法”,而是“让调用方完全不知道具体类型”。策略模式是最直接的落地方式。
实操建议:
- 定义统一接口(如
Handler),声明抽象行为方法(如handle(Request req)) - 为每种业务场景实现该接口(
AHandler、BHandler),内部封装具体逻辑 - 用
Map或Map做运行时路由,避免硬编码, Handler> if-else - 注册入口统一收口(如 Spring 的
@PostConstruct方法或静态初始化块),防止漏注册
避免在多态链路中暴露类型判断
常见错误是把多态当成“更优雅的 if-else”:比如在 Handler 接口里加 getType() 方法,然后外面再写 if (h.getType() == X) {...}。这等于换汤不换药,依然需要修改调用方代码来支持新类型。
关键约束:
立即学习“Java免费学习笔记(深入)”;
- 调用方只持有接口引用,不调用任何用于判断类型的 getter 或
getClass() - 所有分支逻辑必须下沉到具体实现类内部,例如
AHandler.handle()自己决定怎么处理req.getSubType() - 如果必须根据子字段分支,优先考虑将该字段也抽象为策略参数,而非回退到 if-else
Spring 中自动装配策略集合的坑
用 @Autowired private List 获取所有实现类很常见,但容易忽略两个问题:顺序不确定、无法按需加载。
解决方案:
- 给每个
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 序列化)中,每次都要从 Map 或 List 中查找 handler 会产生额外开销。这时可结合枚举+数组索引提升性能。
示例结构:
public enum HandlerType {
PAY, REFUND, TRANSFER;
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()),就说明多态被架空了。










