原型模式 (Prototype Pattern)
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
模式定义
原型模式是一种创建型设计模式,它允许一个对象再创建另外一个可定制的对象,而无需知道任何创建的细节。原型模式通过复制现有实例来创建新实例,避免了重复创建对象的复杂性。
适用场景
- 类初始化需要消耗非常多的资源,这个资源包括数据、硬件资源等
- 通过new产生一个对象需要非常繁琐的数据准备或访问权限
- 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时
- 创建对象的成本比克隆对象的成本大得多时
结构图
graph TD
A[Client] --> B{clone}
B --> C[Prototype]
C --> D{clone}
E[ConcretePrototype] --> F{clone}
F --> G[clone]
实现示例
Java 实现
// 抽象原型类
public abstract class Prototype implements Cloneable {
protected String id;
public Prototype(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public abstract Prototype clone();
}
// 具体原型类
public class ConcretePrototype extends Prototype {
public ConcretePrototype(String id) {
super(id);
}
@Override
public Prototype clone() {
try {
return (Prototype) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException("克隆失败", e);
}
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建原型对象
Prototype prototype = new ConcretePrototype("原型对象");
// 通过克隆创建新对象
Prototype clone = prototype.clone();
clone.setId("克隆对象");
System.out.println("原型对象ID: " + prototype.getId());
System.out.println("克隆对象ID: " + clone.getId());
}
}
经典案例
1. Spring框架中的原型Bean
Spring框架中的原型(Prototype)作用域Bean是原型模式的典型应用:
// Spring中的原型Bean配置
@Component
@Scope("prototype") // 声明为原型作用域
public class UserService {
private String userId;
private List permissions;
// 构造函数
public UserService() {
this.permissions = new ArrayList<>();
}
// getter和setter方法
public String getUserId() { return userId; }
public void setUserId(String userId) { this.userId = userId; }
public List getPermissions() { return permissions; }
public void setPermissions(List permissions) { this.permissions = permissions; }
public void addUserPermission(String permission) {
this.permissions.add(permission);
}
}
// 配置类方式声明原型Bean
@Configuration
public class AppConfig {
@Bean
@Scope("prototype")
public ReportGenerator reportGenerator() {
return new ReportGenerator();
}
}
// 使用原型Bean
@Service
public class BusinessService {
@Autowired
private ApplicationContext context;
public void processUser(String userId) {
// 每次都获取一个新的UserService实例
UserService userService = context.getBean(UserService.class);
userService.setUserId(userId);
userService.addUserPermission("READ");
// 处理用户逻辑...
System.out.println("处理用户: " + userService.getUserId());
}
}
2. Java中的Cloneable接口
Java标准库中的许多类都实现了Cloneable接口,支持原型模式:
// Java标准库中的原型模式应用示例
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JavaPrototypeExample {
public static void main(String[] args) {
// ArrayList实现了Cloneable接口
List originalList = new ArrayList<>();
originalList.add("元素1");
originalList.add("元素2");
originalList.add("元素3");
// 通过clone方法创建副本
List clonedList = (List) ((ArrayList) originalList).clone();
// 修改克隆列表不会影响原始列表
clonedList.add("元素4");
System.out.println("原始列表: " + originalList); // [元素1, 元素2, 元素3]
System.out.println("克隆列表: " + clonedList); // [元素1, 元素2, 元素3, 元素4]
// HashMap也实现了Cloneable接口
Map originalMap = new HashMap<>();
originalMap.put("键1", 1);
originalMap.put("键2", 2);
// 通过clone方法创建副本
Map clonedMap = (Map) ((HashMap) originalMap).clone();
// 修改克隆Map不会影响原始Map
clonedMap.put("键3", 3);
System.out.println("原始Map: " + originalMap); // {键1=1, 键2=2}
System.out.println("克隆Map: " + clonedMap); // {键1=1, 键2=2, 键3=3}
}
}
// 自定义实现Cloneable的类
class Employee implements Cloneable {
private String name;
private int age;
private List skills;
public Employee(String name, int age) {
this.name = name;
this.age = age;
this.skills = new ArrayList<>();
}
// 浅克隆实现
@Override
public Employee clone() throws CloneNotSupportedException {
return (Employee) super.clone();
}
// 深克隆实现
public Employee deepClone() throws CloneNotSupportedException {
Employee cloned = (Employee) super.clone();
cloned.skills = new ArrayList<>(this.skills); // 深拷贝skills列表
return cloned;
}
// getter和setter方法
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public List getSkills() { return skills; }
public void setSkills(List skills) { this.skills = skills; }
public void addSkill(String skill) { this.skills.add(skill); }
}
// 使用示例
class EmployeeCloneExample {
public static void main(String[] args) throws CloneNotSupportedException {
Employee original = new Employee("张三", 30);
original.addSkill("Java");
original.addSkill("Spring");
// 浅克隆
Employee shallowClone = original.clone();
shallowClone.setName("李四");
shallowClone.addSkill("Python"); // 这会影响原始对象,因为skills是共享的
System.out.println("浅克隆后:");
System.out.println("原始员工技能: " + original.getSkills()); // [Java, Spring, Python]
System.out.println("克隆员工技能: " + shallowClone.getSkills()); // [Java, Spring, Python]
// 深克隆
Employee deepClone = original.deepClone();
deepClone.setName("王五");
deepClone.addSkill("JavaScript");
System.out.println("\n深克隆后:");
System.out.println("原始员工技能: " + original.getSkills()); // [Java, Spring, Python]
System.out.println("克隆员工技能: " + deepClone.getSkills()); // [Java, Spring, Python, JavaScript]
}
}
3. 游戏开发中的原型模式
在游戏开发中,原型模式常用于创建大量的相似对象:
// 游戏中的敌人原型
abstract class Enemy implements Cloneable {
protected String name;
protected int health;
protected int attackPower;
protected String weapon;
public abstract void attack();
public abstract void move();
// 克隆方法
public Enemy clone() throws CloneNotSupportedException {
return (Enemy) super.clone();
}
// getter和setter方法
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getHealth() { return health; }
public void setHealth(int health) { this.health = health; }
public int getAttackPower() { return attackPower; }
public void setAttackPower(int attackPower) { this.attackPower = attackPower; }
public String getWeapon() { return weapon; }
public void setWeapon(String weapon) { this.weapon = weapon; }
}
// 具体的敌人类
class Orc extends Enemy {
public Orc() {
this.name = "兽人";
this.health = 100;
this.attackPower = 20;
this.weapon = "斧头";
}
@Override
public void attack() {
System.out.println(name + "挥舞" + weapon + "攻击,造成" + attackPower + "点伤害");
}
@Override
public void move() {
System.out.println(name + "缓慢地移动");
}
}
class Goblin extends Enemy {
public Goblin() {
this.name = "哥布林";
this.health = 50;
this.attackPower = 10;
this.weapon = "短剑";
}
@Override
public void attack() {
System.out.println(name + "用" + weapon + "快速攻击,造成" + attackPower + "点伤害");
}
@Override
public void move() {
System.out.println(name + "敏捷地移动");
}
}
// 敌人工厂使用原型模式
class EnemyFactory {
private Map prototypes = new HashMap<>();
public EnemyFactory() {
// 初始化原型对象
prototypes.put("orc", new Orc());
prototypes.put("goblin", new Goblin());
}
public Enemy createEnemy(String type) throws CloneNotSupportedException {
Enemy prototype = prototypes.get(type);
if (prototype != null) {
return prototype.clone();
}
throw new IllegalArgumentException("未知的敌人类型: " + type);
}
}
// 游戏场景使用示例
public class GamePrototypeExample {
public static void main(String[] args) {
EnemyFactory factory = new EnemyFactory();
try {
// 创建大量敌人对象
List enemies = new ArrayList<>();
// 创建5个兽人
for (int i = 0; i < 5; i++) {
Enemy orc = factory.createEnemy("orc");
orc.setName("兽人" + (i + 1));
enemies.add(orc);
}
// 创建10个哥布林
for (int i = 0; i < 10; i++) {
Enemy goblin = factory.createEnemy("goblin");
goblin.setName("哥布林" + (i + 1));
enemies.add(goblin);
}
// 让所有敌人行动
for (Enemy enemy : enemies) {
enemy.move();
enemy.attack();
}
System.out.println("总共创建了 " + enemies.size() + " 个敌人");
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
优缺点
优点
- 性能提高,直接在内存中拷贝,比new一个对象性能要好
- 逃避构造函数的约束,直接在内存中拷贝,不执行构造函数
- 简化对象的创建过程,使得创建对象就像复制粘贴一样简单
缺点
- 配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易
- 必须实现Cloneable接口
- 在实现深克隆时可能需要编写较为复杂的代码