抽象工厂 vs 原型模式:核心区别与原型模式实战解析

抽象工厂 vs 原型模式:核心区别与原型模式实战解析

在 GoF 设计模式中,抽象工厂(Abstract Factory)原型模式(Prototype) 都属于创建型模式。很多开发者容易混淆它们,尤其听到"原型工厂"这样的非标准术语时更是一头雾水。本文将从定义、类图、代码示例、使用场景等多个维度深度剖析两者的区别,并重点讲解原型模式(Prototype Pattern)的核心思想、浅克隆与深克隆、以及它在 Spring、JDK 等框架中的实际应用。


一、快速理解:抽象工厂 vs 原型模式

维度 抽象工厂模式 原型模式
目的 创建一系列相关或相互依赖的对象族 通过复制(克隆) 现有实例来创建新对象
核心操作 定义工厂接口,每个具体工厂负责生成一族产品 定义 clone() 方法,通过拷贝原型对象来创建新实例
关键角色 AbstractFactory、ConcreteFactory、AbstractProduct、ConcreteProduct Prototype、ConcretePrototype、Client
典型应用 跨平台 UI 控件(Win/Mac 风格)、数据库连接工厂 Spring 原型作用域、Java 对象克隆、缓存对象复制
优点 隔离具体产品类,保证产品族一致性 减少子类化,动态配置对象结构
缺点 新增产品族麻烦(需改抽象工厂) 深克隆需谨慎处理循环引用

⚠️ 注意:没有"原型工厂"这个标准模式名称,通常指 原型模式(Prototype Pattern)。本文统一称为"原型模式"。


二、抽象工厂模式详解

2.1 定义与场景

抽象工厂提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。例如:生产一套"现代风格"的沙发、茶几、椅子,或者"维多利亚风格"的一套家具。

2.2 类图

<<interface>>
AbstractFactory
+createProductA() : AbstractProductA
+createProductB() : AbstractProductB
ConcreteFactory1
+createProductA() : ProductA1
+createProductB() : ProductB1
ConcreteFactory2
+createProductA() : ProductA2
+createProductB() : ProductB2
<<interface>>
AbstractProductA
<<interface>>
AbstractProductB
ProductA1
ProductA2
ProductB1
ProductB2

2.3 代码示例(Java)

java 复制代码
// 产品接口
interface Button { void paint(); }
interface Checkbox { void check(); }

// 具体产品(Win风格)
class WinButton implements Button {
    public void paint() { System.out.println("Windows Button"); }
}
class WinCheckbox implements Checkbox {
    public void check() { System.out.println("Windows Checkbox"); }
}

// 具体产品(Mac风格)
class MacButton implements Button {
    public void paint() { System.out.println("Mac Button"); }
}
class MacCheckbox implements Checkbox {
    public void check() { System.out.println("Mac Checkbox"); }
}

// 抽象工厂
interface GUIFactory {
    Button createButton();
    Checkbox createCheckbox();
}

// 具体工厂
class WinFactory implements GUIFactory {
    public Button createButton() { return new WinButton(); }
    public Checkbox createCheckbox() { return new WinCheckbox(); }
}
class MacFactory implements GUIFactory {
    public Button createButton() { return new MacButton(); }
    public Checkbox createCheckbox() { return new MacCheckbox(); }
}

// 客户端
public class Client {
    private Button button;
    private Checkbox checkbox;
    public Client(GUIFactory factory) {
        button = factory.createButton();
        checkbox = factory.createCheckbox();
    }
    public void paint() {
        button.paint();
        checkbox.check();
    }
}

2.4 适用场景

  • 系统需要独立于产品的创建、组合和表示。
  • 需要保证一个产品族内的对象一起工作(比如 Win 风格的所有控件)。
  • 你可能只使用一个产品族,但将来可能切换为另一族。

三、原型模式(Prototype Pattern)详解

3.1 定义与核心思想

用原型实例指定创建对象的种类,并且通过复制(克隆) 这些原型来创建新的对象。

原型模式的核心是克隆 ,而不是调用 new 来创建对象。当创建对象的成本较高(如涉及网络、数据库、复杂计算),且对象之间只有少量状态不同时,克隆可以极大提升性能。

3.2 浅克隆与深克隆

  • 浅克隆 :复制基本类型和引用类型的地址,即原对象和克隆对象共享引用类型的实例。
  • 深克隆:递归复制引用类型内部的所有对象,实现完全独立的副本。

Java 中实现克隆需要:

  • 实现 Cloneable 接口(标记接口)。
  • 重写 clone() 方法,通常调用 super.clone()(浅克隆)。若要深克隆,需手动克隆引用成员。

3.3 类图

<<interface>>
Prototype
+clone() : Prototype
ConcretePrototype1
-String field
-List<String> list
+clone() : ConcretePrototype1
ConcretePrototype2
-int value
+clone() : ConcretePrototype2
Client
+operation()

3.4 代码示例(Java + 深克隆)

java 复制代码
// 原型接口
public interface Prototype {
    Prototype clone();  // 或者使用 Object 的 clone()
}

// 具体原型类
public class ShoppingCart implements Prototype, Cloneable {
    private Long userId;
    private List<Item> items;  // 引用类型,需深克隆
    
    public ShoppingCart(Long userId, List<Item> items) {
        this.userId = userId;
        this.items = items;
    }
    
