一、简述
原型模式属于对象的创建模式,通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象,这就是原型模式的用意。
二、原型模式结构
原型模式要求对象实现一个可以克隆自身的接口,这样就可以通过复制一个实例对象本身来创建一个新的实例。通过原型实例创建新的对象,就不再需要关心这个实例本身的类型,只要实现了克隆自身的方法,就可以通过这个方法来获取新的对象,而无须再通过 new 去创建。原型模式涉及到三个角色:
1️⃣客户角色(Client)
客户类提出创建对象的请求。
2️⃣抽象原型角色(Prototype)
这是一个抽象角色,通常由一个接口或者抽象类实现,此角色给出所有具体原型类所需的实现的接口。
3️⃣具体原型角色(Concrete Prototype)
被复制的角色,此角色需要实现抽象的原型角色所要求的克隆相关的接口。
三、原型模式示例
1️⃣定义一个抽象原型角色,抽象类,实现 Cloneable 接口:
public abstract class Prototype implements Cloneable {
public Prototype clone() {
Prototype prototype = null;
try {
prototype = (Prototype) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return prototype;
}
public abstract void show();
}
2️⃣定义一个具体原型角色,继承 Prototype 类:
public class ConcretePrototype extends Prototype {
@Override
public void show() {
System.out.println("ConcretePrototype.show()");
}
}
3️⃣定义一个客户端调用:
public static void main(String[] args) {
ConcretePrototype cp = new ConcretePrototype();
for (int i = 0; i < 5; i++) {
ConcretePrototype cpClone = (ConcretePrototype) cp.clone();
cpClone.show();
}
}
比方说一个类实例很有用的时候,就可以使用原型模式去复制它。不过原型模式单独用得不多,一般是和其他设计模式一起使用。
四、原型模式的应用及解读
既然原型模式的关注点是在于通过克隆自身来获取一个和自身一样的对象,那其实只要是实现了 Cloneable 接口的类都可以算是原型模式的应用,比如 ArrayList :
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
...
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
...
}
程序中获取到了一个 ArrayList 的实例 arrayList,完全可以通过调用 arrayList.clone() 获取到原 ArrayList 的拷贝。
五、原型模式的优点
原型模式是一种类的创建模式,可以看到到目前为止的四种创建型模式,客户端(调用方)都没有直接 new 一个类实例出来。把 new 一个类实例的动作由客户端(调用方)交给别人做而不是自己做,这就是创建型模式的宗旨。
使用原型模式创建对象比直接 new 一个对象在性能上好得多,因为 Object 类的 clone() 是一个 native 方法,它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。
使用原型模式的另一个好处是简化对象的创建,使得创建对象就像普通的复制粘贴一样简单。