都说温故而知新。重温一遍工厂设计模式,觉得工厂模式的思路很清晰,落地好耿直、好低效。现在讲下自己的理解,欢迎拍砖。
为什么要用工厂,直接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容器一肩扛下了创建对象的所有。
来,结合代码再感觉下设计原则【耿直】。
简单工厂是最基础的 “手机生产车间”:用一个工厂类封装所有手机的创建逻辑,对外提供统一的创建入口。
// 手机产品的统一接口
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();
}
}IPhone、HuaweiPhone等具体类,只依赖Phone接口和工厂。PhoneSimpleFactory的switch代码,违反 “开闭原则”(对扩展开放、对修改关闭)。为了解决简单工厂 “修改工厂类” 的问题,工厂方法模式把 “手机生产” 拆分成多个专属车间:每个品牌对应一个工厂,让子类决定具体生产哪种手机。
// 手机产品接口(和简单工厂一致)
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();
}
}XiaomiPhoneFactory),符合开闭原则。实际场景中,手机厂商会配套生产原厂充电器(而非手机壳,手机壳多为第三方生产),这类 “手机 + 原厂充电器” 的组合属于产品族(相关联的成套产品)。抽象工厂模式的核心,就是用一个工厂生产一整套配套产品,保证品牌一致性。
// 产品族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();
}
}ElectronicFactory,影响所有具体工厂类,扩展产品类型的成本较高。模式 | 核心逻辑 | 扩展方式 | 适用场景 |
|---|---|---|---|
简单工厂 | 一个车间生产所有手机 | 修改工厂类 | 品牌少、创建逻辑简单 |
工厂方法 | 每个品牌一个专属车间(仅生产手机) | 新增工厂类 | 品牌多、需频繁扩展单一产品 |
抽象工厂 | 一个车间生产成套手机 + 原厂配件 | 新增工厂类(产品族) | 需要品牌配套产品组合的场景 |
对于入门者来说,Spring 的 “对象创建封装” 可以理解为:Spring 是一个 “手机工厂管理中心”,你不用自己建车间,只需要告诉它你要什么手机 / 配件,它帮你找车间、生产、甚至送上门。
下面用 “手机场景” 拆解 Spring 的核心概念:
IPhone、IPhoneCharger就是 Bean。getBean()方法),是抽象工厂模式的典型应用。BeanFactory的升级版,不仅能拿产品,还能自动初始化工厂、管理产品依赖(比如给手机配充电器)。FactoryBean自定义创建逻辑。假设我们要在 Spring 中获取苹果的 “手机 + 充电器” 套装:
// 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();
}
}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();
}
}new IPhone()、new IPhoneCharger(),只需通过接口获取对象,降低与具体类的耦合。IPhone需要绑定IPhoneCharger(比如手机内置充电器适配逻辑),Spring 可以自动把充电器 “注入” 到手机对象中,无需手动关联。@Bean方法(返回HuaweiPhone和HuaweiCharger),业务代码无需任何改动,符合开闭原则。所有优秀的设计模式,本质上都是 “用单一职责原则做拆分,用开闭原则做扩展”。
单一职责原则是 “基础”,负责 “拆得干净”;开闭原则是 “目标”,负责 “扩得方便”。
回到工厂模式。
工厂模式的核心是封装对象创建逻辑,降低业务代码与具体类的耦合,三个变种从简单到复杂,分别对应不同的业务场景:
简单工厂:“一站式” 创建所有产品,牺牲扩展性换简洁;
Java 的Calendar.getInstance()(简单工厂),根据时区创建Calendar实例;工厂方法:“一个产品一个工厂”,以类数量换扩展性;
MyBatis 的SqlSessionFactory(工厂方法),创建SqlSession抽象工厂:“一个产品族一个工厂”,解决相关产品的配套创建问题。
UI 组件的创建。想一下切换主题的场景而 Spring 则是工厂模式的 “超级落地”—— 它接管了所有工厂和对象的管理,让开发者从 “造对象” 的繁琐工作中解放出来,专注于业务逻辑本身。