【设计模式】原型模式(Prototype)
摘要:
1.本文将详细介绍原型模式的原理和实际代码中特别是Android系统代码中的应用。
纲要:
1. 引入原型模式
2. 原型模式的概念及优缺点介绍
3. 原型模式对拷贝的使用
4. 原型模式在Android源码中的应用
1.先来一个段子:
GG和MM经常在QQ上聊天,但是GG打字的速度慢如蜗牛爬行,每次MM在瞬间完成恢复或者问候是,GG都会很紧张的去尽力快速打字,尽管如此,还是让MM有些不高兴,MM说回复信息这么慢,显然是用心不专,不在乎她。哎,GG也是百口难辩啊,不过也确实是没有办法。
有一天,GG想自己的密友K倾诉了自己的苦衷。K顿生大笑。说道:“傻瓜,你怎么不去网上收集一些肉麻的情话以及一些你们经常说话会涉及到主题,把这些东西拷贝下来保存在自己的电脑或者U盘里面,这样一来如果下次在聊天就可以借用过来了!”,“K就是K,我怎么没有想到呢~妙极~妙极^_^”,“不过不要太高兴,这些东西是要适当修改的,要不然你把名字都搞错的话,就等着你的MM把你踹死吧O(∩_∩)O哈哈~”K补充道,“嗯,说的对,谢谢K哥解决了我的心腹之患啊”GG乐不可支的说道。
这是MM由在网上和GG聊天,GG专门复制那些实现准备好的肉麻情话经过稍加修改后发给MM,MM都快美死了…(摘自Android之大话设计模式)
2.原型模式介绍
2.1什么是原型模式?
在类A创建实例的时候,会将传入的类B的实例(这个实例被称为原型对象)进行克隆,以此快速的获得与传入对象一致的初始化数据。克隆是原型模式最显著特点。
类A被称为隔离类,因为原型模式将类A隔离起来,不需要关注类B的变化。
类B称为原型类,实现了clone()接口给类A克隆。类B是易变的,但是实现的接口不易变。类B通常是一个抽象类,传入给类A的实例是类B的子类的实例。
2.2这样做有什么好处?
1、 方便。如果一个原型对象本身数据非常多,一个一个赋值是不现实的。通过进行克隆,我们可以快速的获得这个原型对象的全部数据。
2、 耦合度低。如果原型对象是个“易变类”,即这个类可能会有各种各样的继承,或者这个类随着开发的进行可能会不断的修改。但只要类B实现的接口是稳定不易变的, 原型模式可以使隔离类保持接口的稳定性,不需要随着类的变化而变化。
3.1关于拷贝:
克隆有两种,浅拷贝和深拷贝。浅拷贝就是只克隆一次,对被克隆的类的所有属性进行直接的赋值,而不是递归的克隆。递归的克隆就是对自己的属性也调用它自己的克隆方法。对于基本类型(七种基本类型boolean,char,byte,short,float,double.long)来说,赋值将得到此基本类型的一份拷贝。如果是非基本类型,则得到引用的拷贝。引用的拷贝意味着什么?意味着直接赋值之后,这两个普通类的实例的指向的数据空间是同一个。也就是说它们的修改会互相影响。我们举一个例子:
两个类A,B,它们的属性如下:
Class A {
int a1; String a2; B b; } Class B { int b1; String b2; }
如果我们要实现Class A的浅拷贝,它的clone方法将会是这样的:
Object clone() { return super.clone(); }
万类之王Object自己已经实现了clone方法,我们只需要调用就可以了,在这里Object.clone()其实等价于:
A aa = new A(); aa.a1 = this.a1; aa.a2 = this.a2; aa.b = this.b; return a;
当然,这只是等价,不是真正的实现方法,毕竟Object不知道这个Class A有什么属性。万类之王是通过字节拷贝实现的,调用的是native方法。Class B是非基本类型,所以直接赋值只会得到b引用的拷贝,新克隆出来的aa实例和原实例a指向了同一个b实例的数据空间。所以对a.b的改动会影响到原实例a的b改动。
如果是深拷贝,我们应该这样实现:
Object clone() { A aa = (A)super.clone(); aa.b = (B)a.b.clone(); return (Object)aa; }
其实就是把非基本类型的属性再进行一次克隆就可以了,如果该属性中仍有非基本类型,则需要同样实现深拷贝。这里也就暴露出原型模式的缺点:实现原型模式的类的属性需要全部实现clone(),否则无法实现该模式。对于一个新创建的类来说也许不难,但如果中后期加入,则会需要大量的改动。
因为Java故意的隐瞒了指针这个概念,导致这里的理解有点复杂。从C语言的角度来看,int,float等类型为基本类型,他们本身在声明的时候就拥有了自己储存值的空间,赋值的时候是把值重新拷贝了一份。而自己定义的struct结构体,我们声明的是对应的struct*指针,不同指针指向同一个内存块,它们的修改会互相影响。
3.2Interger,String等包装类的拷贝问题:
细心的同学可能发现了,String不是基础类型,为什么在上面直接赋值就可以了?String的存储实际上通过char[]来实现的,同样,Interger是int的包装类。包装类的特质之一就是在对其值进行操作时会体现出其对应的基本类型的性质。关于该问题的详细情况可以访问:http://freej.blog.51cto.com/235241/168676。
3.3原型模式使用的是哪种拷贝呢?
设计模式里并没有指定原型模式是采用哪种拷贝,我们应根据实际情况选择深拷贝和浅拷贝。当然深拷贝应用的场景要多很多。此外,拷贝也没有指定需要拷贝所有属性,也是根据实际情况选择就可以了。
4.Android源码实战举例:
例子:Intent
Intent是一个实现了简单的原型模式的类。它的clone是这样的:
@Override public Object clone() { return new Intent(this); } 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; //下面几个是引用对象被重新创建了,是深拷贝 if (o.mCategories != null) { this.mCategories = new HashSet<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); } }
所以,Intent是一个深拷贝。有趣的是,它的属性也是使用构造器这种方式进行拷贝。在这种情况下,上文提到的“隔离类”和“易变类”都是自己。
new Intent(Intent o)这种构造方法在源码中屡见不鲜。如Launcher3中的LauncherModel.java,frameworks中的LauncherActivity.java,Settings中的DockEventReceiver。我们可以通过搜索“Intent(intent”或“Intent(mIntent”看到。
版权所有,转载请注明出处:
【设计模式】原型模式(Prototype)的更多相关文章
- PHP设计模式 原型模式(Prototype)
定义 和工厂模式类似,用来创建对象.但实现机制不同,原型模式是先创建一个对象,采用clone的方式进行新对象的创建. 场景 大对象的创建. 优点 1.可以在运行时刻增加和删除产品 2.可以改变值或结构 ...
- [工作中的设计模式]原型模式prototype
一.模式解析 提起prototype,最近看多了js相关的内容,第一印象首先是js的原型 var Person=function(name){ this.name=name; } Person.pro ...
- C#设计模式——原型模式(Prototype Pattern)
一.概述 在软件开发中,经常会碰上某些对象,其创建的过程比较复杂,而且随着需求的变化,其创建过程也会发生剧烈的变化,但他们的接口却能比较稳定.对这类对象的创建,我们应该遵循依赖倒置原则,即抽象不应该依 ...
- 设计模式-原型模式(Prototype)
场景分析: 前面我们提到,交易对象Trade,还有继承他的债券交易BondTrade.期货交易FutureTrade. 现在有一个需求,需要提供方法将交易拆分成多笔小交易. 代码如下(如果没有clon ...
- 设计模式——原型模式(Prototype Pattern)
原型模式:用原型实例制定创建对象的种类,并且通过拷贝这些原型创建新的对象. UML 图: 原型类: package com.cnblog.clarck; /** * 原型类 * * @author c ...
- 大话设计模式--原型模式 Prototype -- C++实现
1. 原型模式: 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象... 注意: 拷贝的时候是浅拷贝 还是 深拷贝, 来考虑是否需要重写拷贝构造函数. 关键在于: virtual Pro ...
- 设计模式——原型模式(Prototype)
用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象.——DP UML类图 模式说明 如果把在一张纸上手写一篇简历的过程看成是类的实例化过程,那么通过原型模式创建对象的过程就是拿着这张纸到复印 ...
- 设计模式--原型模式Prototype(创建型)
一.原型模式 用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象.原型模式实现的关键就是实现Clone函数,还需要实现深拷贝. 二.UML类图 三.例子 //父类 class Resume ...
- 谈谈设计模式~原型模式(Prototype)
返回目录 原型模式是创建型模式的一种,其特点在于通过“复制”一个已经存在的实例来返回新的实例(clone),而不是新建(new)实例.被复制的实例就是我们所称的“原型”,这个原型是可定制的. 原型模式 ...
- Net设计模式实例之原型模式( Prototype Pattern)
一.原型模式简介(Brief Introduction) 原型模式(Prototype Pattern):用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象. Specify the kin ...
随机推荐
- Bootstrap 按钮,图片,辅助类
Bootstrap 按钮 任何带有 class .btn 的元素都会继承圆角灰色按钮的默认外观.但是 Bootstrap 提供了一些选项来定义按钮的样式,具体如下表所示: 以下样式可用于<a&g ...
- BZOJ 1509 逃学的小孩(树的直径)
题意:从树上任找三点u,v,w.使得dis(u,v)+min(dis(u,w),dis(v,w))最大. 有一个结论u,v必是树上直径的两端点. 剩下的枚举w就行了. 具体不会证... # inclu ...
- 【bzoj3545/bzoj3551】[ONTAK2010]Peaks/加强版 Kruskal+树上倍增+Dfs序+主席树
bzoj3545 题目描述 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询 ...
- SPFA判負環
馬上就退役了,時間不足就不多介紹了 反正DFS是會T飛的,BFS就沒關係了qwq #include<cmath> #include<queue> #include<cst ...
- 在Linux上编译TCMalloc
TCMalloc(Thread-Caching Malloc)与标准glibc库的malloc实现一样的功能,但是TCMalloc在效率和速度效率都比标准malloc高很多.TCMalloc是goog ...
- WIN7系统插入蓝牙适配器经常断开问题
WIN7 ACER笔记本一台,蓝牙耳机一个,10块钱的蓝牙适配器一个 目的:可以在笔记本上用适配器与蓝牙耳机匹配 出现问题:1.有2个图标,一会左边感叹号,一会右边感叹号,必须有个存在感叹号 解决:第 ...
- 「CodePlus 2017 12 月赛」白金元首与独舞
description 题面 data range \[ 1 \leq T \leq 10, 1 \leq n, m \leq 200 , 0 \leq k \leq \min(nm, 300)\] ...
- POJ2724:Purifying Machine——题解
http://poj.org/problem?id=2724 描述迈克是奶酪工厂的老板.他有2^N个奶酪,每个奶酪都有一个00 ... 0到11 ... 1的二进制数.为了防止他的奶酪免受病毒侵袭,他 ...
- BZOJ4299 & CC FRBSUM:ForbiddenSum & BZOJ4408 & 洛谷4587 & LOJ2174:[FJOI2016]神秘数——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=4299 https://www.lydsy.com/JudgeOnline/problem.php? ...
- BZOJ3196 & 洛谷3380:二逼平衡树——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=3196 https://www.luogu.org/problemnew/show/P3380 (题 ...