Android源码分析之原型模式

Android源码分析之原型模式

2014年08月06日 17:44:29

阅读数:3465

模式的定义

用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。


使用场景

1、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等,通过原型拷贝避免这些消耗;

2、通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式;

3、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝。


UML类图


角色介绍

Client  :  客户端用户。

Prototype : 抽象类或者接口,声明具备clone能力。

ConcretePrototype : 具体的原型类.


简单示例

下面我们以简单的文档拷贝为例来演示一下简单的原型模式模式。

 
  1. package com.dp.example.builder;

  2.  
  3.  
  4. package com.dp.example.prototype;

  5.  
  6. import java.util.ArrayList;

  7. import java.util.List;

  8.  
  9. /**

  10. * 文档类型, 扮演的是ConcretePrototype角色,而cloneable是代表prototype角色

  11. *

  12. * @author mrsimple

  13. */

  14. public class WordDocument implements Cloneable {

  15. /**

  16. * 文本

  17. */

  18. private String mText;

  19. /**

  20. * 图片名列表

  21. */

  22. private ArrayList<String> mImages = new ArrayList<String>();

  23.  
  24. public WordDocument() {

  25. System.out.println("----------- WordDocument构造函数 -----------");

  26. }

  27.  
  28. /**

  29. * 克隆对象

  30. */

  31. @Override

  32. protected WordDocument clone() {

  33. try {

  34. WordDocument doc = (WordDocument) super.clone();

  35. doc.mText = this.mText;

  36. doc.mImages = this.mImages;

  37. return doc;

  38. } catch (Exception e) {

  39. }

  40.  
  41. return null;

  42. }

  43.  
  44. public String getText() {

  45. return mText;

  46. }

  47.  
  48. public void setText(String mText) {

  49. this.mText = mText;

  50. }

  51.  
  52. public List getImages() {

  53. return mImages;

  54. }

  55.  
  56. /**

  57. * @param img

  58. */

  59. public void addImage(String img) {

  60. this.mImages.add(img);

  61. }

  62.  
  63. /**

  64. * 打印文档内容

  65. */

  66. public void showDocument() {

  67. System.out.println("----------- Word Content Start -----------");

  68. System.out.println("Text : " + mText);

  69. System.out.println("Images List: ");

  70. for (String imgName : mImages) {

  71. System.out.println("image name : " + imgName);

  72. }

  73. System.out.println("----------- Word Content End -----------");

  74. }

  75. }

  76.  

通过WordDocument类模拟了word文档中的基本元素,即文字和图片。WordDocument的在该原型模式示例中扮演的角色为ConcretePrototype, 而Cloneable的角色则为Prototype。WordDocument实现了clone方法以实现对象克隆。下面我们看看Client端的使用 : 

 
  1. public class Client {

  2. public static void main(String[] args) {

  3. WordDocument originDoc = new WordDocument();

  4. originDoc.setText("这是一篇文档");

  5. originDoc.addImage("图片1");

  6. originDoc.addImage("图片2");

  7. originDoc.addImage("图片3");

  8. originDoc.showDocument();

  9.  
  10. WordDocument doc2 = originDoc.clone();

  11. doc2.showDocument();

  12.  
  13. doc2.setText("这是修改过的Doc2文本");

  14. doc2.showDocument();

  15.  
  16. originDoc.showDocument();

  17. }

  18.  
  19. }

输出结果如下 : 

可以看到,doc2是通过originDoc.clone()创建的,并且doc2第一次输出的时候和originDoc输出是一样的。即doc2是originDoc的一份拷贝,他们的内容是一样的,而doc2修改了文本内容以后并不会影响originDoc的文本内容。需要注意的是通过clone拷贝对象的时候并不会执行构造函数!
 

浅拷贝和深拷贝

