原型模式
定义
用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
结构与说明
Prototype:声明一个克隆自身的接口,用来约束想要克隆自己的类,要求它们克隆
ConcretePrototype:实现Prototype接口的类,这些类真正实现克隆自身的功能
Client:使用原型的客户端,首先要获取到原型实例对象,然后通过原型实例克隆自身来创建新的对象实例。
代码
public interface Prototype {
public Prototype clone();
}
public class ConcretePrototype1 implements Prototype{
@Override
public Prototype clone() {
Prototype prototype = new ConcretePrototype1();
return prototype;
}
}
public class Client {
private Prototype prototype;
public Client(Prototype prototype){
this.prototype=prototype;
}
public void operation(){
Prototype newPrototype = prototype.clone();
}
}
不使用设计模式
instanceof判断类型,然后New对象赋值,增加内容不符合开闭原则
使用设计模式
功能
(1)通过克隆创建新的对象实例。
(2)另一个克隆出来的对象实例复制原型实例属性的值。
原型与new
克隆方法使用new来实现,只是类似于new 而不是就是new。
克隆方法得到的实例通常有值,其中值是原型对象的值,new对象一般没有值,或者只有默认值。
克隆出来的实例不会影响到原型实例。
Java中的克隆方法
java中提供了clone方法,定义在Object类中。需要克隆功能的类,只需要实现java.lang.Cloneable接口,接口没有实现的方法,是一个标志接口。
实现Cloneable接口,重写 clone(调用父类,注意修饰符public)
浅度克隆和深度克隆
(1)浅度克隆:只负责克隆按值传递的数据(基本数据类型、String类型)
(2)深度克隆:除了浅度克隆要克隆的值外,还负责克隆引用类型的数据,基本上就是被克隆实例所有的属性的数据都会被克隆出来。
(3)深度克隆还有一个特点,如果被克隆的对象里面的属性数据是引用类型,属性的类型也是对象,那么需要一直递归的克隆下去。这意味着如果要深度克隆成功,必须要整个克隆所涉及的对象都要正确实现克隆方法,如果没有正确实现克隆,就会导致克隆失败。
原型管理器
如果系统中原型的数目不固定,系统中的原型可以被动态的创建和销毁,那么需要在系统中维护一个当前可用的原型注册表,这个注册表被称为原型管理器。
把原型当成资源的话,原型管理器就相当于一个资源管理器,只不过缓存和管理的是原型实例,除了向原型管理器中添加原型通过new创造对象,其它时候都是通过向原型管理器来请求原型实例,然后通过克隆方法来获取新的对象实例,这就可以实现动态管理或动态切换。
public interface Prototype {
public Prototype clone();
public String getName();
public void setName(String name);
}
public class ConcretePrototype1 implements Prototype{
private String name;
@Override
public Prototype clone() {
ConcretePrototype1 prototype1 = new ConcretePrototype1();
prototype1.setName(name);
return prototype1;
}
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name =name;
}
@Override
public String toString() {
return "ConcretePrototype1{" +
"name='" + name + '\'' +
'}';
}
}
public class ConcretePrototype2 implements Prototype{
private String name;
@Override
public Prototype clone() {
ConcretePrototype2 prototype2 = new ConcretePrototype2();
prototype2.setName(name);
return prototype2;
}
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name =name;
}
@Override
public String toString() {
return "ConcretePrototype2{" +
"name='" + name + '\'' +
'}';
}
}
public class PrototypeManager {
private static Map<String,Prototype> map = new HashMap<String,Prototype>();
private PrototypeManager(){}
public synchronized static void setPrototype(String prototypeId,Prototype prototype){
map.put(prototypeId,prototype);
}
public synchronized static void removePrototype(String prototypeId){
map.remove(prototypeId);
}
public synchronized static Prototype getPrototype(String prototypeId){
Prototype prototype = map.get(prototypeId);
if(prototype == null){
System.out.println("无原型被注册");
}
return prototype;
}
}
public class Client {
public static void main(String[] args) {
Prototype prototype = new ConcretePrototype1();
PrototypeManager.setPrototype("prototype1",prototype);
Prototype p3 = PrototypeManager.getPrototype("prototype1").clone();
p3.setName("a1");
System.out.println(p3);
Prototype prototype1 = new ConcretePrototype2();
PrototypeManager.setPrototype("prototype2",prototype1);
Prototype p4 = PrototypeManager.getPrototype("prototype2").clone();
p4.setName("a2");
System.out.println(p4);
}
}
优缺点
1、对客户端隐藏具体的实现类型
2、在运行时动态改变具体的实现类型
3、深度克隆方法实现会比较困难
本质
克隆生成对象
何时选用
1、如何一个系统想要独立于它想要使用的对象时,可以使用原型模式,让系统只面向接口编程,系统需要新的对象时,通过克隆原型来得到。
2、如果需要实例化的类是在运行时动态指定,可以使用原型模式,通过克隆原型来得到实例。