首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >重温工厂设计模式,觉得这模式好笨好蠢好低效好耿直,好需要一个Spring IoC 容器

重温工厂设计模式,觉得这模式好笨好蠢好低效好耿直,好需要一个Spring IoC 容器

作者头像
烟雨平生
发布2026-04-14 18:25:53
发布2026-04-14 18:25:53
260
举报

都说温故而知新。重温一遍工厂设计模式,觉得工厂模式的思路很清晰,落地好耿直、好低效。现在讲下自己的理解,欢迎拍砖。

为什么要用工厂,直接new不好吗?工厂中也会用到new。在技术上,new没有错,这是Java的基础部分。讨论工厂设计模式,有没有用new不是重点,重点在于这个设计是否解耦、是否符合设计原则:类应该对扩展开放,对修改关闭,即开闭原则。

工厂都是用来封装对象的创建,以便将代码从具体类解耦。

那么下面这个是GoF书中的工厂模式吗?

不是的。这个“套路”有污点,不满足开闭原则,无缘“设计模式”的编制。但这仍然是一个不错的编程技巧。

为什么呢?上面的代码的确解耦了类的使用与类的创建,但增加一个手机类型,就要修改com.pattern.factory.PhoneSimpleFactory#createPhone这个静态方法,没有对修改关闭,即不符合开闭原则。这种代码设计只是把一个问题从类的使用者移到一个静态方法中而已。

那么GoF讲的工厂模式是?GoF23种设计模式中有两种工厂方法模式(Factory Method Pattern)和抽象工厂模式(Abstract Factory Pattern)。上面的代码虽然在实际项目用得多,的确也有解耦的意图,只是不够彻底,毕竟为项目快速落地也出了不少力流了不少汗,姑且称为简单工厂或静态工厂吧。不过人民群众的智慧是无穷尽的,这个破绽也不是无法弥补,譬如Java中就可以借助反射来实现对修改关闭。

工厂方法和抽象工厂,这两个模式是符合开闭原则的。怎么实现的呢?

就是再建把上面的PhoneSimpleFactory#createPhone代码中的两个case通过抽象一个PhoneFactory接口,创建两个apple和huawei的PhoneFactory实现,在调用的地方new IPhoneFactory 来创建IPhone类或HuaweiPhoneFactory来创建HuaweiPhone类。这样一来,是不是要增加小米手机时,之前的IPhoneFactory、HuaweiPhoneFactory、IPhone、HuaweiPhone等这些已有的类,是不是不需要做任何改动了?那这是不是既解耦,又符合开闭原则了?是的。但你是不是觉得有种“好笨”、“好蠢”、“好耿直”的感觉!

是的。哥就是这么耿直!!!哥是个有原则的人。

抽象工厂在解耦和设计原则也同工厂方法。但为什么要搞个抽象工厂呢?

因为客观世界是复杂的。譬如HuaWei公司不只生成手机吧,可能还生产手机充电器、耳机等等。如果按工厂方法模式的搞法,是不是要创建手机充电器工厂接口、手机耳机接口,调用的地方要new三个工厂:手机工厂、手机充电器工厂、手机耳机工厂,才能获得手机类、手机充电器类、手机耳机类。这个抽象是与客观世界是不一样的,并且有点乱。

可以再抽象一层,公司维度的抽象,譬如ElectronicFactory,实现类可以是AppleFactory、HuaweiFactory、XiaoMiFactory,AppleFactory可以创建IPhone手机类、IPhone手机充电器类、IPhone手机耳机类。HuaweiFactoryHuaweiFactory和XiaoMiFactory也一样,都能创建自家公司产品的类。

可以看到,“抽象工厂” 之所以带 “抽象” 二字,是因为它的核心载体是 “一个抽象的工厂接口”(定义产品族的创建规范);而 “抽象单一产品” 的工厂方法模式,核心载体是 “工厂方法”(延迟创建逻辑到子类的方法),因此命名聚焦 “方法” 而非 “抽象”。

看完工厂模式,我想Spring了,这些“累”活,Spring IoC容器一肩扛下了创建对象的所有。

来,结合代码再感觉下设计原则【耿直】。

一、简单工厂:一个车间造所有手机

简单工厂是最基础的 “手机生产车间”:用一个工厂类封装所有手机的创建逻辑,对外提供统一的创建入口。

1. 代码实现(手机场景)

