• 设计模式学习(五):原型模式


    设计模式学习(五):原型模式

    作者:Grey

    原文地址:

    博客园:设计模式学习(五):原型模式

    CSDN:设计模式学习(五):原型模式

    原型模式

    原型模式是创建型模式。

    如果对象的创建成本比较大,而同一个类的不同对象之间差别不大(大部分字段的值都相同),在这种情况下,我们可以利用对已有对象(原型)进行复制(或者叫拷贝)的方式来创建新对象,以达到节省创建时间的目的。这种基于原型来创建对象的方式就叫作原型设计模式。

    实际上,创建对象包含的申请内存、给成员变量赋值这一过程,本身并不会花费太多时间,或者说对于大部分业务系统来说,这点时间完全是可以忽略的。应用一个复杂的模式,只得到一点点的性能提升,这就是所谓的过度设计,得不偿失。但是,如果对象中的数据需要经过复杂的计算才能得到(比如排序、计算哈希值),或者需要从 RPC 、网络、数据库、文件系统等非常慢速的 I/O 中读取,每次读取一次的代价都很高,在这种情况下,我们就可以利用原型模式,从其他已有对象中直接拷贝得到,而不用每次在创建新对象的时候,都重复执行这些耗时的操作。

    原型模式用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,典型的应用是对象的克隆方法,示例代码如下

    public class Person implements Cloneable {
        String name = "lisa";
        int age = 1;
        Location loc = new Location("xy", 10);
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            Person p = (Person) super.clone();
            p.loc = (Location) loc.clone();
            return p;
        }
    
        @Override
        public String toString() {
            return "Person{" + "name='" + name + '\'' + ", age=" + age + ", loc=" + loc + '}';
        }
    }
    
    public class Location implements Cloneable {
        private String street;
        private int roomNo;
    
        public Location(String street, int roomNo) {
            this.street = street;
            this.roomNo = roomNo;
        }
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    
        @Override
        public String toString() {
            return "Location{" + "street='" + street + '\'' + ", roomNo=" + roomNo + '}';
        }
    }
    
    public class Main {
        public static void main(String[] args) throws CloneNotSupportedException {
            Person p = new Person();
            System.out.println(p);
            Person p2 = (Person) p.clone();
            System.out.println(p2);
        }
    }
    

    本示例的 UML 图如下:

    image

    注:Java 自带的 clone() 方法进行的就是浅克隆。而如果我们想进行深克隆,可以直接在类中调用父类的克隆方法,即: super.clone() 后,手动给克隆对象的相关属性分配另一块内存,不过如果当原型对象维护很多引用属性的时候,手动分配会比较烦琐。因此,在 Java 中,如果想完成原型对象的深克隆,则通常使用序列化的方式。

    例如:克隆一个巨大的 HashMap,如果构建该 HashMap 的代价很大,我们可以通过

    方式一:先调用 HashMap 默认的克隆方法,然后递归拷贝 HashMap 里面的内容,直到类型是基础类型为止;

    方式二:使用序列化方式克隆。

    关于序列化克隆的示例代码如下

    import java.io.*;
    import java.util.ArrayList;
    import java.util.List;
    
    public class DeepCloneDemo {
        public static void main(String[] args) {
            List<String> hobbies = new ArrayList<>();
            hobbies.add("唱歌");
            hobbies.add("跳舞");
            Prototype p = new Prototype();
            p.setAge(18);
            p.setName("zhangsan");
            p.setHobbits(hobbies);
            Prototype clone = p.clone();
            System.out.println(clone.getAge());
            System.out.println(clone.getName());
            System.out.println(clone.getHobbits());
        }
    }
    
    // 待克隆对象
    class Prototype implements Cloneable, Serializable {
        private int age;
        private String name;
        private List<String> hobbits;
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public List<String> getHobbits() {
            return hobbits;
        }
    
        public void setHobbits(List<String> hobbits) {
            this.hobbits = hobbits;
        }
    
        @Override
        protected Prototype clone() {
            return deepClone();
        }
    
        public Prototype deepClone() {
            try {
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(bos);
                oos.writeObject(this);
                ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
                ObjectInputStream ois = new ObjectInputStream(bis);
                return (Prototype) ois.readObject();
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
        @Override
        public String toString() {
            return "Prototype{" + "age=" + age + ", name='" + name + '\'' + ", hobbits=" + hobbits + '}';
        }
    }
    

    如果只是增量拷贝,可以通过浅拷贝拿到一个新的 HashMap ,然后拿到增量的数据单独进行深拷贝即可。

    更多地,Spring 中创建对象的方式默认采用单例模式,可以通过设置 @Scope("prototype") 注解将其改为原型模式。

    UML 和 代码

    UML 图

    代码

    更多

    设计模式学习专栏

    参考资料

  • 相关阅读:
    Struts2 xml表单验证
    struts2表单验证-整合国际化
    Strut2-Ajax总结
    java中的线性安全和不安全
    智游推送试用
    推送的重连策略
    Android本地通知的实现方式
    MyBatis框架学习二
    Java基础学习 2 (选择结构,循环结构)
    Java基础学习 1 (变量,数据类型,运算符)
  • 原文地址:https://www.cnblogs.com/greyzeng/p/16869568.html
Copyright © 2020-2023  润新知