来源:http://blog.csdn.net/shift_wwx/article/details/79092043


先来看个实例:

public class Person{    private String name;    private int age;    private int grade;    private int class;    private string school;    private double scores;    public Person(){            }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    public int getGrade() {        return grade;    }    public void setGrade(int grade) {        this.grade = grade;    }    public int getClass() {        return class;    }    public void setClass(int class) {        this.class = class;    }    public string getSchool() {        return school;    }    public void setSchool(string school) {        this.school = school;    }    public double getScores() {        return scores;    }    public void setScores(double scores) {        this.scores = scores;    }}
上面这个实例是一个学校学生的档案管理的info 类,里面有名字、年龄、年级、班级、之前毕业的学校、毕业考试的得分。

假如两个学生除了姓名不同,其他的都相同,创建两个学生信息的时候,需要这样:

Person  zhangsan = new Person();zhangsan.setName("zhangsan");zhangsan.setAge("18");zhangsan.setGrade("13");zhangsan.setClass("12");zhangsan.setSchool("jialidun xiaoxue");zhangsan.setScores("20");Person  lisi = new Person();lisi.setName("lisi");lisi.setAge("18");lisi.setGrade("13");lisi.setClass("12");lisi.setSchool("jialidun xiaoxue");lisi.setScores("20");
其实中间就一个姓名不同,但是要做这样一个过程,很是浪费时间和资源。这里是原型模式应用场景之一。


原型模式的使用场景:

  • 类初始化需要消耗很多资源,这个资源包括数据(如大型的数据list)、硬件资源等,通过原型拷贝避免这种消耗。
  • 通过new产生一个对象需要非常繁琐的数据准备和访问权限,这时可以使用原型模式。
  • 一个对象需要提供给其他对象访问,而且各个调用者可能修改对象的数据,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝。


原型模式的UML类图:

Android 中设计模式 ----原型模式_第1张图片

Client:客户端使用者

ProtoType:抽象类或者接口,声明 具有clone 能力

ContreteProtoType:实际的原型类


原型模式需要具备的条件:

  • 实现Cloneable接口。在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。
  • 重写Object类中的clone方法。Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此,需要将clone方法的作用域修改为public类型。
来看下Object类中的clone方法: Android 中设计模式 ----原型模式_第2张图片

通过这个方法可以知道两点:

  • 为什么需要Cloneable接口(里面什么接口都没有)。
  • 最后返回的是interrnalClone()是个native 的方法(下面有机会详细补充native clone过程)。


