杰瑞科技汇

工厂模式与抽象工厂模式有何核心区别?

核心思想:为什么要用工厂模式?

在没有工厂模式的情况下,如果我们需要创建一个对象,通常会在客户端代码中直接使用 new 关键字。

工厂模式与抽象工厂模式有何核心区别?-图1
(图片来源网络,侵删)

问题场景: 假设我们有一个 Shape 接口和它的实现类 CircleRectangle

// 产品接口
interface Shape {
    void draw();
}
// 具体产品 A
class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a Circle");
    }
}
// 具体产品 B
class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a Rectangle");
    }
}
// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 客户端直接依赖于具体类 Circle
        Shape circle = new Circle();
        circle.draw();
    }
}

潜在问题:

  1. 高耦合Client 类直接依赖于 Circle 这个具体类,如果我们想换一个 Shape 的实现,或者新增一个 Square,就必须修改 Client 类的代码。
  2. 可扩展性差:每增加一种新的形状,都需要在客户端代码中添加 new XXX() 的逻辑,违反了“开闭原则”(对扩展开放,对修改关闭)。

工厂模式的解决方案: 我们引入一个“工厂”角色,专门负责创建对象,客户端不再直接创建对象,而是向工厂请求,这样,客户端就只依赖于产品接口,而不依赖于具体的产品实现。


工厂模式

工厂模式提供一个接口,用于创建单个产品,但让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。

工厂模式与抽象工厂模式有何核心区别?-图2
(图片来源网络,侵删)

结构

  • Product (产品接口):定义了工厂创建的对象的接口。
  • ConcreteProduct (具体产品):实现了 Product 接口的具体对象。
  • Factory (工厂接口):声明了一个方法 createProduct(),用于创建 Product 对象。
  • ConcreteFactory (具体工厂):实现了 Factory 接口,负责创建并返回一个具体的 ConcreteProduct 对象。

代码示例

我们改造上面的形状例子,使其符合工厂模式。

// 1. Product (产品接口)
interface Shape {
    void draw();
}
// 2. ConcreteProduct (具体产品)
class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a Circle");
    }
}
class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a Rectangle");
    }
}
// 3. Factory (工厂接口)
interface ShapeFactory {
    Shape createShape();
}
// 4. ConcreteFactory (具体工厂)
class CircleFactory implements ShapeFactory {
    @Override
    public Shape createShape() {
        return new Circle(); // 创建并返回 Circle 对象
    }
}
class RectangleFactory implements ShapeFactory {
    @Override
    public Shape createShape() {
        return new Rectangle(); // 创建并返回 Rectangle 对象
    }
}
// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 客户端不再直接 new Circle(),而是通过工厂获取
        ShapeFactory circleFactory = new CircleFactory();
        Shape circle = circleFactory.createShape();
        circle.draw();
        System.out.println("----------");
        ShapeFactory rectangleFactory = new RectangleFactory();
        Shape rectangle = rectangleFactory.createShape();
        rectangle.draw();
    }
}

优点与缺点

优点:

  • 解耦:客户端代码与具体产品实现解耦,客户端只依赖于 ShapeFactoryShape 接口。
  • 符合开闭原则:新增产品时(如 Square),只需新增一个 Square 类和一个 SquareFactory 类,无需修改现有客户端代码。
  • 封装性:对象的创建逻辑被封装在工厂类中,客户端无需知道创建细节。

缺点:

  • 类的数量增加:每增加一个产品,就需要增加一个对应的工厂类,这会导致系统中类的数量成对增加,增加了系统的复杂度。

抽象工厂模式

抽象工厂模式提供了一个接口,用于创建相关或依赖对象的家族,而不需要指定它们具体的类,当你需要创建的产品是一个产品族(一个品牌下的多个产品,如 BMWCarBMWEngine),并且客户端不希望依赖于具体的产品实现时,抽象工厂模式就非常适用。

工厂模式与抽象工厂模式有何核心区别?-图3
(图片来源网络,侵删)

结构

  • AbstractProduct (抽象产品A/B):定义了一组产品(产品族)的接口,CarEngine
  • ConcreteProduct (具体产品A1/A2/B1/B2):实现了抽象产品接口的具体对象。BMWCar, BenzCar, BMWEngine, BenzEngine
  • AbstractFactory (抽象工厂):声明了一组创建抽象产品的方法,如 createCar()createEngine()
  • ConcreteFactory (具体工厂):实现了抽象工厂接口,负责创建一个产品族中的所有具体产品。BMWFactory 创建 BMWCarBMWEngineBenzFactory 创建 BenzCarBenzEngine

代码示例

假设我们要生产汽车和引擎,有两个品牌:宝马和奔驰,宝马的汽车必须配宝马的引擎,奔驰的汽车必须配奔驰的引擎,这就是一个典型的“产品族”。

