写在前面的话
复习、总结23种设计模式
上一篇
原型模式
定义
不通过new关键字来产生一个对象,而是通过对象复制来实现的模式就叫做原型模式
原型模式有两种表现形式:
(1)简单形式、
(2)登记形式
这两种表现形式仅仅是原型模式的不同实现。
1、简单形式的原型模式
这种形式涉及到三个角色:
客户(Client)角色:客户类提出创建对象的请求。
抽象原型(Prototype)角色:这是一个抽象角色,通常由一个Java接口或Java抽象类实现。此角色给出所有的具体原型类所需的接口。
具体原型(Concrete Prototype)角色:被复制的对象。此角色需要实现抽象的原型角色所要求的接口。
java
package com.design.pattern.prototype.test04;
// 抽象原型角色
public interface Prototype {
/**
* 克隆自身的方法
*
* @return 一个从自身克隆出来的对象
*/
public Object clone();
}
// 具体原型角色
class ConcretePrototype1 implements Prototype {
public Prototype clone() {
//最简单的克隆,新建一个自身对象,由于没有属性就不再复制值了
Prototype prototype = new ConcretePrototype1();
return prototype;
}
}
class ConcretePrototype2 implements Prototype {
public Prototype clone() {
//最简单的克隆,新建一个自身对象,由于没有属性就不再复制值了
Prototype prototype = new ConcretePrototype2();
return prototype;
}
}
java
package com.design.pattern.prototype.test04;
// 客户端角色
public class Client {
/**
* 持有需要使用的原型接口对象
*/
private Prototype prototype;
/**
* 构造方法,传入需要使用的原型接口对象
*/
public Client(Prototype prototype) {
this.prototype = prototype;
}
public void operation(Prototype example) {
//需要创建原型接口的对象
Prototype copyPrototype = (Prototype) prototype.clone();
System.out.println(copyPrototype);
}
}
2、登记形式的原型模式
作为原型模式的第二种形式,它多了一个原型管理器(PrototypeManager)角色,
该角色的作用是:创建具体原型类的对象,并记录每一个被创建的对象。
3.两种形式的比较
简单形式和登记形式的原型模式各有其长处和短处。
如果需要创建的原型对象数目较少而且比较固定的话,可以采取第一种形式。在这种情况下,原型对象的引用可以由客户端自己保存。
如果要创建的原型对象数目不固定的话,可以采取第二种形式。在这种情况下,客户端不保存对原型对象的引用,这个任务被交给管理员对象。在复制一个原型对象之前,客户端可以查看管理员对象是否已经有一个满足要求的原型对象。如果有,可以直接从管理员类取得这个对象引用;如果没有,客户端就需要自行复制此原型对象。
构造函数不会被执行
java
package com.design.pattern.prototype.test01;
/*
如果Things没有实现java.lang.Cloneable接口,
在调用super.clone()时会抛出CloneNotSupportedException
*/
public class Things implements Cloneable {
public Things() {
System.out.println("构造函数被执行...");
}
@Override
protected Object clone() throws CloneNotSupportedException {
// 如果Things没有实现java.lang.Cloneable接口,在调用super.clone()时会抛出CloneNotSupportedException
return super.clone();
}
}
Object类的clone()的原理是从内存中以二进制流的方式进行拷贝,重新分配一个内存块,那构造函数没有被执行也是非常正常的了
java
package com.design.pattern.prototype.test01;
/**
Object类的clone()的原理是从内存中以二进制流的方式进行拷贝,
重新分配一个内存块,那构造函数没有被执行也是非常正常的了
*/
public class Test01构造函数不会被执行 {
public static void main(String[] args) {
Things things = new Things();
try {
Object clone = things.clone();
System.out.println(things);
System.out.println(clone);
System.out.println(clone instanceof Things);
} catch (Exception e) {
e.printStackTrace();
}
}
}
原型模式实现
原型模式的克隆分为浅克隆和深克隆
浅拷贝
Object类的clone()只是拷贝本对象,其内部对象的数组,引用对象都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做浅拷贝
java
package com.design.pattern.prototype.test02;
import java.util.ArrayList;
import java.util.List;
public class Person implements Cloneable{
private List<String> value = new ArrayList<String>();
@Override
protected Person clone() {
try {
return (Person) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
public void addValue(String v) {
this.value.add(v);
}
public List<String> getValue() {
return this.value;
}
}
java
package com.design.pattern.prototype.test02;
import java.util.Arrays;
/*
Object类的clone()只是拷贝本对象,其内部对象的数组,
引用对象都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做浅拷贝
*/
public class Test02浅拷贝 {
public static void main(String[] args) {
Person person = new Person();
person.addValue("123");
Person newPerson = person.clone();
newPerson.addValue("456");
System.out.println(person);
System.out.println(newPerson);
// [123, 456]
System.out.println(Arrays.toString(person.getValue().toArray(new String[0])));
// [123, 456]
System.out.println(Arrays.toString(newPerson.getValue().toArray(new String[0])));
}
}
深拷贝
深度克隆要深入到多少层,是一个不易确定的问题。在决定以深度克隆的方式复制一个对象的时候,必须决定对间接复制的对象时采取浅度克隆还是继续采用深度克隆。因此,在采取深度克隆时,需要决定多深才算深。此外,在深度克隆的过程中,很可能会出现循环引用的问题,必须小心处理。
java
package com.design.pattern.prototype.test03;
import java.util.ArrayList;
import java.util.List;
public class Person implements Cloneable{
private ArrayList<String> value = new ArrayList<String>();
@Override
protected Person clone() {
try {
Person clone = (Person) super.clone();
clone.value = (ArrayList<String>) this.value.clone();
return clone;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
public void addValue(String v) {
this.value.add(v);
}
public List<String> getValue() {
return this.value;
}
}
java
package com.design.pattern.prototype.test03;
import java.util.Arrays;
public class Test03深拷贝 {
public static void main(String[] args) {
Person person = new Person();
person.addValue("123");
Person newPerson = person.clone();
newPerson.addValue("456");
System.out.println(person);
System.out.println(newPerson);
// [123]
System.out.println(Arrays.toString(person.getValue().toArray(new String[0])));
// [123, 456]
System.out.println(Arrays.toString(newPerson.getValue().toArray(new String[0])));
}
}
浅拷贝与深拷贝的区别
深拷贝:在对类中引用数据类型(类的实例对象)进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量
浅拷贝:只对类中基本数据类型进行了拷贝,而对引用数据类型只是进行了引用的传递,而没有真实的创建一个新的对象
浅拷贝和深拷贝是相对的,如果一个对象内部只有基本数据类型,那用 clone() 方法获取到的就是这个对象的深拷贝,而如果其内部还有引用数据类型,那用 clone() 方法就是一次浅拷贝的操作