Android 中设计模式 ----原型模式
来源: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类图:
Client:客户端使用者
ProtoType:抽象类或者接口,声明 具有clone 能力
ContreteProtoType:实际的原型类
原型模式需要具备的条件:
- 实现Cloneable接口。在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。
- 重写Object类中的clone方法。Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此,需要将clone方法的作用域修改为public类型。
通过这个方法可以知道两点:
- 为什么需要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中设计模式
更多相关文章
- Android对Window对象的管理机制分析
- android观察者模式
- [Android]当Activity启动模式为singleTask时的生命周期
- android 5.0之后利用Intent传递Serializable对象存在问题
- activity启动模式
- Adapter软件设计模式在Android的应用
- Android MVP模式中的OOM
- Android的碎片fragment和activity之前的关系 以及MVP模式详解