• [工作中的设计模式]原型模式prototype


    一、模式解析

      提起prototype,最近看多了js相关的内容,第一印象首先是js的原型

    var Person=function(name){
        this.name=name;
    }
    Person.prototype.run=function(){
      alert(this.name+" is running";  
    }

      此处的原型是js的特殊定义,在原型上定义的属性和方法所有的类进行共享。

      不过设计模式中的原型模式指的是:将已有的对象作为原型,拷贝出一份具有相同属性的新的对象。

      模式定义为:原型模式要求对象实现一个可以“克隆”自身的接口,这样就可以通过复制一个实例对象本身来创建一个新的实例。这样一来,通过原型实例创建新的对象,就不再需要关心这个实例本身的类型,只要实现了克隆自身的方法,就可以通过这个方法来获取新的对象,而无须再去通过new来创建。

      原型模式的实现主要有两种:1、继承接口clonable,实现类的拷贝,2、继承接口Serializable,将原实例序列化,然后解序列化生成新的对象。

      针对第一种拷贝,又分为深拷贝和浅拷贝。

      浅拷贝:实例中基本对象类型进行拷贝,引用对象类型仅拷贝引用地址,引用对象中的数据变化,会导致源对象和拷贝对象内容同时发生变化。

      深拷贝:除基本对象类型外,引用对象类型也会一一拷贝其内部的的数据,从而使源对象变化不会引起拷贝对象变化。

    二、模式代码

    浅拷贝实例:

    import java.util.ArrayList;
    import java.util.List;
    
    public class Prototype implements Cloneable {
        public String name;//普通数据类型
        public List<String> list =new ArrayList<String>();//list集合,引用数据类型
        public Prototype(String name){
            this.name=name;
        }
        
        @Override
        public Object clone(){
            try {
                return super.clone();
            } catch (CloneNotSupportedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                return null;
            }
        }
    }
    package prototype.patten;
    
    public class Client {
        public static void main(String[] args) {
            Prototype p1=new Prototype("zhangsan");
            p1.list.add("a");
            p1.list.add("b");
            Prototype p2=(Prototype) p1.clone();
            p1.name="lisi";
            System.out.println("p1.name=="+p1.name);//p1.name==lisi
            System.out.println("p2.name=="+p2.name);//p2.name==zhangsan
            
            p2.list.add("c");
            System.out.println(p1.list.toString());//[a, b, c]
            System.out.println(p2.list.toString());//[a, b, c]
            
        }
    }

    可以看到基本类型name已经完全不同,p1的变化与p2无关,但list的变化仍然相关,p1增加元素会导致p2同时增加。

    深拷贝实例:

    package prototype.patten;
    
    class Person implements Cloneable{
        public String name;
        @Override
        protected Person clone() throws CloneNotSupportedException {
            // TODO Auto-generated method stub
            return (Person)super.clone();
        }
    }
    
    public class DeepPrototype implements Cloneable {
        public Person person;
        public int count;
        
        @Override
        protected DeepPrototype clone() throws CloneNotSupportedException {
            DeepPrototype prototype=(DeepPrototype) super.clone();
            prototype.person=this.person.clone();//内部的引用类型分别执行clone方法
            
            return prototype;
        }
        
    }
    public class Client {
        public static void main(String[] args) throws CloneNotSupportedException {
            DeepPrototype prototype=new DeepPrototype();
            Person person=new Person();
            person.name="张三";
            prototype.person=person;
            prototype.count=10;
            DeepPrototype p2=prototype.clone();
            prototype.person.name="李四";
            prototype.count=11;
            System.out.println("prototype.person.name=="+prototype.person.name);//prototype.person.name==李四
            System.out.println("p2.person.name=="+p2.person.name);        //p2.person.name==张三
            System.out.println("prototype.count=="+prototype.count);    //    prototype.count==11
            System.out.println("p2.count=="+p2.count);//p2.count==10
            
        }
    }

    可以看到,深拷贝的重点是在clone方法中,所有引用对象类型要执行clone的方法,深拷贝完成后,源对象和新对象的所有属性具有无关性。

    因为真的引用对象也要执行clone的方法,ArrayList等方法引用了clonable和Serializable等方法,因此同意可以实现深拷贝

    序列化深克隆

     public  Object deepClone() throws IOException, ClassNotFoundException{
            //将对象写到流里
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
            //从流里读回来
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            return ois.readObject();
        }

    只要实现了接口Serializable,均可以编写此方法,然后通过调用方法完成深克隆,无需考虑参数类型。

    三、模式应用场景

      在工作中目前还没有主动使用clone方法实现对象的创建,序列化方式的保存和复制对象在系统日志等非接化数据中进行使用,不过并非原型模式。

      接触的比较接近与模板方式应用的例子是jquery源码中类的继承使用,由于jquery没有直接的继承方法,因此有与多人开发了如原型继承,混合继承的方法,jquery使用的是对象拷贝继承,可以根据参数选择,实现深拷贝或浅拷贝,算上原型模式的实例化最接近的例子。

    四、模式代码

    jQuery.extend = jQuery.fn.extend = function() {
        var src, copyIsArray, copy, name, options, clone,
            target = arguments[0] || {},
            i = 1,
            length = arguments.length,
            deep = false;
    
        // Handle a deep copy situation
        if ( typeof target === "boolean" ) {
            deep = target;
            target = arguments[1] || {};
            // skip the boolean and the target
            i = 2;
        }
    
        // Handle case when target is a string or something (possible in deep copy)
        if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
            target = {};
        }
    
        // extend jQuery itself if only one argument is passed
        if ( length === i ) {
            target = this;
            --i;
        }
    
        for ( ; i < length; i++ ) {
            // Only deal with non-null/undefined values
            if ( (options = arguments[ i ]) != null ) {
                // Extend the base object
                for ( name in options ) {
                    src = target[ name ];
                    copy = options[ name ];
    
                    // Prevent never-ending loop
                    if ( target === copy ) {
                        continue;
                    }
    
                    // Recurse if we're merging plain objects or arrays
                    if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
                        if ( copyIsArray ) {
                            copyIsArray = false;
                            clone = src && jQuery.isArray(src) ? src : [];
    
                        } else {
                            clone = src && jQuery.isPlainObject(src) ? src : {};
                        }
    
                        // Never move original objects, clone them
                        target[ name ] = jQuery.extend( deep, clone, copy );
    
                    // Don't bring in undefined values
                    } else if ( copy !== undefined ) {
                        target[ name ] = copy;
                    }
                }
            }
        }
  • 相关阅读:
    elasticsearch-排序(六)
    elasticsearch-分词器(五)
    elasticsearch-搜索-基本搜索(四)
    elasticsearch-文档(三)
    elasticsearch-集群(二)
    FFmpeg架构之I/O模块分析
    DirectShow 在 VS2010 中开发环境的设置
    预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)
    2012年软件开发者薪资调查报告
    深入了解 VP8
  • 原文地址:https://www.cnblogs.com/jyyzzjl/p/5165008.html
Copyright © 2020-2023  润新知