原型模式
通过复制实例原型,生成新的实例对象。
在GoF23种设计模式中,属于创建型模式( Creational patterns)
。
我们在new一个新对象时的成本是不低的。且当我们在创建一个复杂的对象,并需要多次创建时,对系统的开销更高。
原型模式正是解决这种问题的良方。
- 优点
- 提高性能(二进制流中进行对象copy)
- 不调用构造函数
- 使用创建操作简单
- 缺点
- 必须实现Cloneable接口
- 需要注意适当运用深浅拷贝
实现
原型模式实现很简单,只要实现Cloneable接口即可。
public class People implements Cloneable {
/**
* 基础数据类型
*/
private String name;
/**
* 引用数据类型
*/
private Secret secret;
public static class Secret {
private int age;
public Secret(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Secret{" +
"age=" + age +
'}' + super.toString();
}
}
public People(String name, Secret secret) {
this.name = name;
this.secret = secret;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Secret getSecret() {
return secret;
}
public void setSecret(Secret secret) {
this.secret = secret;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", secret=" + secret +
'}' + super.toString();
}
}
- 运行测试
public class Test {
public static void main(String[] args) throws CloneNotSupportedException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//对18号进行克隆
People x18 = new People("x战警18号",new People.Secret(18));
People x19 = (People) x18.clone();
System.out.println(x18);
System.out.println(x19);
//重命名
x19.setName("x战警19号");
x19.getSecret().setAge(19);
System.out.println(x18);
System.out.println(x19);
}
}
- 输出
//对18号进行克隆
People{name='x战警18号', secret=Secret{age=18}top.ordin.tool.design.pattern.creational.prototype.People$Secret@5b2133b1}top.ordin.tool.design.pattern.creational.prototype.People@72ea2f77
People{name='x战警18号', secret=Secret{age=18}top.ordin.tool.design.pattern.creational.prototype.People$Secret@5b2133b1}top.ordin.tool.design.pattern.creational.prototype.People@33c7353a
//重命名
People{name='x战警18号', secret=Secret{age=19}top.ordin.tool.design.pattern.creational.prototype.People$Secret@5b2133b1}top.ordin.tool.design.pattern.creational.prototype.People@72ea2f77
People{name='x战警19号', secret=Secret{age=19}top.ordin.tool.design.pattern.creational.prototype.People$Secret@5b2133b1}top.ordin.tool.design.pattern.creational.prototype.People@33c7353a
这里我们可以看到。
- 克隆出来的x19的内存地址为33c7353a,不同于原来的72ea2f77。是一个新对象。
- x19的Secret地址与原来一致。当我们修改x19的年龄时,x18的年龄也变了。这里的秘密属性显然不是个新的。
因此,我们把上面的拷贝过程称为浅拷贝。
这是原型模式在使用时需要注意的地方。如果我们要实现克隆一个完全新的对象,我就需要实现深拷贝。
深拷贝与浅拷贝
浅拷贝:仅拷贝对象的属性值。因此上面发生的显现就不难理解,对于引用型的Secret对象,在拷贝时仅是拷贝了该对象的引用地址。故而我们改变引用地址的值时,所有该地址的引用值都会改变。
深拷贝:在拷贝引用类型成员变量时,为引用类型的数据成员另辟了一个独立的内存空间,实现真正内容上的拷贝。
具体操作就是需要该对象的引用类型对象也要实现 Cloneable接口,在对象进行克隆时,对引用类型对象进行单独的克隆赋值。
public class People implements Cloneable {
/**
* 基础数据类型
*/
private String name;
/**
* 引用数据类型
*/
private Secret secret;
public static class Secret implements Cloneable{
private int age;
public Secret(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Secret{" +
"age=" + age +
'}' + super.toString();
}
}
public People(String name, Secret secret) {
this.name = name;
this.secret = secret;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Secret getSecret() {
return secret;
}
public void setSecret(Secret secret) {
this.secret = secret;
}
@Override
protected Object clone() throws CloneNotSupportedException {
People people = (People)super.clone();
people.secret = (Secret) secret.clone();
return people;
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", secret=" + secret +
'}' + super.toString();
}
}
- 运行测试
public class Test {
public static void main(String[] args) throws CloneNotSupportedException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//对18号进行克隆
People x18 = new People("x战警18号",new People.Secret(18));
People x19 = (People) x18.clone();
System.out.println(x18);
System.out.println(x19);
//重命名
x19.setName("x战警19号");
x19.getSecret().setAge(19);
System.out.println(x18);
System.out.println(x19);
}
}
- 输出
//对18号进行克隆
People{name='x战警18号', secret=Secret{age=18}top.ordin.tool.design.pattern.creational.prototype.People$Secret@5b2133b1}top.ordin.tool.design.pattern.creational.prototype.People@72ea2f77
People{name='x战警18号', secret=Secret{age=18}top.ordin.tool.design.pattern.creational.prototype.People$Secret@33c7353a}top.ordin.tool.design.pattern.creational.prototype.People@681a9515
//重命名
People{name='x战警18号', secret=Secret{age=18}top.ordin.tool.design.pattern.creational.prototype.People$Secret@5b2133b1}top.ordin.tool.design.pattern.creational.prototype.People@72ea2f77
People{name='x战警19号', secret=Secret{age=19}top.ordin.tool.design.pattern.creational.prototype.People$Secret@33c7353a}top.ordin.tool.design.pattern.creational.prototype.People@681a9515
总结
原型模式实现起来虽然很简单,但是一定要注意复制的对象是否符合预期。是拷贝了对象的值还是仅仅拷贝了对象的引用,也就是深克隆和浅克隆的合理运用。
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名,转载请标明出处
最后编辑时间为: