• 软件设计模式修炼 -- 原型模式



    在软件系统中,有时候需要多次创建某一类型对象,为了简化创建过程,可以只创建一个对象,然后再通过克隆的方法复制出多个相同的对象,这就是原型模式的设计思想。


    模式定义

    原型模式是一种对象创建模式,用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。


    模式结构

    在这里插入图片描述

    1. Prototype(抽象原型类)

      抽象原型类是定义具有克隆自己方法的接口,是所有具体原型类的公共父类,可以是抽象类也可以是接口。

    2. ConcretePrototype(具体原型类)

      具体原型类实现具体克隆方法,在克隆方法中返回自己的一个克隆对象

    3. Client(客户类)

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


    Java对原型模式的支持

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

    需要注意的是能实现克隆的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. 结果分析

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

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


  • 相关阅读:
    深拷贝浅拷贝
    计算属性和监听,computed,watch
    字面量的引用与使用
    MYSQL 触发器
    JavaScript寻找对象方式
    JavaScript事件传播
    HTML 绑定事件
    JavaScript 中的 String()方法
    JavScript re模块
    JavScript Math函数的使用方法
  • 原文地址:https://www.cnblogs.com/Yee-Q/p/12452095.html
Copyright © 2020-2023  润新知