原型模式(Prototype Pattern)详解
一、原型模式简介
原型模式(Prototype Pattern) 是一种 创建型设计模式 (对象创建型模式),它通过复制一个已有的对象来创建新对象,而不是通过构造函数或工厂方法来创建。这个"已有对象"被称为"原型(Prototype)"。
使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象。
简单来说:
"你有一个对象 A,想再创建一个和 A 一样的对象 B,就直接'克隆'A。"
孙悟空"拔毛变小猴"
工作原理:将一个原型对象传给要发动创建的对象(即客户端对象),这个要发动创建的对象通过请求原型对象复制自己来实现创建过程
创建新对象(也称为克隆对象)的工厂就是原型类自身,工厂方法由负责复制原型对象的克隆方法来实现
通过克隆方法所创建的对象是全新的对象,它们在内存中拥有新的地址,每一个克隆对象都是独立的
通过不同的方式对克隆对象进行修改以后,可以得到一系列相似但不完全相同的对象
原型模式包含以下3个角色 :
Prototype(抽象原型类)
ConcretePrototype(具体原型类)
Client(客户类)
原型模式的结构 :
二、解决的问题类型
原型模式主要用于解决以下问题:
- 频繁创建相似对象时效率低:比如创建对象的成本很高(如数据库查询、网络请求等),使用克隆可以节省资源。
- 希望隐藏对象的创建细节:客户端不关心具体类,只需要知道如何克隆即可。
- 需要动态加载类的场景:运行时根据配置动态决定要创建的对象类型。
- 创建新对象成本较大:新对象可以通过复制已有对象来获得,如果是相似对象,则可以对其成员变量稍作修改
- 系统要保存对象的状态而对象的状态变化很小
- 需要避免使用分层次的工厂类来创建分层次的对象: 并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更加方便
三、使用场景
场景 | 示例 |
---|---|
对象创建成本高 | 如数据库连接池中的连接对象 |
动态配置对象 | 根据不同参数创建不同的配置对象 |
复杂对象复制 | 比如复杂的图形、文档结构等 |
插件化系统 | 运行时加载类并克隆实例 |
四、核心概念
1. Cloneable
接口(Java 中)
Java 提供了 Cloneable
接口,用于标识该类的对象可以被克隆。
2. clone()
方法
继承 Object
的所有类都默认拥有 clone()
方法,但它是 protected 的,所以需要重写为 public 才能调用。
3. 浅拷贝 vs 深拷贝(浅克隆与深克隆)
- 浅拷贝:只复制基本数据类型字段,引用类型的字段仍指向原对象的引用。
- 深拷贝:递归复制所有层级的对象,真正意义上的"完全独立"。
浅克隆(Shallow Clone) :当原型对象被复制时,只复制它本身和其中包含的值类型的成员变量,而引用类型的成员变量并没有复制
深克隆(Deep Clone):除了对象本身被复制外,对象所包含的所有成员变量也将被复制
五、代码案例(Java)
我们以一个简单的用户配置对象为例,演示原型模式的使用。
1. 定义可克隆的类
java
// 可克隆的用户配置类
class UserPreferences implements Cloneable {
private String theme;
private int fontSize;
private List<String> favoriteColors;
public UserPreferences(String theme, int fontSize, List<String> favoriteColors) {
this.theme = theme;
this.fontSize = fontSize;
this.favoriteColors = favoriteColors;
}
// 重写 clone() 方法(浅拷贝)
@Override
protected UserPreferences clone() {
try {
return (UserPreferences) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException("Clone not supported", e);
}
}
// Getter 和 Setter 省略
public void display() {
System.out.println("Theme: " + theme + ", Font Size: " + fontSize + ", Favorite Colors: " + favoriteColors);
}
public List<String> getFavoriteColors() {
return favoriteColors;
}
}
2. 使用原型模式克隆对象
java
public class Client {
public static void main(String[] args) {
// 创建原型对象
List<String> colors = new ArrayList<>(Arrays.asList("Blue", "Green"));
UserPreferences original = new UserPreferences("Dark", 14, colors);
// 克隆对象
UserPreferences copy = original.clone();
// 修改副本内容
copy.getFavoriteColors().add("Red");
// 输出验证
System.out.println("Original:");
original.display(); // 输出也包含 Red,说明是浅拷贝
System.out.println("Copy:");
copy.display();
}
}
3. 实现深拷贝(修改部分代码)
如果你希望实现深拷贝,需要手动复制引用类型字段:
java
@Override
protected UserPreferences clone() {
try {
UserPreferences clone = (UserPreferences) super.clone();
// 深拷贝:复制引用类型字段
clone.favoriteColors = new ArrayList<>(this.favoriteColors);
return clone;
} catch (CloneNotSupportedException e) {
throw new RuntimeException("Clone not supported", e);
}
}
典型代码(c++)
cpp
abstract class Prototype
{
public abstract Prototype Clone();
}
class ConcretePrototype : Prototype
{
private string attr; //成员变量
public string Attr
{
get { return attr; }
set { attr = value; }
}
//克隆方法
public override Prototype Clone()
{
ConcretePrototype prototype = new ConcretePrototype();
prototype.Attr = attr;
return prototype;
}
}
cpp
......
ConcretePrototype prototype = new ConcretePrototype();
ConcretePrototype copy = (ConcretePrototype)prototype.Clone();
......
其他案例
- 在使用某OA系统时,有些岗位的员工发现他们每周的工作都大同小异,因此在填写工作周报时很多内容都是重复的,为了提高工作周报的创建效率,大家迫切地希望有一种机制能够快速创建相同或者相似的周报,包括创建周报的附件。试使用原型模式对该OA系统中的工作周报创建模块进行改进。
- 邮件复制(浅克隆):由于邮件对象包含的内容较多(如发送者、接收者、标题、内容、日期、附件等),某系统中现需要提供一个邮件复制功能,对于已经创建好的邮件对象,可以通过复制的方式创建一个新的邮件对象,如果需要改变某部分内容,无须修改原始的邮件对象,只需要修改复制后得到的邮件对象即可。使用原型模式设计该系统。在本实例中使用浅克隆实现邮件复制,即复制邮件(Email)的同时不复制附件(Attachment)。
- 邮件复制(深克隆)
使用深克隆实现邮件复制,即复制邮件的同时复制附件。
六、优缺点分析
优点 | 描述 |
---|---|
✅ 提高性能 | 避免重复构造复杂对象,直接复制更高效 |
✅ 简化对象创建过程 | 客户端无需了解对象的具体构造逻辑,提供了简化的创建结构,原型模式中产品的复制是通过封装在原型类中的克隆方法实现的,无须专门的工厂类来创建产品 |
✅ 解耦 | 不依赖具体类名,只需知道原型即可创建新对象,扩展性较好 |
其他 | 可以使用深克隆的方式保存对象的状态,以便在需要的时候使用,可辅助实现撤销操作 |
缺点 | 描述 |
---|---|
❌ 实现深拷贝复杂 | 尤其当对象嵌套多层引用时,需要递归复制。在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重的嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来可能会比较麻烦 |
❌ 破坏封装性 | 如果对象内部状态较多,直接克隆可能暴露细节 |
❌ 违反开闭原则 | 如果类未实现 Cloneable ,需要修改才能支持。需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时,需要修改源代码,违背了开闭原则 |
七、与其他模式对比(补充)
模式名称 | 目标 |
---|---|
工厂模式 | 通过工厂统一创建对象,强调封装创建逻辑 |
建造者模式 | 分步骤构建复杂对象 |
原型模式 | 通过克隆已有对象创建新对象,强调复用 |
八、最终小结
原型模式是一种非常实用的设计模式,特别适合那些:
- 创建对象成本较高;
- 需要动态创建对象;
- 或者对象之间差异较小、主要靠复制后微调的场景。
掌握原型模式可以帮助你在某些特定业务场景中提升性能、简化逻辑,并实现灵活的对象创建机制。
📌 一句话总结:
原型模式就像复印机一样,把一个对象"复印"一份,避免每次都重新打印一张新纸。
✅ 推荐使用方式:
- 如果对象结构简单,使用浅拷贝即可。
- 如果对象包含嵌套引用结构,请务必实现深拷贝。
- 可结合 JSON 序列化等方式实现通用深拷贝(如使用 Gson、Jackson 等库)。
九、扩展
原型管理器
原型管理器(Prototype Manager)将多个原型对象存储在一个集合中供客户端使用,它是一个专门负责克隆对象的工厂,其中定义了一个集合用于存储原型对象,如果需要某个原型对象的一个克隆,可以通过复制集合中对应的原型对象来获得
(带原型管理器的原型模式如下)
cpp
using System.Collections;
class PrototypeManager
{
Hashtable ht = new Hashtable(); //使用Hashtable存储原型对象
public PrototypeManager()
{
ht.Add("A", new ConcretePrototypeA());
ht.Add("B", new ConcretePrototypeB());
}
public void Add(string key, Prototype prototype)
{
ht.Add(key,prototype);
}
public Prototype Get(string key)
{
Prototype clone = null;
clone = ((Prototype)ht[key]).Clone(); //通过克隆方法创建新对象
return clone;
}
}
部分内容由AI大模型生成,注意识别!