原型模式Prototype

简介

指从一个样板对象中复制出一个内部属性一致的对象,其实就是克隆。而被复制的对象就叫做原型,多用于创建复杂的或者构造耗时的实例

定义

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

场景

  • 类初始化需要消耗非常多的资源, 这个资源包括数据,硬件资源等, 可通过原型拷贝避免这些消耗
  • 通过 new 产生一个对象需要非常繁琐的数据准备或访问权限, 同样可以使用原型模式
  • 一个对象需要提供给其他对象访问, 并且会能会对其修改属性, 可以用原型拷贝多个对象提供使用

使用 Cloneable接口 , Objecct.clone()方法

  • 先看下Object.clone()方法说明
    表示:创建一个对象的副本。注意,clone不同于赋值,赋值是对象的引用传递,而clone是则创建一个与原型有相同数据的对象。
  protected native Object clone() throws CloneNotSupportedException;

看下clone后的区别

x.clone() != x;  //可以看到clone后的对象与原型不是同一个引用

x.clone().getClass() == x.getClass();  //但还是同个对象类型

x.clone().equals(x) == true;    //数据一样

如果一个对象使用object.clone的时候没有实现Cloneable接口,则会抛出一个ClassNotSupportedException异常。
此外,所有的array数组默认都已经实现了Cloneable接口

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
  • 例子
public class WordDocument implements Cloneable{
    // 文本
    public String mText;
    // 图片名列表
    public ArrayList<String> mImages = new ArrayList<String>();
    @Override
    protected WordDocument clone() {
        try {
            // 通过本地方法特殊途径, 构建一个对象
            WordDocument doc = (WordDocument) super.clone();
            //mText 虽然是对象, 但是因为是 String 类型, 属于安全类型, 由于final类,实例不可更改的特性. 如果对副本进行字符串的修改, 只不过是把原引用删除,重新指向了新的字符串.
            doc.mText = this.mText;
            // 因为Image是引用类型, 这样直接赋值属于浅拷贝, 再次对集合进行clone. 实现wordDocument的深拷贝
            doc.mImages = (ArrayList<String>) this.mImages.clone();
            // 这里进行修改 那么此时属于浅拷贝 
            //doc.mImages = this.mImages;
            return doc;
        }catch (Exception ex){}
        return null;
    }

}

本质也就是类实现Cloneable接口,再通过super.clone()构造有一个本类对象的一个初始状态,然后给被拷贝的对象赋值而已。
这里区别一下浅拷贝和深拷贝
* 浅拷贝(影子拷贝):拷贝出来的对象并不是完全的一份独立对象。新的对象属性中引用了原始对象中的数据。如代码中 doc.mImages = this.mImages 可以看到这里只是引用赋值了而已
* 深拷贝:拷贝出来的对象的属性与原始对象没有关联,是完全独立开来的一个副本。如代码中  doc.mImages = (ArrayList) this.mImages.clone(); 

拷贝

拷贝对象一般有两种方法,第一种就是new一个新的对象,第二种就是clone。
当new构造对象比较耗时和成本较高时,通过clone就可以获得效率提升

Android源码

@Override
public Object clone() {
   return new Intent(this);
}

/**
* Copy constructor.
*/
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<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进行数据复制,成本比较低

实战

当登录模块登录成功之后, 会把一些个人信息,token等信息在保存类中的某个数据结构上, 并通过一个方法对外暴露出去, 提供其他模块使用. 但是如果你返回的是一个数据结构也就是一个对象, 这个对象包含了很多个人信息, 但是正常来说, 对于外部应该只提供查看数据的能力, 不应该提供修改的能力.

也就是克隆一个对象给你读取数据,尽管它修改了数据,但毕竟不是修改在原始的数据上

所以这个使用, 就可以对登录模块对外暴露的方法进行修改, 利用 原型模式 对外返回的是一个内部数据的 深拷贝 , 这样就把可能出现的隐患彻底的隔绝了.

注意

原型模式 是通过内存中二进制流的方式拷贝, 要比直接通过 new 一个对象性能更好, 特别是循环体内产生大量对象是. 但是注意, 因为是 二进制流的拷贝 , 所以构造函数是不会执行的. 这点要明确记牢.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值