前言
原型模型的使用在Android里最大的体现是Intent,通过看源码不难发现,intent传值进去之后在startActivity之前会clone一份Intent,说回来原型模式其实就牵扯到拷贝问题,换一句话,预防对象被意外改动使用拷贝就是原型模式。
使用场景
1.类初始化操作复杂,牵扯到太多资源的。
2.一个对象需要提供给其他对象访问,那各个调用它的对象都可能更改它的值,但是更改的值只对该对象使用有效(如果移除更改别的地方也跟着改动,那应该考虑单例模式了)
简单实现
在开发中常常会存储一些参数,而这些参数不能够提供对外的修改方法,只能够提供私有的存储方法,在指定的时候调用,比如我们要写一个外卖地址存储模块,当用户修改地址之后并不立刻去修改缓存信息,而是等待接口返回之后再同步Client和Service的用户信息。抽象出一个User类为用户信息类,定义一个UserManager类。为User管理类。首先我们来看看User类:
package com.demo.prototype;
/**
* Created by italkbb on 2017/12/13.
*/
public class User implements Cloneable{
private String age;
private String sex;
private String name;
private String address;
private String phoneNumber;
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
/**
* 复写clone方法,以便于获取用户实体的一个备份
* @return
* @throws CloneNotSupportedException
*/
@Override
protected User clone() throws CloneNotSupportedException {
return (User) super.clone();
}
}
用户类里定义了私有的获取用户信息方法,只不过这个用户信息是一个备份,不会影响缓存实体本身。
下面单例模式在同级包下面定义一个用户管理类:
package com.demo.prototype;
/**
* Created by italkbb on 2017/12/13.
*/
public class UserManager {
private User mUser;
private UserManager(){}
public static UserManager getInstance(){
return UserManagerHolder.mUserManager;
}
private static class UserManagerHolder{
private static final UserManager mUserManager = new UserManager();
}
protected void setUserInfo(User user){
mUser = user;
}
/**
* 获取到clone的对象,这里外面做一个判空,以防万一
* @return
* @throws CloneNotSupportedException
*/
public User getUserInfo() throws CloneNotSupportedException {
return mUser.clone();
}
}
管理类里面对外公布了获取用户信息备份的方法,但修改方法是私有的,不允许随便更改,那么用户信息接口请求回来之后必须要更改这些信息,已达到两边同步,那么假设有一个接口回调处理,我们模拟一个信息修改接口:
package com.demo.prototype;
/**
* Created by italkbb on 2017/12/13.
*/
public interface ModifyInfo {
// 信息修改成功调用的接口方法
void modifySucceed(User user);
// 信息修改失败调用的方法,这里面可以清理一些资源。
void modifyFailed(Error error);
}
接下来在接口的实现类里面更改这个用户信息,注意我们应该吧接口回调实现类放在UserManager的同包目录,因为这是一个模块,所以我的设计就是把用户信息更新方法为包级私有。
package com.demo.prototype;
/**
* Created by italkbb on 2017/12/13.
*/
public class ModifyInfoImpl implements ModifyInfo {
@Override
public void modifySucceed(User user) {
// 获取得到服务器用户信息,同步到本地缓存
// 单例模式获取管理类,通过管理类来同步用户数据
UserManager.getInstance().setUserInfo(user);
}
@Override
public void modifyFailed(Error error) {
}
}
这样简单的原型模式就完成了,我们使用的时候只有接口回来才会修改用户信息,其他时候并没有权限去调用修改的方法。
关于拷贝
在上面的demo中,我们用到了clone,这种拷贝叫做深拷贝,拷贝对象的修改不会影响之前的对象,对于基本数据类型,用等号赋值的效果就是深拷贝的效果,但是对于非基本数据类型,等号只是一个浅拷贝的过程,更改拷贝数据,源数据也会跟着改变,所以在这个模式里面,应该尽可能使用深拷贝。
后记
任何模式都会有优缺点,原型模式的有点事避免了数据被莫名的改动,通过二进制拷贝要比new对象性能好很多,如果对象越多,这个优点就越明显;缺点就是clone时候,对象的构造函数不会执行,那么在构造函数执行相关操作,应该多考虑考虑,是否满足需求。我的邮箱redzkh@gmail.com,有错误还望指正。