移动架构师-设计模式篇 《原型模式》

原型模式,对于开发者而言,或陌生,或熟悉,但是都应该很熟悉Cloneable接口。通过Cloneable接口,我们可以很容易的复制一个对象副本来单独使用,当然,这取决于Class是否实现了该接口以及如何实现。

原型模式是创建型的设计模式之一。官方而言,原型模式的作用是用指定原型对象来创建一个新的对象;从本身的功能与意义来理解的话,我们可以举个日常的例子。

我们应该都写过周报或日报,但是我们在写周报或日报的时候,除非第一次,否则大多数人的做法都是复制上一次周报的文件,修改文件名称,并修改部分周报内容,然后提交。为什么呢? 因为一我们已经有了一个类似的文件, 二则新的文件或许与旧的文件内容不同,但很多内容却是相同的。复用上一次的,省时省力,何乐而不为呢?

其实原型模式也是这样的。比如有一个产品A, A有100个字段;现在我们想要创建一个产品B,发现B很多特点和A都一样,可能部分属性是不同的;此时你会怎么做呢 ? 手动创建一个新的对象,还是从A的副本修改少部分属性呢 ? 答案显而易见。直接描述不够形象, 那么来一段简单的代码。

// 这是一个学生类,很简单!
public class Student implements Cloneable {
        public String name;
        public int age;
        public int sex;

        public Student(Student stu) {
             this.name = stu.name;
             this.age = stu.age;
             this.sex = stu.sex;
        }

        @override
        public Object clone() {
            return new Student(this);
        }
}
pubic class Main {
  
      public static void main(String[] args) {
                  // 有一个学生,小明
                  Student stu_xiaoming = new Student();
                  stu_xiaoming.name = "xiaoming";
                  stu_xiaoming.age = 18;
                  stu_xiaoming.sex = 1;  
                  
                  // 小明有一个发小 小毛,同班同读,也是18岁, 男孩
                  // 如果是你,你怎么生成这个发小的对象呢 ?
                  // 这就是原型模式的作用

                  Student stu_faxiao = stu_xiaoming.clone();
                  stu_faxiao.name = "xiaomao"; 
      }
}

通过上面的描述与例子,我想已经都猜到了原型模式的作用了。可能有人会说, 就3个属性罢了,复制一下也是很快的; 但是在开发中, 可能一个产品会有几十个几百个属性,都要复制吗 ?而且复制会产生大量的类似代码,无论对结构,以及开发效率都是不利的。

它主要面对的问题是:“某些结构复杂的对象”的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是他们却拥有比较稳定一致的接口。

深拷贝与浅拷贝
既然聊到复制, 我们来聊一下深拷贝与浅拷贝的问题。其实很好理解:
1.基本变量如int,boolean,double等类型的变量采用=赋的是值,如

int a = 10; 
int b = a;
int b = 5;
// 此时a = 10, b = 5.  b的赋值不会影响a

2.引用变量如List,String,数组等引用型的变量采用=赋得是址,即原引用和被赋值引用指向同一个地址,如

int[] a = new int[]{1,2,3,4};
int[] b = a;
b[3] = 5;
// 此时 a与b都是[1,2,3,5]。 b赋值给a,则a与b指向同一块内存空间,a与b对内存空间的修改,都同时生效 

3.Class的属性既包含基本类型,也包含引用类型;Class中所有的变量以及嵌套变量都是深拷贝,那么Class才是深拷贝

public class A implements Colneable {
    public int a; // 基本类型由于理论1,赋值后都是独立的
    public B b; // 引用对象如果

    @override 
    public Object clone() {
        A c = new A();
        c.a = this.a; // 基本类型不影响
        c.b = this.b; // 这里b是浅复制,此时两个引用指向同一内存, 相互影响
        // 以下是深复制,属性赋值后, 两者相互独立
        B d = new B();
        d.v = this.b.v;
        c.b = d;
    }
}

public class B {
        public int v;
}

最后的最后,结合Android的源码,来看一下原型模式的经典实现。今天的嘉宾是Intent, 大家可以借鉴~

// 片段1 实现Cloneable接口
public class Intent implements Parcelable, Cloneable {
...
// 片段2 接下来看一下clone的实现
   @Override
    public Object clone() {
        return new Intent(this);
    }
// 片段3 大家一定会好奇,构造方法才是关键,那构造方法里是什么呢 ?
public Intent(Intent o) {
        this.mAction = o.mAction;
        this.mData = o.mData;
        this.mType = o.mType;
        this.mPackage = o.mPackage;
        this.mComponent = o.mComponent;
        this.mFlags = o.mFlags;
        this.mContentUserHint = o.mContentUserHint;
        this.mLaunchToken = o.mLaunchToken;
        if (o.mCategories != null) {
            this.mCategories = new ArraySet<String>(o.mCategories);
        }
        if (o.mExtras != null) {
            this.mExtras = new Bundle(o.mExtras);
        }
        if (o.mSourceBounds != null) {
            this.mSourceBounds = new Rect(o.mSourceBounds);
        }
        if (o.mSelector != null) {
            this.mSelector = new Intent(o.mSelector);
        }
        if (o.mClipData != null) {
            this.mClipData = new ClipData(o.mClipData);
        }
    }
// 以上三步实现经典原型模式,同单例模式的讲解,借助官方的源码来完成我们自己的设计模式,才完美!

从本篇可以看出, 原型模式是很简单的,也好理解。原型模式主要解决了复杂对象的创建问题,它有什么优点和缺点呢 ?如下

1.隐藏了创建的细节,我们只知道clone生成一个副本,但是怎么生成,who knows ?
2.比直接创建一个对象更有效率,特别是复杂对象,因为它操作内存中的二进制流
3.省时省力省代码,复制很low,还会造成多余的代码,多次使用也很麻烦;总之复用比复制好太多

原型模式很简单, 本篇文章亦是,如果你觉得有收获,不妨为我打Call !!!

    原文作者:DevCW
    原文地址: https://www.jianshu.com/p/1df510d03cf1
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