代码语言:javascript
复制
// 手机产品的统一接口
interface Phone {
    // 手机的核心功能:打电话
    void call();
}
// 具体产品1:苹果手机
class IPhone implements Phone {
    @Override
    public void call() {
        System.out.println("用iPhone打电话,信号稳稳的~");
    }
}
// 具体产品2:华为手机
class HuaweiPhone implements Phone {
    @Override
    public void call() {
        System.out.println("用华为手机打电话,信号超强~");
    }
}
// 简单工厂:一个车间负责所有手机的生产
class PhoneSimpleFactory {
    // 静态方法:根据品牌类型生产对应手机
    public static Phone createPhone(String brand) {
        switch (brand) {
            case "apple":
                return new IPhone();
            case "huawei":
                return new HuaweiPhone();
            default:
                throw new IllegalArgumentException("抱歉,暂不支持该品牌的手机生产");
        }
    }
}
// 客户端(业务代码):只需要找工厂要手机
public class Client {
    public static void main(String[] args) {
        // 不用自己new IPhone(),找工厂要即可
        Phone myPhone = PhoneSimpleFactory.createPhone("apple");
        myPhone.call();
    }
}

2. 解决的问题 & 适用场景

  • 解决的问题业务代码不用直接依赖IPhoneHuaweiPhone等具体类,只依赖Phone接口和工厂。
  • 优点实现简单,集中管理所有手机的创建逻辑。
  • 缺点新增品牌(比如小米)需要修改PhoneSimpleFactoryswitch代码,违反 “开闭原则”(对扩展开放、对修改关闭)。
  • 适用场景手机品牌少、创建逻辑简单的小型场景。

二、工厂方法:每个品牌一个专属车间

为了解决简单工厂 “修改工厂类” 的问题,工厂方法模式把 “手机生产” 拆分成多个专属车间:每个品牌对应一个工厂,让子类决定具体生产哪种手机。

1. 代码实现(手机场景)

代码语言:javascript
复制
// 手机产品接口(和简单工厂一致)
interface Phone {
    void call();
}
// 具体产品:IPhone、HuaweiPhone(和简单工厂一致)
class IPhone implements Phone {
    @Override
    public void call() {
        System.out.println("用iPhone打电话,信号稳稳的~");
    }
}
class HuaweiPhone implements Phone {
    @Override
    public void call() {
        System.out.println("用华为手机打电话,信号超强~");
    }
}
// 工厂接口:定义“生产手机”的规则
interface PhoneFactory {
    Phone createPhone();
}
// 专属车间1:苹果手机工厂
class IPhoneFactory implements PhoneFactory {
    @Override
    public Phone createPhone() {
        // 只生产IPhone
        return new IPhone();
    }
}
// 专属车间2:华为手机工厂
class HuaweiPhoneFactory implements PhoneFactory {
    @Override
    public Phone createPhone() {
        // 只生产HuaweiPhone
        return new HuaweiPhone();
    }
}
// 客户端:找对应品牌的工厂要手机
public class Client {
    public static void main(String[] args) {
        // 要苹果手机,找苹果工厂
        PhoneFactory factory = new IPhoneFactory();
        Phone myPhone = factory.createPhone();
        myPhone.call();
    }
}

2. 解决的问题 & 适用场景

  • 解决的问题新增品牌时,不用修改原有工厂,只需要新增一个对应的工厂类(比如新增XiaomiPhoneFactory),符合开闭原则。
  • 优点扩展灵活,每个工厂只负责一种手机的生产,职责单一。
  • 缺点手机品牌越多,工厂类也越多,会增加代码复杂度。
  • 适用场景手机品牌较多、需要频繁扩展的场景。

三、抽象工厂:生产 “手机 + 原厂充电器” 的成套车间

实际场景中,手机厂商会配套生产原厂充电器(而非手机壳,手机壳多为第三方生产),这类 “手机 + 原厂充电器” 的组合属于产品族(相关联的成套产品)。抽象工厂模式的核心,就是用一个工厂生产一整套配套产品,保证品牌一致性。

1. 代码实现(手机 + 原厂充电器场景)

