• 设计模式之原型模式


        这节讲一下原型模式,原型模式用于解决大量相同或相似对象的创建问题,传统的,我们实例化对象要用new关键字,在面对大量重复对象的创建情况下,new实例的过程是比较消耗资源的,所以我们可以利用一个对象作为原型,通过这个对象的不断克隆自己来产出一个个新实例(这跟js的原型对象并不相同,读者不要跟其做理论比较)。

        我们可以设想一下,为何克隆比new实例要高效:拿画画来说,new实例相当于每次要构思这幅画,并将其画出来,克隆相当于第一次构思并画出后,后续的画都是临摹,所以克隆是高效的。

        我们看一下原型模式的定义:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。

        在示例代码之前,先要理解一个概念:深拷贝和浅拷贝:

        所谓浅拷贝就是指拷贝时只拷贝值类型的属性,引用类型的属性地址跟原型对象的地址指向一致,而深拷贝就是将原型对象的值类型和引用类型的属性都拷贝一份,可以说是和原型对象完全分离开来。

        接下来我们看一下如何让一个对象可被克隆:

    class prototypeClass : ICloneable
    {
        public prototypeClass(string classname)
        {
            this.classname = classname;
        }
        public object Clone()
        {
            return MemberwiseClone();
        }
    ​
        public string classname { get; set; }
    ​
        public void ShowName()
        {
            Console.WriteLine(classname);
        }
    }

        具体的逻辑就是让原型类实现ICloneable接口,实现接口的Clone()方法,方法中调用MemberwiseClone()返回一个基于该原型对象浅拷贝的对象。

        在主方法中调用,我们查看一下运行结果:

    prototypeClass p = new prototypeClass("小明");
    prototypeClass p_clone = (prototypeClass)p.Clone();
    p.ShowName();
    p_clone.ShowName();
    Console.WriteLine(p == p_clone);

        下面我再添加一个类,并在prototypeClass类中添加这个类作为属性:

        在主方法中比较一下克隆对象的s属性是否与原型对象的s属性相等:

       运行结果如下:

        结果为true,这就证明了此为浅拷贝。

        若要实现深拷贝,需做点改动:

    static prototypeClassS ps=new prototypeClassS("小艾");
    public object Clone()
    {
        prototypeClass p=(prototypeClass) MemberwiseClone();
        p.s=(prototypeClassS)ps.Clone();//手动调用属性的克隆方法实现深度拷贝
        return p;
    }

         加入一个静态原型属性,每次克隆该原型对象时都去手动将引用对象克隆出来并赋值,如果被拷贝的属性自己也有引用类型的属性,为了实现深拷贝,也得做对应的修改,这样就形成了一个拷贝链,层层深入,从而完成深度拷贝(其实这挺繁琐的)。

        

        下面我通过代码来演示一下new实例和克隆的性能差距:

    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();
    for (int i = 0; i < 100000; i++)
    {
        prototypeClass clone = (prototypeClass)p.Clone();
    }
    ​
    stopwatch.Stop();
    Console.WriteLine("克隆时间:" + stopwatch.Elapsed);
    ​
    stopwatch.Start();
    for (int i = 0; i < 100000; i++)
    {
        prototypeClass clone = new prototypeClass("小明",new prototypeClassS("新二"));
    }
    stopwatch.Stop();
    Console.WriteLine("new 实例:" + stopwatch.Elapsed);

        在主方法中编写上述代码,下面看一下运行结果:

        差距不言而喻。

        当然,原型模式也有缺点:

    • 我们需要为每一个类都配置一个 clone 方法,clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。

    • 当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。

      个人公众号,热爱分享,知识无价。

  • 相关阅读:
    在 Flink 算子中使用多线程如何保证不丢数据?
    日处理数据量超10亿:友信金服基于Flink构建实时用户画像系统的实践
    Java编码技巧之高效代码50例
    codeforces 1284D. New Year and Conference(线段树)
    codeforces 1284C. New Year and Permutation(组合数学)
    codeforces 1284B. New Year and Ascent Sequence(二分)
    Codeforces Hello2020 A-E简要题解
    POJ2456 Aggressive cows(二分)
    POJ3122 Pie(二分)
    POJ3258 River Hopscotch(二分最大化最小值)
  • 原文地址:https://www.cnblogs.com/charlesmvp/p/13782143.html
Copyright © 2020-2023  润新知