Android源码设计模式解析与实战(四)

1. 原型模式介绍

原型模式是一个创建型的模式。原型二字表明了该模式应该有一个样板实例,用户从这个样板对象中复制出一个内部属性一致的对象,这个过程就是我们俗称的“克隆”。

2. 使用场景

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

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

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

个人理解,原型模式,其实就是利用Java提供的克隆Cloneable生成对象的过程。

注意:通过Cloneable接口的原型模式并不一定比通过new操作快,不过,如果对象包含大量数据需要赋值,使用原型模式可以减少大量代码。

3. 浅拷贝和深拷贝

(1) 浅拷贝,也称为影子拷贝,这份拷贝实际上并不是将原始对象的所有字段都重新构造一份,而是使用的引用方式。即两个对象指向同一个地址。

public class WordDoc implements Cloneable {
    private int mSize;
    private String mText;
    private ArrayList<String> mImages = new ArrayList<String>();

    @Override
    protected WordDoc clone() {
        try {
            WordDoc doc = (WordDoc) super.clone();
            doc.mText = this.mText;
            doc.mImages = this.mImages;
            return doc;
        } catch (Exception e) {
        }
        return null;
    }
}

可以看到,这种方式,doc.mImages = this.mImages使用的是引用方式

(2) 深拷贝,即在拷贝对象时,对于引用型的字段也要采用拷贝的形式。

doc.mImages = (ArrayList<String) this.mImages.clone()

public class WordDoc implements Cloneable {
    private int mSize;
    private String mText;
    private ArrayList<String> mImages = new ArrayList<String>();

    @Override
    protected WordDoc clone() {
        try {
            WordDoc doc = (WordDoc) super.clone();
            doc.mText = this.mText;
            doc.mImages = (ArrayList<String) this.mImages.clone();
            return doc;
        } catch (Exception e) {
        }
        return null;
    }
}

这种方式,要求字段的类也要实现Cloneable接口,即是可以clone的。

Java基本类型int、byte、boolean不需要进行clone。

查看ArrayList源码,可以看到。

public class ArrayList<E> extends AbstractList<E> implements Cloneable, Serializable, RandomAccess {
    int size;
    transient Object[] array;
    
    @Override
    public Object clone() {
        try {
            ArrayList<?> result = (ArrayList<?>) super.clone();
            result.array = array.clone();
            return result;
        } catch (CloneNotSupportException e) {
            throw new AssertionError();
        }
    }
}

注意:通过clone方法实现对象克隆,但这个方法并不是Cloneable接口中的,而是Object中的方法。Cloneable也是一个标识接口,它表明这个类的对象是可以拷贝的。如果没有实现Cloneable接口却调用了clone()函数将抛出异常。

还要注意:通过clone拷贝对象时,并不会执行构造函数。

4. 原型模式实战

在开发中,有时会满足一些需求,就是有的对象中的内容允许客户端程序读取,而不允许修改。

public class User {
    public int age;
    public String name;
    public String address;
}
// 登录接口
public interface Login {
    void login();
}
// 登录实现
public class LoginImpl implements Login {
    @Override
    public void login() {
        // 登录到服务器,获取用户信息
        User loginUser = new User();
        // 将服务器返回的完整信息设置给loginUser对象
        loginUser.age = 22;
        loginUser.name = "Mr.Simple";
        login.address = "北京市";

        // 登录完后将用户信息设置到Session中
        LoginSession.getLoginSession().setLoginedUser(loginUser);
    }
}
// 登录session
public class LoginSession {
    static LoginSession sLoginSession = null;
    private User loginUser;
    private LoginSession() {
    }
    public static LoginSession getLoginSession() {
        if (sLoginSession == null) {
            sLoginSession = new LoginSession();
        }
        return sLoginSession;
    }
    // 设置登录的用户信息,不对外开放
    void setLoginedUser(User user) {
        loginUser = user;
    }
    public User getLoginedUser() {
        return loginedUser;
    }
}

问题:上面的getLoginedUser()方法,可以直接获取User对象,并更改对象内容,且会影响Session中的原对象内容。这样,客户端程序就不通过LoginSession设置用户信息了。

利用原型模式实现保护性拷贝即可避免这种问题

public class LoginSession {
    ......
    public User getLoginedUser() {
        return loginedUser.clone();
    }
}

这种方式获取的是一个拷贝对象,修改拷贝对象也不会影响最初的登录用户对象。

转载于:https://my.oschina.net/android520/blog/833624

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值