什么是工厂模式?3种实现方式的区别和特点?


面试考察点

  1. 演进思维:面试官不仅仅是想知道三种工厂模式分别是什么,更是想看你能否讲清楚它们的演进逻辑——简单工厂有什么问题?工厂方法为什么出现?抽象工厂又解决了什么问题?这条线理顺了,说明你是真懂了。

  2. 开闭原则理解:三种工厂模式对开闭原则的满足程度不同,面试官想看你是否理解这个递进关系。

  3. 实战关联:能否把工厂模式和 Spring 的 BeanFactoryFactoryBean、日志门面 SLF4J 等框架联系起来。

核心答案

一句话定义:工厂模式将对象的创建过程封装起来,客户端不需要知道具体创建了哪个类,只需要通过工厂获取对象即可。

工厂模式有 3 种实现方式,它们是一个逐步演进的关系:

类型核心思想工厂类数量适用场景
简单工厂一个工厂类,通过参数决定创建哪个产品1 个产品种类少且不常扩展
工厂方法每个产品对应一个工厂类多个产品种类多、需要扩展
抽象工厂一个工厂创建一族相关产品多个需要创建产品族

上面的图展示了三种工厂模式的演进逻辑:

  • 简单工厂:所有产品集中在一个工厂里创建,通过参数区分。简单直接,但扩展性差。
  • 工厂方法:把工厂拆开,每个产品对应一个工厂。扩展性好,但类会变多。
  • 抽象工厂:在工厂方法的基础上,让一个工厂能创建一族相关的产品。解决 "产品族" 问题。

深度解析

一、简单工厂模式(静态工厂方法)

先定义产品接口和具体产品:

// 产品接口
public interface Phone {
    void produce();
}

// 具体产品 A
public class ApplePhone implements Phone {
    @Override
    public void produce() {
        System.out.println("生产苹果手机");
    }
}

// 具体产品 B
public class HuaweiPhone implements Phone {
    @Override
    public void produce() {
        System.out.println("生产华为手机");
    }
}

工厂类:

public class PhoneFactory {

    // 静态方法,根据参数创建不同的产品
    public static Phone createPhone(String type) {
        if ("apple".equals(type)) {
            return new ApplePhone();
        } else if ("huawei".equals(type)) {
            return new HuaweiPhone();
        }
        throw new IllegalArgumentException("未知手机类型:" + type);
    }
}

// 使用
Phone phone = PhoneFactory.createPhone("apple");
phone.produce(); // 生产苹果手机

简单工厂的优点很明显——客户端只需要传个参数就行,不用关心具体创建逻辑。缺点也很致命:每新增一个产品,都要改 createPhone() 方法,加一个 else if。违反了开闭原则。

简单工厂其实不算 GoF 23 种设计模式里的,它更像是一种编程习惯。但面试经常把它和另外两种放在一起问,因为它确实是工厂方法的 "前身"。

二、工厂方法模式

工厂方法的核心改进:把工厂类也抽象化,每个具体产品对应一个具体工厂

// 抽象工厂
public interface PhoneFactory {
    Phone createPhone();
}

// 苹果工厂——只生产苹果手机
public class ApplePhoneFactory implements PhoneFactory {
    @Override
    public Phone createPhone() {
        return new ApplePhone();
    }
}

// 华为工厂——只生产华为手机
public class HuaweiPhoneFactory implements PhoneFactory {
    @Override
    public Phone createPhone() {
        return new HuaweiPhone();
    }
}

// 使用
PhoneFactory factory = new ApplePhoneFactory();
Phone phone = factory.createPhone();
phone.produce(); // 生产苹果手机

好处是什么?新增产品完全不用改已有代码。比如要加一个小米手机:

// 新增产品
public class XiaomiPhone implements Phone {
    @Override
    public void produce() {
        System.out.println("生产小米手机");
    }
}

// 新增工厂
public class XiaomiPhoneFactory implements PhoneFactory {
    @Override
    public Phone createPhone() {
        return new XiaomiPhone();
    }
}

ApplePhoneFactoryHuaweiPhoneFactory 一行都不用改,完美符合开闭原则。

代价就是类的数量增多了——一个产品对应一个工厂。但这在面向对象设计里是完全可以接受的,用类的数量换可维护性。

三、抽象工厂模式

抽象工厂解决的是一个更复杂的问题——产品族

什么叫产品族?打个比方:苹果有手机和耳机,华为也有手机和耳机。苹果的手机和耳机是一族(风格统一),华为的也是一族。你不会拿苹果手机配华为耳机(虽然物理上可以),因为产品族不一致。

// 产品族接口:手机
public interface Phone {
    void call();
}

// 产品族接口:耳机
public interface Earphone {
    void listen();
}

// 抽象工厂:定义创建一族产品的接口
public interface ElectronicsFactory {
    Phone createPhone();
    Earphone createEarphone();
}

苹果产品族:

public class ApplePhone implements Phone {
    @Override
    public void call() {
        System.out.println("用 iPhone 打电话");
    }
}