将main函数的内容修改为如下 : 

 
  1. public static void main(String[] args) {

  2. WordDocument originDoc = new WordDocument();

  3. originDoc.setText("这是一篇文档");

  4. originDoc.addImage("图片1");

  5. originDoc.addImage("图片2");

  6. originDoc.addImage("图片3");

  7. originDoc.showDocument();

  8.  
  9. WordDocument doc2 = originDoc.clone();

  10.  
  11. doc2.showDocument();

  12.  
  13. doc2.setText("这是修改过的Doc2文本");

  14. doc2.addImage("哈哈.jpg");

  15. doc2.showDocument();

  16.  
  17. originDoc.showDocument();

  18. }


输出结果如下 : 

细心的朋友可能发现了,在doc2添加了一张名为"哈哈.jpg"的照片,但是却也显示在originDoc中?这是怎么回事呢?  其实学习过C++的朋友都知道,这是因为上文中WordDocument的clone方法中只是简单的进行浅拷贝,引用类型的新对象doc2的mImages只是单纯的指向了this.mImages引用,而并没有进行拷贝。doc2的mImages添加了新的图片,实际上也就是往originDoc里添加了新的图片,所以originDoc里面也有"哈哈.jpg" 。那如何解决这个问题呢?  那就是采用深拷贝,即在拷贝对象时,对于引用型的字段也要采用拷贝的形式,而不是单纯引用的形式。示例如下 : 

 
  1. /**

  2. * 克隆对象

  3. */

  4. @Override

  5. protected WordDocument clone() {

  6. try {

  7. WordDocument doc = (WordDocument) super.clone();

  8. doc.mText = this.mText;

  9. // doc.mImages = this.mImages;

  10. doc.mImages = (ArrayList<String>) this.mImages.clone();

  11. return doc;

  12. } catch (Exception e) {

  13. }

  14.  
  15. return null;

  16. }

如上代码所示,将doc.mImages指向this.mImages的一份拷贝, 而不是this.mImages本身,这样在doc2添加图片时并不会影响originDoc,如图所示 :


源码分析

在Android源码中,我们以熟悉的Intent来分析源码中的原型模式。简单示例如下 : 

 
  1. Uri uri = Uri.parse("smsto:0800000123");

  2. Intent shareIntent = new Intent(Intent.ACTION_SENDTO, uri);

  3. shareIntent.putExtra("sms_body", "The SMS text");

  4.  
  5. Intent intent = (Intent)shareIntent.clone() ;

  6. startActivity(intent);

结果如下 : 

可以看到,我们通过shareIntent.clone方法拷贝了一个对象intent, 然后执行startActivity(intent), 随即就进入了短信页面,号码为0800000123,文本内容为The SMS text,即这些内容都与shareIntent一致。

下面我们看看Intent的clone的实现 : 

 
  1. @Override

  2. public Object clone() {

  3. return new Intent(this);

  4. }

  5.  
  6. /**

  7. * Copy constructor.

  8. */

  9. public Intent(Intent o) {

  10. this.mAction = o.mAction;

  11. this.mData = o.mData;

  12. this.mType = o.mType;

  13. this.mPackage = o.mPackage;

  14. this.mComponent = o.mComponent;

  15. this.mFlags = o.mFlags;

  16. if (o.mCategories != null) {

  17. this.mCategories = new ArraySet<String>(o.mCategories);

  18. }

  19. if (o.mExtras != null) {

  20. this.mExtras = new Bundle(o.mExtras);

  21. }

  22. if (o.mSourceBounds != null) {

  23. this.mSourceBounds = new Rect(o.mSourceBounds);

  24. }

  25. if (o.mSelector != null) {

  26. this.mSelector = new Intent(o.mSelector);

  27. }

  28. if (o.mClipData != null) {

  29. this.mClipData = new ClipData(o.mClipData);

  30. }

  31. }

    可以看到,clone方法实际上在内部调用了new Intent(this); 这就和C++中的拷贝构造函数完全一致了,而且是深拷贝。由于该模式比较简单,就不做太多说明。

优点与缺点

优点 :

1、原型模式是在内存二进制流的拷贝,要比直接 new 一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点。

缺点 :

1、这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的,在实际开发当中应该注意这个潜在的问题。优点就是减少了约束,缺点也是减少了约束,需要大家在实际应用时考虑。

猜你喜欢

转载自blog.csdn.net/xinzhou201/article/details/81672983
今日推荐