代码语言:javascript
复制
// 产品族1:手机(核心产品接口)
interface Phone {
    void call();
}
// 具体手机产品
class IPhone implements Phone {
    @Override
    public void call() {
        System.out.println("用iPhone打电话,信号稳稳的~");
    }
}
class HuaweiPhone implements Phone {
    @Override
    public void call() {
        System.out.println("用华为手机打电话,信号超强~");
    }
}
// 产品族2:手机充电器(配套产品接口)
interface PhoneCharger {
    // 充电器核心功能:给手机充电
    void chargePhone();
}
// 具体充电器产品(与手机品牌配套)
class IPhoneCharger implements PhoneCharger {
    @Override
    public void chargePhone() {
        System.out.println("用苹果原厂充电器给iPhone充电,快充稳定不发烫~");
    }
}
class HuaweiCharger implements PhoneCharger {
    @Override
    public void chargePhone() {
        System.out.println("用华为原厂充电器给华为手机充电,超级快充半小时满电~");
    }
}
// 抽象工厂:定义“生产成套产品”的规则(手机+原厂充电器)
interface ElectronicFactory {
    Phone createPhone();       // 生产手机
    PhoneCharger createCharger(); // 生产配套充电器
}
// 成套车间1:苹果工厂(生产iPhone+苹果原厂充电器)
class AppleFactory implements ElectronicFactory {
    @Override
    public Phone createPhone() {
        return new IPhone();
    }
    @Override
    public PhoneCharger createCharger() {
        return new IPhoneCharger();
    }
}
// 成套车间2:华为工厂(生产华为手机+华为原厂充电器)
class HuaweiFactory implements ElectronicFactory {
    @Override
    public Phone createPhone() {
        return new HuaweiPhone();
    }
    @Override
    public PhoneCharger createCharger() {
        return new HuaweiCharger();
    }
}
// 客户端:找对应品牌的工厂,拿成套产品
public class Client {
    public static void main(String[] args) {
        // 要苹果的成套产品(手机+充电器),找苹果工厂
        ElectronicFactory factory = new AppleFactory();
        Phone myPhone = factory.createPhone();
        PhoneCharger myCharger = factory.createCharger();

        myPhone.call();
        myCharger.chargePhone();
    }
}

2. 解决的问题 & 适用场景

  • 解决的问题保证同一品牌的手机和配件(原厂充电器)是配套的,避免 “iPhone 配华为充电器” 的兼容性问题,同时让客户端无需分别创建手机和充电器,简化调用逻辑。
  • 优点统一管理产品族的创建,确保产品一致性;客户端仅依赖抽象工厂和产品接口,与具体产品解耦。
  • 缺点如果要新增产品类型(比如加原厂耳机),需要修改抽象工厂接口ElectronicFactory,影响所有具体工厂类,扩展产品类型的成本较高。
  • 适用场景需要生产 “品牌配套的成套产品” 的场景(比如手机厂商的原厂设备套装、家电品牌的成套智能设备等)。

四、三种工厂模式的核心对比(手机场景版)

模式

核心逻辑

扩展方式

适用场景

简单工厂

一个车间生产所有手机

修改工厂类

品牌少、创建逻辑简单

工厂方法

每个品牌一个专属车间(仅生产手机)

新增工厂类

品牌多、需频繁扩展单一产品

抽象工厂

一个车间生产成套手机 + 原厂配件

新增工厂类(产品族)

需要品牌配套产品组合的场景

五、Spring:帮你管所有 “手机工厂” 的超级管理中心

对于入门者来说,Spring 的 “对象创建封装” 可以理解为:Spring 是一个 “手机工厂管理中心”,你不用自己建车间,只需要告诉它你要什么手机 / 配件,它帮你找车间、生产、甚至送上门

下面用 “手机场景” 拆解 Spring 的核心概念:

1. 核心角色:

  • IOC 容器(控制反转)就是 “手机工厂管理中心”,负责管理所有 “车间(工厂)” 和 “产品(对象)” 的创建、存储、分发。
  • BeanSpring 里的 “对象”,比如我们的IPhoneIPhoneCharger就是 Bean。
  • BeanFactory【抽象工厂】负责创建所有Bean实例,是“管理中心的基础规则手册”,定义了 “怎么拿产品(获取 Bean)” 的基本规则(比如getBean()方法),是抽象工厂模式的典型应用。
  • ApplicationContext“实际运营的管理中心”,是BeanFactory的升级版,不仅能拿产品,还能自动初始化工厂、管理产品依赖(比如给手机配充电器)。
  • FactoryBean“定制产品的小作坊”,如果需要生产特殊定制的产品(比如 “限量版 iPhone + 定制充电器套装”),可以通过FactoryBean自定义创建逻辑。

2. Spring 怎么帮你生产手机 + 充电器?(入门级示例)

