原型模式
在软件设计中,我们经常需要频繁创建大量结构相同、内容类似的对象,传统方式往往通过 `new` 的方式反复构造。但是对于某些对象来说,**构建成本高、配置复杂、甚至依赖外部资源**。
这时,一个非常优雅的解决方案就是 **原型模式(Prototype Pattern)**。
一、什么是原型模式?
**原型模式(Prototype Pattern)**:
> 使用原型实例指定创建对象的种类,并通过复制这些原型创建新的对象。
也就是说,不再用 `new Xxxx()`,而是 **通过克隆(clone)直接复制已有对象**。
官方话术听起来很抽象?
一句话总结就是:
> **原型模式就是用"复制"代替"重新创建"。**
二、为什么需要原型模式?
原型模式适用于以下场景:
1. 对象创建成本高
例如:
* 复杂对象构造
* 大量字段初始化
* IO 读取配置后生成对象
使用 clone 可以避免重复初始化。
2. 需要大量相似对象
比如:
* 游戏中大量相似的怪物对象
* 工厂中创建大量模板对象
* 配置对象复制并局部修改
3. 需要在运行时动态决定对象类型
不通过 if-else / new 语句
而通过原型列表(原型注册表)创建。
三、原型模式结构(UML 类图)
```
┌─────────────────────┐
│ Prototype 原型接口 │<────────┐
└─────────────────────┘ │ clone()
▲ │
│ │
┌───────────────────────┐ │
│ ConcretePrototype 具体原型类 │
└───────────────────────┘
│
│ clone()
▼
┌──────────────────────┐
│ Client 客户端 │
└──────────────────────┘
```
四、Java 实现原型模式(浅拷贝版本)
下面用你的例子:**飞机 Plane 克隆**。
1. 原型接口
```java
public interface PlanePrototype extends Cloneable {
PlanePrototype clonePlanePrototype();
}
```
2. 具体原型类
```java
public class Plane implements PlanePrototype {
private String planeName;
private String planeId;
public Plane(String planeName, String planeId) {
this.planeName = planeName;
this.planeId = planeId;
}
@Override
public PlanePrototype clonePlanePrototype() {
try {
return (Plane) super.clone(); // 浅拷贝
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
@Override
public String toString() {
return planeName + " - " + planeId;
}
}
```
3. 客户端调用
```java
public class ClientDemo {
public static void main(String[] args) {
Plane plane = new Plane("飞机1", "001");
Plane copy = (Plane) plane.clonePlanePrototype();
System.out.println(plane);
System.out.println(copy);
}
}
```
输出结果两个对象内容一样,但它们是 **不同的对象实例**。
五、浅拷贝 vs 深拷贝
浅拷贝:复制对象,但共享引用字段
如果对象内部有引用类型:
```java
class Plane {
Engine engine;
}
```
浅拷贝会导致:
```
plane.engine == copy.engine (true)
```
两个对象共享同一个 engine → 有风险。
深拷贝:复制对象 + 复制内部引用对象
深拷贝实现方式:
```java
@Override
public PlanePrototype clonePlanePrototype() {
try {
Plane plane = (Plane) super.clone();
plane.engine = this.engine.clone(); // 深拷贝引用对象
return plane;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
```
适合复杂对象复制。
六、原型注册表(Prototype Manager)
当系统需要**按类型创建对象**时,可以把原型对象放到 Map 中:
```java
Map<String, PlanePrototype> registry = new HashMap<>();
registry.put("fighter", new Plane("战斗机", "F-001"));
registry.put("civil", new Plane("客机", "C-001"));
// 根据 key 复制原型
Plane clone = (Plane) registry.get("fighter").clonePlanePrototype();
```
→ 运行时动态决定"类"是什么,不需要写 new。
这是原型模式的进阶用法。
七、原型模式的优点
**提高对象创建性能**(避免重复构造)
**避免重复初始化**
**运行时动态生成对象**
**简化代码,只需 clone 就能创建大量对象**
八、原型模式的缺点
深拷贝复杂
clone() 容易滥用
Java 的 Cloneable 接口设计较老旧(不是最优雅)
如果对象内部结构复杂,深拷贝维护成本较高。
九、实际开发中何时使用原型模式?
推荐场景:
需要频繁创建结构类似的对象
创建成本高的对象(复杂构造、运行依赖)
配置类模板复制
游戏开发、图形系统等需要大量相似对象的场景
不推荐场景:
对象结构非常复杂
有大量需要深拷贝的对象
clone() 使用会降低代码可读性
十、总结
原型模式是一种简单却非常实用的设计模式,本质是:
> **用已有对象当作模板,通过克隆来创建新对象。**
优点是提升性能、减少 new 复杂对象的开销;缺点是深拷贝难维护。
对于 Java 开发来说,掌握浅拷贝 + 深拷贝 + Prototype 注册表,就已经足够应对绝大多数项目场景。