软件设计模式学习(八)原型模式

原型模式

在软件系统中,有时候须要屡次建立某一类型对象,为了简化建立过程,能够只建立一个对象,而后再经过克隆的方法复制出多个相同的对象,这就是原型模式的设计思想。java


模式定义

原型模式是一种对象建立模式,用原型实例指定建立对象的种类,而且经过复制这些原型建立新的对象。ide


模式结构

在这里插入图片描述

  1. Prototype(抽象原型类)测试

    抽象原型类是定义具备克隆本身方法的接口,是全部具体原型类的公共父类,能够是抽象类也能够是接口。this

  2. ConcretePrototype(具体原型类)prototype

    具体原型类实现具体克隆方法,在克隆方法中返回本身的一个克隆对象设计

  3. Client(客户类)code

    让一个原型克隆自身,从而建立一个新的对象,在客户类中只需直接实例化或经过工厂方法等方式建立一个对象,再经过调用该对象的克隆方法复制多个相同的对象。对象


Java对原型模式的支持

Java语言中的原型模式实现很简单,原型模式结构中定义了一个抽象原型类,全部的Java类都继承自java.lang.Object,而Object类提供一个clone方法,能够将一个Java对象复制一份。所以在Java中能够直接使用Object提供的clone()方法来实现对象的克隆。blog

须要注意的是能实现克隆的Java类必须实现一个标识接口Cloneable,表示这个Java类支持复制。若是一个类没有实现这个接口但调用了clone()方法,Java编译器将抛出一个CloneNotSupportedException异常。继承

public class PrototypeDemo implements Cloneable {

    @Override
    protected Object clone() {

        Object o = null;

        try {
            o = super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println("Not support cloneable");
        }
        return o;
    }
}
public class Client {

    PrototypeDemo prototypeDemo = new PrototypeDemo();
    PrototypeDemo prototypeDemo2 = (PrototypeDemo) prototypeDemo.clone();
}


深克隆与浅克隆

一般状况下,一个类包含一些成员对象,在使用原型模式克隆对象时,根据其成员对象是否也克隆,原型模式可分为两种形式:深克隆和浅克隆。

  1. 浅克隆

    被复制对象的全部普通成员变量都具备与原来的对象相同的值,而全部对其余对象的引用仍然指向原来对象。
    在这里插入图片描述

    obj1是原型对象,obj2为复制后对象,containedObj1和containedObj2为成员对象。

  2. 深克隆

    引用其余对象的变量将指向被复制过的新对象,而不是原有被引用的对象。
    在这里插入图片描述

Java语言原型模式的实现

Java语言提供的clone()方法将对象复制一份并返回给调用者,通常而言,clone()方法知足:

  1. 对任何对象x,都有clone() != x,即克隆对象与原对象不是同一个对象。
  2. 对任何对象x,都有x.clone().getClass() == x.getClass(),即克隆对象与原对象的类型同样。
  3. 若是对象的equals()方法定义恰当,那么x.clone().equals(x)应该成立。

为了获取对象的一份拷贝,能够利用Object类的clone()方法,具体步骤以下

  1. 在派生类中覆盖基类的clone()方法,并声明为public。
  2. 在派生类的clone()方法中,调用super.clone()。
  3. 在派生类中实现Cloneable接口

经过覆盖Object类的clone()方法实现浅克隆,若是须要实现深克隆,能够经过序列化等方式来实现。


原型模式实例之邮件复制(浅克隆)

  1. 实例说明

    因为邮件对象包含的内容较多(如发送者、标题、内容、日期、附件等),某系统现需提供一个邮件复制功能,对于已经建立好的邮件对象,能够经过复制的方式建立一个新的邮件对象,若是须要改变某部份内容,无须修改原始的邮件对象,只须要修改复制后获得的邮件对象便可。本例使用浅克隆实现邮件复制,即复制邮件(E-mail)的同时不复制附件。

  2. 实例代码及解释

    1. 抽象原型类Object

      Object做为抽象原型类,提供了克隆方法clone(),用于建立一个原型对象,其clone()方法由JVM完成具体实现,用户在使用时无须关心。

      public class Object {
      
          protected native Object clone() throws CloneNotSupportedException;
      }
    2. 具体原型类Email(邮件类)

      public class Email implements Cloneable {
      
          private Attachment attachment = null;
      
          public Email() {
      
          }
      
          public Email(Attachment attachment) {
              this.attachment = attachment;
          }
      
          public Attachment getAttachment() {
              return attachment;
          }
      
          @Override
          public Object clone() {
      
              Email clone = null;
      
              try {
                  clone = (Email) super.clone();
              } catch (CloneNotSupportedException e) {
                  e.printStackTrace();
              }
              return clone;
          }
      }
    3. 附件类Attachment

      public class Attachment {
      
          public void download() {
              System.out.println("下载附件");
          }
      }
    4. 客户端测试类Client

      public class Client {
      
          public static void main(String[] args) {
      
              Email email = new Email(new Attachment());
      
              Email copyEmail = (Email) email.clone();
      
              System.out.println(email == copyEmail);
              System.out.println(email.getAttachment() == copyEmail.getAttachment());
          }
      }
    5. 结果分析

      编译并运行客户端测试类,输出结果以下:
      在这里插入图片描述

      经过结果能够看出,复制获得的对象与原型对象的引用不一致,但两个对象的成员对象是同一个,说明虽然对象自己复制了一份,但其成员对象在内存中没有复制,原型对象与克隆对象都维持了对相同成员对象的引用。


原型模式实例之邮件复制(深克隆)

  1. 实例说明

    用深克隆实现邮件复制

  2. 实例代码及解释

    1. 具体原型类Email(邮件类)

      Email做为具体类,因为实现的是深克隆,无须使用Object的clone()方法;经过序列化的方法实现深克隆,因为要将Email类型对象写入流中,所以Email类需实现Serializable接口。

      public class Email implements Serializable {
      
          private Attachment attachment = null;
      
          public Email() {
      
              this.attachment = new Attachment();
          }
      
          public Attachment getAttachment() {
      
              return attachment;
          }
      
          public Object deepclone() throws IOException, ClassNotFoundException {
      
              //将对象写入流中
              ByteArrayOutputStream bao = new ByteArrayOutputStream();
              ObjectOutputStream oos = new ObjectOutputStream(bao);
              oos.writeObject(this);
      
              //将对象从流中取出
              ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());
              ObjectInputStream ois = new ObjectInputStream(bis);
              return (ois.readObject());
          }
      }
    2. 附件类Attachment

      Attachment类型对象也将被写入流中,所以也需实现Serializable接口。

      public class Attachment implements Serializable {
      
          public void download() {
              System.out.println("下载附件");
          }
      }
    3. 客户端测试类Client

      public class Client {
      
          public static void main(String[] args) {
      
              Email email, copyEmail = null;
      
              email = new Email();
      
              try {
                  copyEmail = (Email) email.deepclone();
              } catch (IOException e) {
                  e.printStackTrace();
              } catch (ClassNotFoundException e) {
                  e.printStackTrace();
              }
      
              System.out.println(email == copyEmail);
              System.out.println(email.getAttachment() == copyEmail.getAttachment());
          }
      }
    4. 结果分析

      编译并运行客户端测试类,输出结果以下:
      在这里插入图片描述

      经过结果能够看出,复制获得的对象与原型对象的引用不一致,原型对象与克隆对象对成员对象的引用不相同,说明其成员对象也复制了一份。

相关文章
相关标签/搜索