文章目录
- 前言
- 1. 原型模式
- 1.1 适用场景
- 2. 原型模式的简单实现
前言
在23种常见设计模式概述——GoF 23(四人帮,Gang of Four)一文中提到过设计模式可以分为三种类型,分别为
- 创建型模式
- 结构型模式
- 行为型模式
1. 原型模式
这里介绍的原型模式(Prototype Pattern
)属于第一种类型,即创建型模式。对于创建型模式,侧重点也还是关注于如何生成新的对象。在原型模式中,通过复制一个现有的对象来生成新的对象,而不是通过实例化的方式。当复制一个对象时,新的对象将拥有与原对象相同的属性和方法。被复制的实例就是我们所称的“原型”。
在创建一个对象比较复杂或者构造比较耗时的时候,复制一个现有的实例可以提高程序的运行效率。
1.1 适用场景
- 类初始化需要消耗比较多的资源,通过原型拷贝可以避免这些消耗;
- 通过
new
产生一个对象需要非常繁琐的数据准备或者访问权限,使用原型拷贝可以避免; - 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值,可以考虑使用原型拷贝来复制多个对象给调用者;
2. 原型模式的简单实现
import java.util.ArrayList;
public class Man extends Human implements Cloneable{
private ArrayList<Human> familys;
public Man(){
System.out.println("--------------执行了Man的构造函数--------------");
familys = new ArrayList<>();
this.userAge = 0;
this.userName = "0";
}
@Override
protected Man clone() throws CloneNotSupportedException {
Man man = (Man) super.clone();
man.userName = this.userName;
man.userAge = this.userAge;
man.familys = this.familys;
return man;
}
public void setFamilys(ArrayList<Human> familys) {
this.familys = familys;
}
public ArrayList<Human> getFamilys() {
return familys;
}
public void showUserInfo(){
System.out.println("-----------------------------------");
System.out.println("姓名:" + this.userName);
System.out.println("年龄:" + this.userAge);
System.out.print("家庭成员:");
for (Human family : familys) {
System.out.print(family.userName+"\t");
}
System.out.println();
}
}
class Human{
// 定义为protected子类中是可访问
protected String userName;
protected int userAge;
public Human(){}
public Human(String userName, int userAge){
this.userAge = userAge;
this.userName = userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public void setUserAge(int userAge) {
this.userAge = userAge;
}
}
测试:
public class RandomizedSet {
public static void main(String[] args) throws CloneNotSupportedException {
Man man = new Man();
man.setUserName("张三");
man.setUserAge(21);
ArrayList<Human> familys = new ArrayList<>();
familys.add(new Human("张怡", 43));
familys.add(new Human("刘宝", 50));
man.setFamilys(familys);
man.showUserInfo();
Man clone = man.clone();
clone.showUserInfo();
clone.setUserName("王五");
clone.getFamilys().add(new Human("王二", 23));
clone.showUserInfo();
man.showUserInfo();
}
}
结果:
从上面的案例当中可以看到:拷贝后的对象和原对象的普通属性再是一个内存空间,因为基本类型不需要进行克隆。而字符串类型为常量,一旦定义出不同的字符串就会自动分配一块新的存储空间,也不会公用同一块存储空间。但是对于ArrayList
这种引用类型属性,还是使用的同一个空间。注意到通过clone
进行拷贝对象的时候,不会再执行类的构造函数。
其实也就是说上述拷贝为一个浅拷贝,并不是将所有字段都重新构造了一份副本,而是使用引用来指向同一个地址空间。如果需要解决这个问题,我们可以重新修改下clone
函数:
@Override
protected Man clone() throws CloneNotSupportedException {
Man man = (Man) super.clone();
man.userName = this.userName;
man.userAge = this.userAge;
man.familys = (ArrayList<Human>) this.familys.clone();
return man;
}
再次执行结果为:
References
- 《Android源码设计模式解析与实战》