知道了原型模式的实现过程,上面的实例就可以改一下了:
public class Person implement Cloneable{    private String name;    private int age;    private int grade;    private int mClass;    private string school;    private double scores;    public Person(){            }    @override    public Object clone() {Person p = null;try {    p = (Person) super.clone();    p.name = this.name;    p.age = this.age;    p.grade = this.grade;    p.mClass = this.mClass;    p.school = this.school;    p.scores = this.scores;} catch(CloneNotSupportedException e){    e.printStackTrace();}return p;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    public int getGrade() {        return grade;    }    public void setGrade(int grade) {        this.grade = grade;    }    public int getClass() {        return class;    }    public void setClass(int class) {        this.class = class;    }public string getSchool() {        return school;    }    public void setSchool(string school) {        this.school = school;}public double getScores() {        return scores;    }    public void setScores(double scores) {        this.scores = scores;}}


同样的两学生信息可以改为:

Person  zhangsan = new Person();zhangsan.setName("zhangsan");zhangsan.setAge("18");zhangsan.setGrade("13");zhangsan.setClass("12");zhangsan.setSchool("jialidun xiaoxue");zhangsan.setScores("20");Person lisi = (Person)zhangsan.clone();//这里利用原型模式,代码更简洁lisi.setName("lisi");
使用原型模式创建对象比直接new一个对象在性能上要好的多,因为Object类的clone方法是一个native方法, 它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。

使用原型模式的另一个好处是简化对象的创建,使得创建对象就像我们在编辑文档时的复制粘贴一样简单。

因为以上优点,所以在需要重复地创建相似对象时可以考虑使用原型模式。比如需要在一个循环体内创建对象,假如对象创建过程比较复杂或者循环次数很多的话,使用原型模式不但可以简化创建过程,而且可以使系统的整体性能提高很多


原型模式的注意事项

1、使用原型模式复制对象不会调用类的构造方法。

因为对象的复制是通过调用Object类的clone方法来完成的,它直接在内存中复制数据因此不会调用到类的构造方法。不但构造方法中的代码不会执行,甚至连访问权限都对原型模式无效。单例模式中,只要将构造方法的访问权限设置为private型,就可以实现单例。但是clone方法直接无视构造方法的权限,所以,单例模式与原型模式是冲突的,在使用时要特别注意。


2、深拷贝与浅拷贝。

Object类的clone方法只会拷贝对象中的基本的数据类型。

8种基本数据类型byte、char、short、int、long、float、double、boolean。

对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。


还是用上面的实例,类中添加一个ArrayList的对象,如下:

public class Person implement Cloneable{    private String name;    private int age;    private int grade;    private int mClass;    private string school;    private double scores;    private ArrayList list = new ArrayList();//多加一个ArrayList对象    public Person(){            }
如果是浅拷贝两个对象会公用一个引用,改动一个,另一个也会跟着改变。现在正常情况下,我们需要深拷贝,所以clone函数需要改成:
public Object clone() {    Person p = null;    try {        p = (Person) super.clone();p.name = this.name;p.age = this.age;p.grade = this.grade;p.mClass = this.mClass;p.school = this.school;p.scores = this.scores;p.list = this.list.clone();//进行深拷贝    } catch(CloneNotSupportedException e){e.printStackTrace();    }    return p;}

如果这里不用深拷贝,用浅拷贝改成

p.list = this.list;
会怎么样呢?
Person  zhangsan = new Person();zhangsan.setName("zhangsan");zhangsan.setAge("18");zhangsan.setGrade("13");zhangsan.setClass("12");zhangsan.setSchool("jialidun xiaoxue");zhangsan.setScores("20");zhangsan.getList().add("hehe1");//zhangsan 里面添加一个hehe1Person lisi = (Person)zhangsan.clone();lisi.setName("lisi");lisi.getList().add("hehe2");//lisi 里面添加一个hehe2
如果这个时候查看zhangsan中的list,会发现多了一个hehe2,因为拷贝的时候只是做了一个引用拷贝,相当于指向的是同一个内存,修改同一块内存的时候当然会出现相同的结果。

最后来看下Android中用到的原型模式,例如Intent,
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;    if (o.mCategories != null) {this.mCategories = new ArraySet(o.mCategories);    }    if (o.mExtras != null) {this.mExtras = new Bundle(o.mExtras);    }    if (o.mSourceBounds != null) {this.mSourceBounds = new Rect(o.mSourceBounds);//也没有用clone,而是new了一个    }    if (o.mSelector != null) {this.mSelector = new Intent(o.mSelector);    }    if (o.mClipData != null) {this.mClipData = new ClipData(o.mClipData);    }}@Overridepublic Object clone() {    return new Intent(this);//并没有用clone,而是new 了一个新的}

从整个android framework搜索下来其实并没有多少原型模式,可能是因为clone中存在浅拷贝的危险。android中都使用了拷贝构造函数的形式,这种情况能成功的避开浅拷贝带来的危险。对于new和clone的效率,在new 中有复杂的逻辑处理或者内存占用大的对象时,效率会低于clone。但对于现在的处理器来说这些性能不是决定性的因素。

谨慎改写clone 这一篇博文中提到了clone带来的优缺点,值得阅读。

原型模式 这一篇博文解释的也很详细,值得阅读。




更多的设计模式看:Android中设计模式



更多相关文章

  1. Android对Window对象的管理机制分析
  2. android观察者模式
  3. [Android]当Activity启动模式为singleTask时的生命周期
  4. android 5.0之后利用Intent传递Serializable对象存在问题
  5. activity启动模式
  6. Adapter软件设计模式在Android的应用
  7. Android MVP模式中的OOM
  8. Android的碎片fragment和activity之前的关系 以及MVP模式详解

随机推荐

  1. 异常:Unknown features (Run Configuratio
  2. android中在androidmanifest.xml设置权限
  3. Activity configuration change
  4. Android初学
  5. 百度地图SDK for Android(安卓)v2.1.2全
  6. Android程序报错:程序包org.apache.http不
  7. Android系统Intent中的Uri使用
  8. Android(安卓)xUtils框架(二) ViewUtils
  9. 【Android】NDK中log输出方法
  10. Android:多分辨率适配