简单工厂不是设计模式,因其未被《设计模式》原书收录,仅是实用工具类,违反开闭原则;而工厂方法模式通过子类决定实例化,符合设计模式标准。

为什么简单工厂不是设计模式
因为 SimpleFactory 没有被《设计模式》原书收录——它只是个实用工具类,不满足“开闭原则”:每加一个产品类,就得改工厂的 if-else 或 switch。真正的工厂方法模式靠子类决定实例化谁,把创建逻辑“推”给继承体系。
常见错误现象:NullPointerException 出现在工厂返回 null 后没校验;或硬编码字符串(如 "mysql")导致拼错时运行时报 IllegalArgumentException。
- 使用场景:简单工厂适合内部工具类、原型阶段、产品类型极少(≤3)且几乎不变的情况
- 工厂方法更适合框架扩展点,比如日志组件支持
Log4jLogger、Slf4jLogger,新增实现只需加子类,不碰原有工厂代码 - 参数差异:简单工厂通常用字符串/枚举做参数;工厂方法模式中,参数往往被“上移”到子类构造或配置里,工厂接口本身可能无参
怎么写一个不暴露 new 的工厂方法
关键不是“怎么 new”,而是“谁来 new”。工厂方法把 new 藏进子类,父类只定义契约。
示例:定义 LoggerFactory 抽象类,含抽象方法 createLogger();子类 Log4jLoggerFactory 重写它并返回 new Log4jLogger()。客户端只依赖 LoggerFactory,运行时由具体子类决定实例化哪个日志实现。
立即学习“Java免费学习笔记(深入)”;
- 容易踩的坑:在抽象工厂类里写默认
new实现——这等于退化成简单工厂,违背了模式本意 - 性能影响:工厂方法本身无额外开销,但若子类工厂初始化成本高(如加载配置、连接池),需考虑单例包装或延迟初始化
- 兼容性注意:如果基类工厂方法签名后期要加参数,所有子类都得改——不如一开始用
Map<String, Supplier<T>>配置化注册,更灵活
简单工厂里用 if-else 还是 Map 查找
用 Map。硬编码 if-else 在产品变多时难维护,且无法热插拔。
示例:用 Map<String, Supplier<Product>> 预注册,get("pdf") 返回对应 Supplier,调用 get() 得实例。比反射安全,比 Class.forName() 快,也避免 NoClassDefFoundError。
- 常见错误:
Map未初始化就直接get(),结果返回null,后续调用get()抛NullPointerException - 使用场景:当产品类已知、数量适中(5–20)、不需要运行时动态注册时,
Map方案最轻量 - 注意线程安全:如果工厂被多线程并发使用,
HashMap要换成ConcurrentHashMap,或用static块初始化后只读
工厂类该不该用 Spring 的 @Bean 替代
可以,但要看控制权在哪。Spring 的 @Bean 本质是容器托管的工厂方法,但它把“谁创建”和“何时创建”交给了框架。
如果你需要根据运行时参数(比如 HTTP 请求头里的 X-Client-Type)决定创建哪个实现,纯 @Bean 不够用——得配合 @Scope("prototype") + ObjectProvider 或自定义 FactoryBean。
- 容易踩的坑:直接在
@Bean方法里写if (type.equals("A")) return new A(); else return new B();——这又写回了简单工厂,且失去 Spring 生命周期管理优势 - 性能影响:Spring 容器启动时会预实例化单例
@Bean,若工厂创建对象开销大,应设为@Scope("prototype")并按需获取 - 兼容性注意:脱离 Spring 环境(如单元测试、命令行工具)时,这类工厂不可用,必须保留原始工厂接口作为 fallback
真正复杂的地方不在“怎么写工厂”,而在于“谁该持有工厂实例”——是静态工具类?是 Spring 管理的 bean?还是传入构造函数的依赖?选错会导致测试难写、耦合变紧、扩展路径被堵死。










