原型模式,对于开发者而言,或陌生,或熟悉,但是都应该很熟悉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 !!!
网友评论