public class AirPods implements Earphone {
    @Override
    public void listen() {
        System.out.println("用 AirPods 听歌");
    }
}

// 苹果工厂:创建一族的苹果产品
public class AppleFactory implements ElectronicsFactory {
    @Override
    public Phone createPhone() {
        return new ApplePhone();
    }

    @Override
    public Earphone createEarphone() {
        return new AirPods();
    }
}

华为产品族:

public class HuaweiPhone implements Phone {
    @Override
    public void call() {
        System.out.println("用华为 Mate 打电话");
    }
}

public class FreeBuds implements Earphone {
    @Override
    public void listen() {
        System.out.println("用 FreeBuds 听歌");
    }
}

// 华为工厂:创建一族的华为产品
public class HuaweiFactory implements ElectronicsFactory {
    @Override
    public Phone createPhone() {
        return new HuaweiPhone();
    }

    @Override
    public Earphone createEarphone() {
        return new FreeBuds();
    }
}

使用:

// 切换整个产品族,只需要换一个工厂
ElectronicsFactory factory = new AppleFactory();
factory.createPhone().call();       // 用 iPhone 打电话
factory.createEarphone().listen();  // 用 AirPods 听歌

// 想换华为?换一个工厂就行
factory = new HuaweiFactory();
factory.createPhone().call();       // 用华为 Mate 打电话
factory.createEarphone().listen();  // 用 FreeBuds 听歌

抽象工厂最大的优势就是保证产品族的一致性——你拿到 AppleFactory,创建出来的手机和耳机一定是苹果的,不会出现混搭。

但抽象工厂也有一个缺点:新增产品等级(比如新增一个 "智能手表" 产品)需要修改所有的工厂类。这点和简单工厂的问题类似。所以抽象工厂适合 "产品等级结构稳定,但产品族经常变化" 的场景。

四、三种模式对比总结

对比项简单工厂工厂方法抽象工厂
工厂类数量1 个多个(每产品一个)多个(每产品族一个)
产品维度单一产品单一产品产品族(多等级)
新增产品❌ 改工厂类✅ 新增工厂类❌ 改所有工厂类
新增产品族✅ 新增工厂类
开闭原则不满足部分满足部分满足
复杂度

五、框架中的工厂模式

1. Spring 的 BeanFactory

BeanFactory 是 Spring 的核心容器,本质上就是一个大工厂。你通过 getBean() 获取对象,不需要知道 Bean 是怎么创建的——可能是反射实例化的,可能是通过工厂方法创建的,可能是从配置类里生成的。

BeanFactory factory = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = factory.getBean(UserService.class);

2. Spring 的 FactoryBean

FactoryBean 是 Spring 提供的一个工厂接口,让你可以自定义复杂对象的创建逻辑。MyBatis 的 SqlSessionFactoryBean 就是一个典型应用:

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory> {
    @Override
    public SqlSessionFactory getObject() {
        // 复杂的创建逻辑...
        return sqlSessionFactory;
    }
}

3. SLF4J 日志门面

LoggerFactory.getLogger() 本质上就是一个简单工厂。你不需要关心底层用的是 Logback 还是 Log4j2,工厂帮你搞定。

4. JDBC DriverManager.getConnection()

根据 URL 创建不同数据库的连接,也是简单工厂的思想。

面试高频追问

  1. 简单工厂、工厂方法、抽象工厂有什么区别?

    • 简单工厂:一个工厂通过参数创建不同产品,新增产品要改工厂。工厂方法:每个产品一个工厂,新增产品只需新增工厂。抽象工厂:一个工厂创建一族产品,新增产品族只需新增工厂,但新增产品等级要改所有工厂。
  2. 抽象工厂的优缺点是什么?

    • 优点:保证产品族一致性,切换产品族只需换一个工厂。缺点:新增产品等级结构需要修改所有工厂类,违反开闭原则。
  3. Spring 中哪里用到了工厂模式?

    • BeanFactory(IoC 容器本身就是工厂)、FactoryBean(自定义复杂对象创建)、ApplicationContext(Bean 的大工厂)。

常见面试变体

  • "手写一个工厂模式"
  • "工厂方法模式和抽象工厂模式的区别?"
  • "Spring 的 BeanFactoryFactoryBean 有什么区别?"
  • "开闭原则在工厂模式中是怎么体现的?"

记忆口诀

三种工厂:简单(一个工厂搞定)、方法(一个产品一个工厂)、抽象(一族产品一个工厂)。

演进逻辑:简单工厂违反开闭 → 工厂方法拆工厂 → 抽象工厂管产品族。

选择依据:产品少用简单,产品多用方法,产品族用抽象。

总结

工厂模式的演进逻辑就是一部 "消灭 if-else、追求开闭原则" 的历史。面试时先说三种工厂的核心区别,再用 "手机生产" 的例子依次展开,最后结合 Spring 的 BeanFactoryFactoryBean 讲框架应用。能把演进逻辑讲清楚,面试官就知道你是真理解了,而不是背了三种写法。