    @Override
    public ShoppingCart clone() {
        try {
            ShoppingCart clone = (ShoppingCart) super.clone(); // 浅克隆
            // 手动深克隆 items
            clone.items = new ArrayList<>(this.items);
            // 如果 Item 也是引用类型,同样需要递归克隆
            return clone;
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
    
    // getters/setters...
}

// 客户端克隆购物车
ShoppingCart original = new ShoppingCart(100L, itemList);
ShoppingCart copy = original.clone();
copy.setUserId(101L);  // 新用户获得相同购物车内容,但独立修改不影响原对象

3.5 实际应用场景(你在哪里用到过?)

① Spring Framework 中的原型作用域

Spring 的 Bean 有 Singleton (默认)和 Prototype 作用域。当 Bean 配置为 scope="prototype" 时,每次请求该 Bean 都会通过克隆或新建 返回一个新的实例。虽然 Spring 内部不是直接使用 Java 的 clone(),但思想完全一致:通过原型定义创建新对象

java 复制代码
@Component
@Scope("prototype")
public class OrderProcessor {
    // 每次注入都会是新实例
}
② JDK 中的 Object.clone()Cloneable

Java 语言内置支持原型模式,java.lang.Object 提供了 protected native Object clone() 方法,任何实现了 Cloneable 接口的类都可以调用 super.clone() 获得浅拷贝。例如 ArrayListHashMap 都实现了 clone()

③ 缓存对象复制

在高并发场景下,某些配置数据(如商品类目树)从 DB 加载后可以被复制给多个请求线程,每个线程可以独立修改副本而不影响原缓存。

④ 游戏开发中的角色复制

比如"分身术"技能:基于玩家当前角色状态(装备、血量等)克隆出一个完全相同的新角色对象。

⑤ 性能优化:避免重复数据库查询

一个复杂报表查询耗时 5 秒,得到结果后将其原型化。后续相同参数的请求直接克隆原型对象返回,无需再查 DB。


四、抽象工厂 vs 原型模式:对比总结

对比点 抽象工厂 原型模式
创建方式 通过工厂接口的 createXXX() 方法创建新对象(通常是 new 通过现有对象的 clone() 方法复制对象
对象状态 创建的对象是全新的、通常具有默认状态 克隆出来的对象与原型对象初始状态完全相同,之后可独立修改
复杂度 需要设计产品等级结构和工厂层次 需要处理浅/深克隆及循环引用
对客户透明 客户不知道具体产品类 客户只需要知道原型接口
性能 第一次创建时较慢(需调用构造器) 克隆通常比 new + 属性赋值快(尤其构造器有复杂逻辑时)
扩展方向 增加新产品族(新的具体工厂) 增加新原型类(实现 clone

两者并不互斥,甚至可结合使用

例如,抽象工厂可以产生不同的原型对象,然后客户端通过克隆这些原型来创建产品族中的具体产品。这样抽象工厂负责"选取"原型,原型模式负责"复制"实例。


五、选择流程图:抽象工厂还是原型模式?





需要创建对象
创建对象的成本很高

(网络/IO/复杂计算)?
原型模式更合适
需要创建一系列

相互关联的对象?
抽象工厂模式
简单工厂或工厂方法
确保对象支持克隆

处理深拷贝问题
设计产品族接口

和具体工厂


六、常见面试题

Q1:原型模式是工厂模式的一种吗?

A:不是。原型模式属于创建型模式,但不属于工厂模式。它不依赖工厂,而是依赖克隆。

Q2:Java 的 clone() 为什么是浅克隆?如何实现深克隆?

A:Object.clone() 是浅克隆,因为它只复制成员字段的值(引用类型只复制地址)。深克隆需要手动复制引用类型内部的对象,常用的方式有:

  • 递归调用 clone()(要求所有引用类都实现 Cloneable)。
  • 使用序列化(Serializable + 流复制)。

Q3:原型模式和单例模式冲突吗?

A:单例模式只允许一个实例,原型模式每次克隆产生新实例,两者目标相反,不能同时应用在一个类上。

Q4:Spring 的 prototype 作用域是每次都是全新对象还是克隆?

A:Spring 默认使用反射调用构造器创建新实例,而不是 clone。但概念上属于"原型模式"思想:每次获取都是不同的对象实例。


七、总结

  • 抽象工厂:解决"产品族"创建问题,强调产品间的关联性。
  • 原型模式:解决"昂贵创建"问题,通过克隆简化对象创建。

在实际开发中,如果对象的创建成本主要来自构造器中的复杂逻辑(如大量计算、数据库查询),原型模式可以显著提升性能;如果需要保证不同操作系统下的 UI 控件风格一致,抽象工厂是最佳选择。

相关推荐
想吃火锅100511 天前
【前端手撕】instanceof
前端·javascript·原型模式
UXbot12 天前
帮助企业低门槛开展AI应用开发的平台推荐
前端·低代码·ui·交互·产品经理·原型模式·web app
UXbot12 天前
如何选择适合公司项目的UI设计工具?企业选型指南
前端·低代码·ui·团队开发·原型模式·设计规范·web app
UXbot12 天前
原型设计工具如何帮助新人快速进入产品行业?
前端·低代码·ui·交互·团队开发·原型模式·web app
sunny.day17 天前
js原型与原型链
开发语言·javascript·原型模式·js原型链
UXbot17 天前
AI网页开发工具能替代工具吗?5大平台对比
前端·人工智能·低代码·ui·原型模式·web app
weixin_3077791318 天前
从“大海捞针”到“主动推理”:AI如何重塑云原生故障诊断的根因链
开发语言·人工智能·算法·自动化·原型模式
swordbob18 天前
prototype 注入到 singleton 里,prototype是否还是线程安全的
安全·spring·单例模式·原型模式
isNotNullX19 天前
企业数据中台建设,ETL工具选错了会踩哪些坑?
数据仓库·etl·原型模式
半个烧饼不加肉19 天前
JS 底层探究-- 普通函数和构造函数
开发语言·javascript·原型模式