// 抽象产品 A
interface Car {
    void drive();
}
// 抽象产品 B
interface Engine {
    void start();
}
// --- 宝马产品族 ---
// 具体产品 A1
class BMWCar implements Car {
    @Override
    public void drive() {
        System.out.println("Driving a BMW Car.");
    }
}
// 具体产品 B1
class BMWEngine implements Engine {
    @Override
    public void start() {
        System.out.println("BMW Engine started.");
    }
}
// --- 奔驰产品族 ---
// 具体产品 A2
class BenzCar implements Car {
    @Override
    public void drive() {
        System.out.println("Driving a Benz Car.");
    }
}
// 具体产品 B2
class BenzEngine implements Engine {
    @Override
    public void start() {
        System.out.println("Benz Engine started.");
    }
}
// 抽象工厂
interface CarFactory {
    Car createCar();
    Engine createEngine();
}
// 具体工厂 1: 宝马工厂
class BMWFactory implements CarFactory {
    @Override
    public Car createCar() {
        return new BMWCar();
    }
    @Override
    public Engine createEngine() {
        return new BMWEngine();
    }
}
// 具体工厂 2: 奔驰工厂
class BenzFactory implements CarFactory {
    @Override
    public Car createCar() {
        return new BenzCar();
    }
    @Override
    public Engine createEngine() {
        return new BenzEngine();
    }
}
// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 客户端想要一辆宝马汽车
        CarFactory bmwFactory = new BMWFactory();
        Car bmwCar = bmwFactory.createCar();
        Engine bmwEngine = bmwFactory.createEngine();
        bmwCar.drive();
        bmwEngine.start();
        System.out.println("----------");
        // 客户端想要一辆奔驰汽车
        CarFactory benzFactory = new BenzFactory();
        Car benzCar = benzFactory.createCar();
        Engine benzEngine = benzFactory.createEngine();
        benzCar.drive();
        benzEngine.start();
    }
}

优点与缺点

优点:

  • 保证产品族的一致性:抽象工厂确保客户端创建的是同一品牌下的兼容产品(如宝马车配宝马引擎)。
  • 易于切换产品族:客户端只需要切换工厂,就能轻松切换整个产品族,而无需修改任何创建逻辑。
  • 符合开闭原则:增加新的产品族(如 TeslaFactory)非常容易,只需新增具体产品和具体工厂即可。

缺点:

  • 扩展新产品困难:如果要在产品族中增加一个新产品(比如在 CarEngine 之上增加 AirConditioner),那么所有的具体工厂(BMWFactory, BenzFactory)都需要进行修改,这违反了开闭原则。

工厂模式 vs. 抽象工厂模式:一张图看懂

特性 工厂模式 抽象工厂模式
创建对象 创建单一类型的产品 创建一个产品族(多个相关或依赖的产品)
关注点 关注的是产品等级结构(如 Shape 及其子类) 关注的是产品族(如 BMW 下的所有产品)
接口方法 工厂接口通常只有一个 createProduct() 方法 工厂接口有多个 createProductA(), createProductB() 等方法
扩展性 易于扩展新产品,难于扩展产品族 易于扩展产品族,难于扩展新产品
核心思想 将单个对象的创建延迟到子类 将一个产品族的创建封装起来

简单记忆:

  • 工厂模式:一个工厂只管造一种东西(ShapeFactory 只管造 Shape)。
  • 抽象工厂模式:一个工厂管造一整套东西(BMWFactory 既造 BMWCar 又造 BMWEngine)。

何时使用?

使用工厂模式的情况:

  1. 当一个类无法知道它所必须创建的对象的类时。
  2. 当一个类希望由其子类来指定它所创建的对象时。
  3. 当类将创建对象的职责委托给一个帮助子类,并且你希望将帮助子类的本地化知道的信息不暴露到客户端代码中时。

通俗例子:你开了一家披萨店,但只卖一种口味的披萨(芝士披萨),当需要制作披萨时,你创建一个 CheesePizzaFactory 来生产它,如果以后想卖意式披萨,就再创建一个 ItalianPizzaFactory,这符合工厂模式。

使用抽象工厂模式的情况:

  1. 当一个系统要独立于它的产品的创建、组合和表示时。
  2. 当一个系统要由多个产品族中的一个来配置,而你想运行时切换产品族。
  3. 当你提供的类库只包含非原子对象时,你想让这些类工作,但又不希望暴露它们的实现细节(只暴露 CarFactory 接口,而不暴露 BMWCar 的实现)。

通俗例子:你开了一家汽车组装厂,这个厂既能生产宝马系列(宝马车、宝马引擎),也能生产奔驰系列(奔驰车、奔驰引擎),你通过选择 BMWFactoryBenzFactory 来决定整个生产线组装哪个品牌的产品,这符合抽象工厂模式。

工厂模式 抽象工厂模式
目的 解耦,创建单一对象 解耦,创建产品族
结构 1个产品接口,1个工厂接口 多个产品接口,1个工厂接口
扩展 易扩展产品,难扩展产品族 易扩展产品族,难扩展产品
选择 当你需要创建一个对象,且有多种实现时 当你需要创建一组相关对象时

理解这两种模式的关键在于区分“产品等级结构”和“产品族”,工厂模式处理前者,抽象工厂模式处理后者,在实际开发中,根据业务需求选择合适的模式,能让你的代码更加优雅和健壮。

分享:
扫描分享到社交APP
上一篇
下一篇