假设我们要在 Spring 中获取苹果的 “手机 + 充电器” 套装:

步骤 1:定义 Bean 并配置(告诉管理中心要生产的产品)
代码语言:javascript
复制
// 1. 复用之前的产品类(Phone、IPhone、PhoneCharger、IPhoneCharger)
interface Phone { void call(); }
class IPhone implements Phone {
    @Override
    public void call() {
        System.out.println("用Spring生产的iPhone打电话~");
    }
}
interface PhoneCharger { void chargePhone(); }
class IPhoneCharger implements PhoneCharger {
    @Override
    public void chargePhone() {
        System.out.println("用Spring生产的苹果原厂充电器给iPhone充电~");
    }
}
// 2. 配置类:告诉Spring要生产哪些产品(Bean)
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration // 标记这是Spring的“工厂配置文件”
public class PhoneConfig {
    // 定义iPhone Bean:Spring调用此方法创建iPhone对象
    @Bean
    public Phone iphone() {
        return new IPhone();
    }
    // 定义苹果充电器Bean:Spring调用此方法创建充电器对象
    @Bean
    public PhoneCharger iphoneCharger() {
        return new IPhoneCharger();
    }
}
步骤 2:从 Spring 容器获取成套产品
代码语言:javascript
复制
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SpringClient {
    public static void main(String[] args) {
        // 启动Spring容器(管理中心),加载配置文件
        ApplicationContext context = new AnnotationConfigApplicationContext(PhoneConfig.class);

        // 从容器中获取iPhone和充电器(不用自己new)
        Phone myPhone = context.getBean(Phone.class);
        PhoneCharger myCharger = context.getBean(PhoneCharger.class);

        myPhone.call();
        myCharger.chargePhone();
    }
}

3. Spring 怎么解耦?

  • 消除硬编码 new业务代码不用写new IPhone()new IPhoneCharger(),只需通过接口获取对象,降低与具体类的耦合。
  • 自动依赖注入如果IPhone需要绑定IPhoneCharger(比如手机内置充电器适配逻辑),Spring 可以自动把充电器 “注入” 到手机对象中,无需手动关联。
  • 灵活切换产品如果要换成华为产品,只需修改配置类中的@Bean方法(返回HuaweiPhoneHuaweiCharger),业务代码无需任何改动,符合开闭原则。

总结

所有优秀的设计模式,本质上都是 “用单一职责原则做拆分,用开闭原则做扩展”。

单一职责原则是 “基础”,负责 “拆得干净”;开闭原则是 “目标”,负责 “扩得方便”。

回到工厂模式。

工厂模式的核心是封装对象创建逻辑,降低业务代码与具体类的耦合,三个变种从简单到复杂,分别对应不同的业务场景:

简单工厂:“一站式” 创建所有产品,牺牲扩展性换简洁;

代码语言:javascript
复制
Java 的Calendar.getInstance()(简单工厂),根据时区创建Calendar实例;

工厂方法:“一个产品一个工厂”,以类数量换扩展性;

代码语言:javascript
复制
MyBatis 的SqlSessionFactory(工厂方法),创建SqlSession

抽象工厂:“一个产品族一个工厂”,解决相关产品的配套创建问题。

代码语言:javascript
复制
UI 组件的创建。想一下切换主题的场景

而 Spring 则是工厂模式的 “超级落地”—— 它接管了所有工厂和对象的管理,让开发者从 “造对象” 的繁琐工作中解放出来,专注于业务逻辑本身。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-12-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 的数字化之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、简单工厂:一个车间造所有手机
    • 1. 代码实现(手机场景)
    • 2. 解决的问题 & 适用场景
  • 二、工厂方法:每个品牌一个专属车间
    • 1. 代码实现(手机场景)
    • 2. 解决的问题 & 适用场景
  • 三、抽象工厂:生产 “手机 + 原厂充电器” 的成套车间
    • 1. 代码实现(手机 + 原厂充电器场景)
    • 2. 解决的问题 & 适用场景
  • 四、三种工厂模式的核心对比(手机场景版)
  • 五、Spring:帮你管所有 “手机工厂” 的超级管理中心
    • 1. 核心角色:
    • 2. Spring 怎么帮你生产手机 + 充电器?(入门级示例)
      • 步骤 1:定义 Bean 并配置(告诉管理中心要生产的产品)
      • 步骤 2:从 Spring 容器获取成套产品
    • 3. Spring 怎么解耦?
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档