所谓原型模式就是从原型实例去复制克隆出新的实例,而绝不是去从类去实例化。就好比打飞机的游戏,我们操作的主角飞机只有一架,可以用单例模式去实现,而敌机好多都是一样的,如果每出一个敌机我们就去new一个敌机的对象,一下来个三十个,就去new三十个敌机的对象吗?那显而会造成极大的内存浪费,这个时候考虑使用原型模式是非常好的。
如何去使用原型模式呢?,java中所有的类都是从java.lang.Object继承而来,而Object类提供了一个对对象进行复制的方法。
protected native Object clone() throws CloneNotSupportedException;
我们可以使用这个克隆方法来将我们的对象克隆一个。但在使用时,我们需要克隆的对象必须实现Cloneable接口,这个接口的作用只有一个,就是在运行时期通知java虚拟机可以安全的在这个类上使用clone()方法,如果没有实现Cloneable接口,调用clone()方法就会抛出CloneNotSupportedException异常。
一般而言,克隆方法满足以下的条件:
(1)对任何对象x,都有:x.clone() != x。也就是说克隆对象和元对象不是同一个对象。
(2)对任何对象x,都有:x.clone().getClass() == x.getClass(),也就是说,克隆兑现与元对象的类型一样。
(3)如果对象x的equals方法定义的恰当的话,x.clone().equals(x)结果是true。
一般来说前两个是必须的,第三个是可选的。
1、简单形式的原型模式
这种形式设计到三个角色:
客户(Client)角色:客户类提出创建对象的请求。
抽象原型(Prototype):这是一个抽象角色,通常由一个java借口或者java抽象类实现,此角色给出所有具体原型类所需的接口。
具体原型(Concrete Prototype)角色:被复制的对象,此角色需要实现抽象的原型角色所要求的接口。
Client.java
public class Client {
private Prototype prototype;
public void operation(Prototype example){
Prototype p = (Prototype) example.clone();
}
}
Prototype.java //抽象原型角色,因为本身是个接口,所以只能继承Cloneable,然后申明一个clone()方法。
public interface Prototype extends Cloneable {
Object clone();
}
ConcretePrototype.java // 具体原型角色实现clone()方法
public class ConcretePrototype implements Prototype {
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
2、登记式原型模式
这个形式比简单式的多了一个原型管理器(Prototype Manager)的角色:穿件具体原型类的对象,并记录每一个被创建的对象。
Prototype.java
public interface Prototype extends Cloneable{
public Object clone();
}
ConcretePrototypee.java
public class ConcretePrototypee implements Prototype {
@Override
public synchronized Object clone() {
Prototype prototype = null;
try {
prototype =(Prototype) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return prototype;
}
}
PrototypeManager.java //作为对所有对象的登记,这个角色提供必要的方法供外界增加新的原型对象和取得已经登记过的原型对象。
public class PrototypeManager {
private Vector object = new Vector();
/**
* 聚集管理方法:增加一个新的对象
*/
public void add(Prototype prototype){
object.add(prototype);
}
/**
* 聚集管理方法:取出聚集中的一个对象
*/
public Prototype get(int i){
return (Prototype) object.get(i);
}
public int getSize(){
return object.size();
}
}
Client.java
public class Client {
private PrototypeManager pm;
private Prototype prototype;
public void registerPrototype(){
prototype = new ConcretePrototypee();
Prototype copytype = (Prototype) prototype.clone();
pm.add(copytype);
}
}
两种形式比较
如果需要床架男的原型对象数据较少而且比较固定,可以采取简单形式。如果创建的对象数目不固定,可以采取登记式的形式。在复制一个原型对象之前,客户端可以查看管理员对象是否已经由一个满足要求的原型对象,如果有,可以直接从管理员类取得这个对象引用,如果么有,客户端就需要自行复制此远行对象。
举个简单的例子:
Resume.java
public class Resume implements Cloneable {
private String name;
private Integer age;
private String sex;
private Integer high;
public Resume(String name) {
this.name = name;
}
public void setInfo(Integer age, String sex, Integer high){
this.age = age;
this.sex = sex;
this.high = high;
}
@Override
public Object clone(){
Resume resume = null;
try {
resume = (Resume) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return resume;
}
public void display(){
System.out.println("姓名:"+name);
System.out.println("年龄:"+age);
System.out.println("性别:"+sex);
System.out.println("身高:"+high+"cm");
}
}
Client.java
public class Client {
/**
* Resume client
*/
public static void main(String[] args) {
Resume resume = new Resume("威酱");
resume.setInfo(18,"男",180);
Resume b = (Resume) resume.clone();
System.out.println("=====resume=====");
resume.display();
System.out.println("=====B=====");
b.display();
System.out.println("resume==b?"+(resume == b));
System.out.println("resume.clone().getClass()==resume.getClass()?"+(resume.getClass()==b.getClass()));
System.out.println("b.equals(resume)?"+b.equals(resume));
}
}
输出结果:
=====resume=====
姓名:威酱
年龄:18
性别:男
身高:180cm
=====B=====
姓名:威酱
年龄:18
性别:男
身高:180cm
resume==b?false
resume.clone().getClass()==resume.getClass()?true
b.equals(resume)?false
最后原型模式中,咱们还得简单的说一下两个该念:深拷贝和浅拷贝。
浅拷贝:被复制的对象所有变量都含有与原来对象相同的值,而所有的对其他对象的引用都任然指向原来的对象,也即是说,浅拷贝仅仅只是复制了对象,而不会复制它所引用的对象。
深拷贝:被复制对象的所有变量 都将有 与原来对象 相同的值,那些引用其他对象的变量,将指向被复制过得新对象,而不再是原有的那些被引用的对象。换言之,深拷贝把要复制的对象所引用的对象都复制的一遍。