原型模式就是用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象
也就是说用户从一个实例中复制出一个内部属性一致的对象,这个被复制的对象就是原型。
原型模式多用于创建复杂的或者构造耗时的实例,因为这种情况下复制一个已经存在的实例可使程序运行更高效。
1,类初始化需要消耗非常多的资源
2,通过new产生一个对象需要非常繁琐的数据准备或访问权限
3,一个对象需要提供给其他对象访问,而且各个调用者都可能会修改其值时,可以使用原型模式拷贝多个对象供调用者使用,即保护性拷贝
注意:通过实现Cloneable接口的原型模式在调用clone函数构造实例时并不一定比通过new操作速度快,只有当通过new构造对象较为耗时时,通过clone方法才能获得效率上的提高。
假设有这样一个需求,用户通过网络请求获取到服务器上的用户信息,其他模块可以拿到用户信息,本地修改用户信息以供展示用,但是各个模块拿到的用户信息必须得是服务器上请求的原始的用户信息。
这种情况下我们就可以使用原型模式来实现。
首先新建我们的user类:实现clone方法,拷贝对象
/**
* 用户信息 实现cloneable接口
* */
public class User implements Cloneable {
private String userName;
private int age;
private String address;
public void setUserName(String userName) {
this.userName = userName;
}
public void setAge(int age) {
this.age = age;
}
public void setAddress(String address) {
this.address = address;
}
public String getUserName() {
return userName;
}
public int getAge() {
return age;
}
public String getAddress() {
return address;
}
@NonNull
@Override
protected User clone() throws CloneNotSupportedException {
User user = (User) super.clone();
user.userName =this.userName;
user.address =this.address;
user.age =this.age;
return user;
}
}
然后创建缓存数据类,注意,返回的是User的clone对象:
public class UserDao {
private User user;
private UserDao(){
}
public static UserDao getInstance(){
return UserDao.SingletonHolder.userDao;
}
private static class SingletonHolder{
private static final UserDao userDao =new UserDao();
}
public void setUser(User user) {
this.user = user;
}
public User getUser() {
try {
return user.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}
然后模拟服务器获取数据和不同模块间设置用户信息的操作:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getUserFromNet();
setUserAgeM1();
setUserAgeM2();
}
/**
* 模拟从服务器获取user数据存到本地
* */
public void getUserFromNet(){
User user =new User();
user.setUserName("袁震");
user.setAge(20);
user.setAddress("淄博");
UserDao.getInstance().setUser(user);
}
/**
* 模拟M1模块设置年龄
* */
public void setUserAgeM1(){
User user =UserDao.getInstance().getUser();
Log.d(TAG,"yz-----M1模块获得年龄:"+user.getAge());
user.setAge(28);
Log.d(TAG,"yz-----M1模块设置年龄:"+user.getAge());
}
/**
* 模拟M2模块获得年龄
* */
public void setUserAgeM2(){
User user =UserDao.getInstance().getUser();
Log.d(TAG,"yz-----M2模块获得年龄:"+user.getAge());
user.setAge(30);
Log.d(TAG,"yz-----M2模块设置年龄:"+user.getAge());
}
}
最后输出结果:
原型模式不一定必须通过实现cloneable接口来实现,因为有时候拷贝的效率并不一定比new的效率高。
在上述案例中,User类也可以这样实现:
/**
* 用户信息 实现cloneable接口
* */
public class User {
private String userName;
private int age;
private String address;
public void setUserName(String userName) {
this.userName = userName;
}
public void setAge(int age) {
this.age = age;
}
public void setAddress(String address) {
this.address = address;
}
public String getUserName() {
return userName;
}
public int getAge() {
return age;
}
public String getAddress() {
return address;
}
protected User clone(){
User user = new User();
user.userName =this.userName;
user.address =this.address;
user.age =this.age;
return user;
}
}
原型模式本质上就是对象拷贝,与C++中的深拷贝,浅拷贝类似,不了解的可以查看文章Android JNI2--C++基础-CSDN博客
优点:原型模式是在内存中二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好的体现其优点
缺点:直接在内存中拷贝,构造函数是不会执行的,实际开发时需要注意
参考文献:Android源码设计模式解析与实战第二版