简介
原型模式(Prototype Pattern)指原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象,属于创建型设计模式。
应用场景
(1)创建对象成本较大(例如,初始化时间长,占用CPU太多,或者占用网络资源太多等),需要优化资源。
(2)创建一个对象需要烦琐的数据准备或访问权限等,需要提高性能或者提高安全性。
(3)系统中大量使用该类对象,且各个调用者都需要给它的属性重新赋值。
在Spring中,原型模式应用得非常广泛,例如scope="prototype"、JSON.parseObject(),都是原型模式的具体应用。
优点
(1)Java自带的原型模式基于内存二进制流的复制,在性能上比直接new一个对象更加优良。
(2)可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
缺点
(1)需要为每一个类都配置一个clone方法。
(2)clone方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。 (3)当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。
通用模板
-
创建原型接口:规定复制接口。
java// 抽象原型 public interface IPrototype<T> { T clone(); }
-
具体原型类:被复制的对象。
javapublic class Prototype implements IPrototype<Prototype> { private String desc; public Prototype(String desc) { this.desc = desc; } public Prototype clone() { // 进行复制 return new Prototype(desc); } @Override public String toString() { return "Prototype{" + "desc='" + desc + '\'' + '}'; } }
模板测试
-
代码
javapublic class Client { public static void main(String[] args) { // 创建原型对象 Prototype prototypeA = new Prototype("PrototypeA"); System.out.println(prototypeA); // 复制原型对象 Prototype clone = prototypeA.clone(); System.out.println(clone); } }
-
结果
textPrototype{desc='PrototypeA'} Prototype{desc='PrototypeA'}
Java Object clone方法
Java的Object提供了一个clone()方法,它的意图就是复制一个新的对象出来,我们需要实现一个Cloneable接口来标识一个对象是"可复制"的:
-
原型模型写法
javapublic class Student implements Cloneable { private int id; private String name; private int score; // 复制新对象并返回: public Object clone() { Student std = new Student(); std.id = this.id; std.name = this.name; std.score = this.score; return std; } }
-
测试
javapublic class Client { public static void main(String[] args) { Student std1 = new Student(); std1.setId(123); std1.setName("Bob"); std1.setScore(88); // 复制新对象: Student std2 = (Student) std1.clone(); System.out.println(std1); System.out.println(std2); System.out.println(std1 == std2); // false } }
-
结果
textcom.demo.Student@7f31245a com.demo.Student@6d6f6e28 false
"生搬硬套"实战
场景描述
假设我们有一个游戏场景,游戏中有很多不同类型的怪物(Monster),我们需要不断创建怪物对象来填充游戏世界。每个怪物都有自己的属性,比如生命值(health)、攻击力(attack)等。如果我们每次都通过构造函数来创建一个新的怪物实例并设置其属性,那么这将非常耗时且代码会变得冗长。这时,原型模式就可以派上用场了。
代码开发
java
public class Monster implements Cloneable {
private int health;
private int attack;
public Monster(int health, int attack) {
this.health = health;
this.attack = attack;
}
// 获取健康值
public int getHealth() {
return health;
}
// 设置健康值
public void setHealth(int health) {
this.health = health;
}
// 获取攻击力
public int getAttack() {
return attack;
}
// 设置攻击力
public void setAttack(int attack) {
this.attack = attack;
}
// 实现克隆方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
- 测试
java
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
// 创建一个原型怪物
Monster prototypeMonster = new Monster(100, 50);
// 使用原型模式克隆出多个怪物
Monster monster1 = (Monster) prototypeMonster.clone();
Monster monster2 = (Monster) prototypeMonster.clone();
// 修改每个怪物的特性
monster1.setHealth(90);
monster2.setAttack(60);
System.out.println("Monster 1: Health=" + monster1.getHealth() + ", Attack=" + monster1.getAttack());
System.out.println("Monster 2: Health=" + monster2.getHealth() + ", Attack=" + monster2.getAttack());
}
}
总结
原型模式的核心在于复制原型对象。以系统中已存在的一个对象为原型,直接基于内存二进制流进行复制,不需要再经历耗时的对象初始化过程(不调用构造函数),性能提升许多。当对象的构建过程比较耗时时,可以把当前系统中已存在的对象作为原型,对其进行复制(一般是基于二进制流的复制),躲避初始化过程,使得新对象的创建时间大大缩短。