首页 > 技术文章 > 原型模式

blogtech 2019-07-08 11:06 原文

原型模式算是JAVA中最简单的设计模式了,原因是因为它已经被提供了语言级的支持,但是如果提到它的实现原理,又是最复杂的一个设计模式。

(1)先看一下原型模式的UML类图

  其中,Prototype是一个原型的抽象类或借口,它里面有一个共有方法,叫clone。ConcretePrototype1与ConcretePrototype2是两个具体的实例,继承或实现了Prototype。这就对应了定义中用原型实例指定创建对象的种类。Client是客户端类,它与Prototype是关联的关系,即在Client类的实例中,有Prototype的对象。客户端可以通过调用Prototype的clone方法来对实现了Prototype的ConcretePrototype1或ConcretePrototype2的对象进行复制来创建新对象,这样比new会有更高的执行效率。

 

(2)下面我们先来看看这个又简单又复杂的设计模式的定义

定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

定义比较简单,总结一下是通过实例指定种类,通过拷贝创建对象。

在JAVA语言中使用原型模式是非常简单的,这是因为Object类当中提供了一个本地方法clone,而JAVA中的任何类只要实现了Cloneable标识接口,就可以使用clone方法来进行对象的拷贝。

我们写一个简单的实例来测试一下,很简单:

public class Prototype implements Cloneable {
 
        private int x;
 
        private int y;
 
        private int z;
 
        public Prototype() {
 
                this.x = 2;
 
                this.y = 3;
 
                this.z = 4;
 
        }
 
        public void change() {
 
                this.x = 9;
 
                this.y = 8;
 
                this.z = 7;
 
        }
 
        public Prototype clone() {
 
                Object object = null;
 
                try {
 
                        object = super.clone();
 
                } catch (CloneNotSupportedException exception) {
 
                        throw new RuntimeException(exception);
 
                }
 
                return (Prototype) object;
 
        }
 
        public String toString() {
 
                return "[" + x + "," + y + "," + z + "]";
 
        }
 
        public static void main(String[] args) {
 
                Prototype prototype1 = new Prototype();
 
                prototype1.change();
 
                System.out.println(prototype1);
 
                Prototype prototype2 = prototype1.clone();
 
                System.out.println(prototype2);
 
        }
 
}
 

输出结果:

  [9,8,7]

  [9,8,7] 

   从输出结果可以看出来,clone方法将prototype1复制了一个,然后赋给了prototype2,这就像复制粘贴一样。值得注意的是,在使用Object.clone()方法去拷贝一个对象时,构造方法是不被执行的,否则prototype2实例中x,y,z的值应该为2,3,4才对,如果你觉得不够直观,可以在构造方法里写一个输出语句试试。

(3)从原型模式的使用方式不难推断出,原型模式常使用于以下场景:

       1、对象的创建非常复杂,可以使用原型模式快捷的创建对象。

       2、在运行过程中不知道对象的具体类型,可使用原型模式创建一个相同类型的对象,或者在运行过程中动态的获取到一个对象的状态。

对于clone方法,它执行的是浅拷贝,也就是说如果是引用类型的属性,则它不会进行拷贝,而是只拷贝引用。

       看下面这个简单的测试,就能看出来了。

class Field implements Cloneable{       
 
        private int a;
 
        public int getA() {
 
                return a;
 
        }
 
        public void setA(int a) {
 
                this.a = a;
 
        }
 
       
 
        protected Field clone() {
 
                Object object = null;
 
                try {
 
                        object = super.clone();
 
                } catch (CloneNotSupportedException exception) {
 
                        throw new RuntimeException(exception);
 
                }
 
                return (Field) object;
 
        }
 
       
 
}
 
 
 
public class DeepPrototype implements Cloneable {
 
        private int x;
 
        private int y;
 
        private int z;
 
        private Field field;
 
        public DeepPrototype() {
 
                this.x = 2;
 
                this.y = 3;
 
                this.z = 4;
 
                this.field = new Field();
 
                this.field.setA(5);
 
        }
 
       
 
        public Field getField() {
 
                return field;
 
        }
 
        protected DeepPrototype clone() {
 
                Object object = null;
 
                try {
 
                        object = super.clone();
 
                        ((DeepPrototype)object).field = this.field.clone();
 
                } catch (CloneNotSupportedException exception) {
 
                        throw new RuntimeException(exception);
 
                }
 
                return (DeepPrototype) object;
 
        }
 
        public String toString() {
 
                return "[" + x + "," + y + "," + z + "," + field.getA() + "]";
 
        }
 
        public static void main(String[] args) {
 
                DeepPrototype prototype1 = new DeepPrototype();
 
                System.out.println(prototype1);
 
                System.out.println(prototype1.getField());
 
                DeepPrototype prototype2 = prototype1.clone();
 
                System.out.println(prototype2);
 
                System.out.println(prototype2.getField());
 
        }
 
}

输出结果:

  [2,3,4,5]

  com.prototype.Field@a90653

  [2,3,4,5]

  com.prototype.Field@de6ced

(4)下面我们来看下原型模式的主要优点:

        1、由于clone方法是由虚拟机直接复制内存块执行,所以在速度上比使用new的方式创建对象要快。

        2、可以基于原型,快速的创建一个对象,而无需知道创建的细节。

        3、可以在运行时动态的获取对象的类型以及状态,从而创建一个对象。

  然而原型模式的缺点也是相当明显的,主要的缺点就是实现深度拷贝比较困难,需要很多额外的代码量。

       不过实际当中我们使用原型模式时,也可以写一个基类实现Cloneable接口重写clone方法,然后让需要具有拷贝功能的子类继承自该类,这是一种节省代码量的常用方式。像上面的例子一样,如果一个类继承自Prototype,则会自动具有拷贝功能。

推荐阅读