• 原型模式


    原型模式主要使用与以下场景:

      (1)类初始化消耗资源较多。

      (2)使用 new 生成一个对象需要非常繁琐的过程(数据准备、访问权限等)。

      (3)构造函数比较复杂。

      (4)在循环体中产生大量对象。

      在 spring 中,原型模式应用的非常广泛。例如 scope="prototype" ,我们常用的JSON.oarseObject() 也是一种原型模式。

    浅克隆

      一个标准的原型模式代码应该这样设计,先创建一个 Prototype 接口

      public interface Prototype { Prototype clone(); } 

      创建具体需要克隆的类 ConcretePrototypeA:

    @Data
    public class ConcretePrototypeA implements Prototype {
        private int age;
        private String name;
        private List hobbies;
        @Override
        public Prototype clone() {
            ConcretePrototypeA concretePrototypeA = new ConcretePrototypeA();
            concretePrototypeA.setAge(this.age);
            concretePrototypeA.setName(this.name);
            concretePrototypeA.setHobbies(this.hobbies);
            return concretePrototypeA;
        }
    }

      创建 Client 类:

    public class Client {
        private Prototype prototype;
        public Client(Prototype prototype){
            this.prototype = prototype;
        }
        public Prototype startClone(Prototype concretePrototype){
            return (Prototype)concretePrototype.clone();
        }

      测试代码如下:

        @Test
        void PrototypeTest(){
            //创建一个具体的需要克隆的对象
            ConcretePrototypeA concretePrototype = new ConcretePrototypeA();
            //天成属性,方便测试
            concretePrototype.setAge(18);
            concretePrototype.setName("prototype");
            List hoobies = new ArrayList<String>();
            concretePrototype.setHobbies(hoobies);
            System.out.println(concretePrototype);
    
            //创建client类准备克隆
            Client client = new Client(concretePrototype);
            ConcretePrototypeA concretePrototypeClone = (ConcretePrototypeA)client.startClone(concretePrototype);
            System.out.println(concretePrototypeClone);
    
            System.out.println("克隆对象中的引用类型地址值:" + concretePrototypeClone.getHobbies());
            System.out.println("原对象中的引用类型地址值:" + concretePrototype.getHobbies());
            System.out.println("对象地址比较:" + (concretePrototype.getHobbies() == concretePrototypeClone.getHobbies()));
        }

      从测试结果可以看出,hobbies 的引用地址是相同的,意味着赋值的不是指,而是引用的地址。这样的话,如果我们修改任意一个对象的属性值,则 concretePrototype 和 concretePrototypeClone 的 hobbies 的值都会改变,这就是我们常说的浅克隆。浅克隆只是完整复制了值类型数据,没有赋值引用对象。换言之,所有的应用对象仍然指向原来的对象,显然这不是我们想要的结果。

    深克隆

      我们换一个场景,大家都知道七天大声,首先它是一个猴子,有七十二变,把一根毫毛就可以吹出千万个猴子,还有一个金箍棒,金箍棒可以变大变小。这就是我们耳熟能详的原型摩尔是的经典体现。

      创建原型猴子类 Monkey:

      

    public class Monkey {
        public int height;
        public int weight;
        public Date birthday;
    }

      创建引用对象金箍棒类:

    public class JinGuBang implements Serializable {
        public float h = 100;
        public float d = 10;
        public void big(){
            this.d *= 2;
            this.h *= 2;
        }
        public void small(){
            this.d /= 2;
            this.h /= 2;
        }
    }

      创建具体的对象七天大圣类

    public class QiTianDaSheng extends Monkey implements Cloneable, Serializable {
        public JinGuBang jinGuBang;
    
        public QiTianDaSheng(){
            this.birthday = new Date();
            this.jinGuBang = new JinGuBang();
        }
        @Override
        protected Object clone() throws CloneNotSupportedException{
            return  this.deepClone();
        }
    
        public Object 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);
    
                QiTianDaSheng copy = (QiTianDaSheng) ois.readObject();
                copy.birthday = new Date();
                return copy;
            } catch (Exception e){
                e.printStackTrace();
                return null;
            }
        }
    
        public QiTianDaSheng shallClone(QiTianDaSheng target){
            QiTianDaSheng qiTianDaSheng =   new QiTianDaSheng();
            qiTianDaSheng.height = target.height;
            qiTianDaSheng.weight = target.weight;
    
            qiTianDaSheng.jinGuBang = target.jinGuBang;
            qiTianDaSheng.birthday = target.birthday;
            return  qiTianDaSheng;
        }
    }
    QiTianDaSheng

      测试代码如下:

        @Test
        void DeepCloneTest(){
            QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
            try{
                QiTianDaSheng clone = (QiTianDaSheng) qiTianDaSheng.clone();
                System.out.println("深克隆:" + (qiTianDaSheng.jinGuBang == clone.jinGuBang));
            } catch (Exception e){
                e.printStackTrace();
            }
            QiTianDaSheng q = new QiTianDaSheng();
            QiTianDaSheng n = q.shallClone(q);
            System.out.println("浅克隆:" + (q.jinGuBang == n.jinGuBang));
        }

      测试结果如下:

     

    克隆破坏单例模式

      如果克隆的目标是单例对象,那么意味着深克隆会破坏单例模式。实际上防止克隆破坏单例模式的解决思路很简单,禁止深克隆便可。要么单例就不实现 Cloneable 接口,要么我们重写 clone() 方法,在clone() 方法中直接返回单例对象即可,具体代码如下:

     @Override public Object clone() throws CloneNotSupportedException{ return INSTANCE; } 

      clean() 方法源码:

        /**
         * Returns a shallow copy of this <tt>ArrayList</tt> instance.  (The
         * elements themselves are not copied.)
         *
         * @return a clone of this <tt>ArrayList</tt> instance
         */
        public Object clone() {
            try {
                ArrayList<?> v = (ArrayList<?>) super.clone();
                v.elementData = Arrays.copyOf(elementData, size);
                v.modCount = 0;
                return v;
            } catch (CloneNotSupportedException e) {
                // this shouldn't happen, since we are Cloneable
                throw new InternalError(e);
            }
        }

    本文来自博客园,作者:l-coil,转载请注明原文链接:https://www.cnblogs.com/l-coil/p/12864219.html

  • 相关阅读:
    用gethub下载ardupilot的最新源码
    Mavlink协议理解
    Mavlink消息包解析
    Mavlink 协议 理解
    极飞P20农业无人机多机协同作业飞行
    如何下载最新的固件到Pixhawk
    韦东山笔记之用busybox构建根文件系统
    Visual studio 打包程序时添加自定义的系统必备组件
    MFC 编译链接错误:unresolved external symbol
    在Visual studio 2017中使用EF6连接MySQL
  • 原文地址:https://www.cnblogs.com/xianquan/p/12864219.html
Copyright © 2020-2023  润新知