Java工厂模式与创建模式实现技巧

工厂方法返回null或ClassCastException的主因是子类未正确重写createProduct()或返回类型不符;应使用泛型返回、安全转换、严格契约验证及类型测试。

工厂方法为什么总返回 null 或 ClassCastException

常见原因是子类工厂没正确重写 createProduct(),或返回了错误类型。比如抽象工厂接口声明返回 Product,但具体工厂返回了 LegacyProduct,而调用方强转成 ModernProduct —— 运行时就抛 ClassCastException

实操建议:

  • 在抽象工厂中把返回类型设为泛型: T createProduct(Class type),配合 type.cast(...) 做安全转换
  • 所有具体工厂必须确保返回实例满足接口契约,别绕过编译检查写 return (ModernProduct) new LegacyProduct()
  • 单元测试里对每个工厂显式验证返回值的 instanceof 类型,不只测是否非空

SimpleFactory 和 FactoryMethod 混用导致扩展困难

很多人一开始用静态 SimpleFactory.create(String type),后来加新类型就往里塞

if ("v2".equals(type)) 分支——这违反开闭原则,也难做 Spring Bean 管理。

实操建议:

  • 从项目初期就用 FactoryMethod:让每个产品族有独立工厂类(如 PaymentFactoryNotificationFactory),通过 Spring 的 @Qualifier 注入具体实现
  • 避免在工厂内部硬编码判断逻辑;改用配置驱动:读取 application.properties 中的 payment.strategy=alipay,再查 Map 容器获取实例
  • 如果真需要简单工厂,至少把它变成非静态、可被继承的类,并预留 protected 钩子方法供子类定制创建逻辑

抽象工厂中产品族耦合引发测试失败

AbstractFactory 同时创建 ButtonCheckbox,但测试时只 mock 其中一个,另一个却依赖真实实现——结果 UI 组件初始化失败,或出现 NPE。

实操建议:

  • 抽象工厂的每个创建方法应彼此独立,不要在 createButton() 里调用 createCheckbox();否则破坏单一职责,也增加测试隔离难度
  • 使用构造注入替代工厂内直接 new:把 ButtonFactoryCheckboxFactory 作为参数传入抽象工厂构造器,方便单元测试替换
  • 考虑用 Builder 模式收口复杂对象创建,把工厂退化为“策略选择器”,例如 UiComponentBuilder.forTheme("dark").withButtonStyle("rounded")
public interface ProductFactory {
    T create(Class clazz);
}

public class JsonProductFactory implements ProductFactory {
    @Override
    public JsonProduct create(Class clazz) {
        return clazz.cast(new JsonProduct()); // 安全转型
    }
}
工厂模式真正的难点不在结构图,而在边界控制——什么时候该让工厂知道“怎么造”,什么时候只让它决定“造哪个”。漏掉这个分寸,再多的 abstractinterface 都会变成套娃式维护负担。