设计模式_原型模式
问题
大量相同或相似对象的创建问题,用传统的构造函数来创建对象。过程如下:
Sheep.java
public class Sheep {
private int age;
private String name;
private String color;
public Sheep(int age, String name, String color) {
super();
this.age = age;
this.name = name;
this.color = color;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Sheep [age=" + age + ", name=" + name + ", color=" + color
+ "]";
}
}
client.java
public class Client {
public static void main(String[] args) {
Sheep sheep1 = new Sheep(2, "sheep1", "black");
Sheep sheep2 = new Sheep(sheep1.getAge(), sheep1.getName(),
sheep1.getColor());
Sheep sheep3 = new Sheep(sheep1.getAge(), sheep1.getName(),
sheep1.getColor());
Sheep sheep4 = new Sheep(sheep1.getAge(), sheep1.getName(),
sheep1.getColor());
Sheep sheep5 = new Sheep(sheep1.getAge(), sheep1.getName(),
sheep1.getColor());
}
}
特点
- 简单、易于理解
- 创建对象需要重新获取原始对象的属性,效率低下
- 需要重新初始化对象,而不是动态的获取队形运行时状态,不够灵活
改进
Object类中有一个clone()方法,该方法可以将一个对象复制一份,只需要实现了clone的Java类实现Cloneable接口,该接口表示该类能够复制且具有复制的能力->原型模式。
基本介绍
原型模式:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。
原型模式是一种创建型设计模式,允许一个对象再创建一个可定制的对象,无需了解创建的细节信息。
原型模式的结构和实现
结构
- 抽象原型类(Prototype):规定了具体原型对象必须实现的接口。
- 具体原型类(ConcretePrototype1/ConcretePrototype2):实现抽象原型类的 clone() 方法,它是可被复制的对象。
- 访问类(Client):使用具体原型类中的 clone() 方法来复制新的对象。
实现
Sheep.java
public class Sheep implements Cloneable {
private int age;
private String name;
private String color;
public Sheep(int age, String name, String color) {
super();
this.age = age;
this.name = name;
this.color = color;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Sheep [age=" + age + ", name=" + name + ", color=" + color
+ "]";
}
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
}
Client.java
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Sheep sheep = new Sheep(2, "sheep1", "black");
Sheep sheep1 = (Sheep) sheep.clone();// 克隆
Sheep sheep2 = (Sheep) sheep.clone();
Sheep sheep3 = (Sheep) sheep.clone();
Sheep sheep4 = (Sheep) sheep.clone();
}
}
spring中配置bean的scope为“prototype”,即使用原型模式来创建实例,多次获取同一id的bean是不同的对象。
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { //....省略部分代码 // 创建实例. if (mbd.isSingleton()) { //是否单例 sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { // 显式地从单例缓存中删除实例:创建过程可能会急切地将实例放在那里,以实现循环引用解析。还要删除接收到对该bean的临时引用的任何bean destroySingleton(beanName); throw ex; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) {//是否原型 // It's a prototype -> create a new instance. Object prototypeInstance = null; try { beforePrototypeCreation(beanName); prototypeInstance = createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } //....省略部分代码 }
深拷贝
问题
对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
对于数据类型是引用类型的成员变量,比如成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因此实际上两个对象的该成员变量都指向了同一个实例。这种情况下,一个对象的修改就会影响到另一个对象的变量值。
为上例中的Sheep添加一个引用属性parent。如下:
Sheep.java
public class Sheep implements Cloneable {
private int age;
private String name;
private String color;
private Sheep parent;
public Sheep(int age, String name, String color) {
super();
this.age = age;
this.name = name;
this.color = color;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Sheep [age=" + age + ", name=" + name + ", color=" + color
+ ", parent=" + parent + "]";
}
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
}
Client.java
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Sheep sheep = new Sheep(2, "sheep", "black");
sheep.setParent(new Sheep(4, "sheep_parent", "white"));
Sheep sheep1 = (Sheep) sheep.clone();// 克隆
Sheep sheep2 = (Sheep) sheep.clone();
Sheep sheep3 = (Sheep) sheep.clone();
Sheep sheep4 = (Sheep) sheep.clone();
System.out.println("sheep parent hashCode"
+ sheep.getParent().hashCode());
System.out.println("sheep1 parent hashCode"
+ sheep1.getParent().hashCode());
System.out.println("sheep2 parent hashCode"
+ sheep2.getParent().hashCode());
System.out.println("sheep3 parent hashCode"
+ sheep3.getParent().hashCode());
System.out.println("sheep4 parent hashCode"
+ sheep4.getParent().hashCode());
}
}
深拷贝基本介绍
- 复制对象的所有基本数据类型的成员变量
- 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量为所引用的对象,直到该对象可达的所有对象。也就是说,深拷贝要对整个对象进行拷贝。
实现方式1:重写clone方法实现深拷贝。
DeepCloneableTarget.java
public class DeepCloneableTarget implements Cloneable, Serializable {
private static final long serialVersionUID = 1L;
private String cloneName;
private String cloneClass;
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
}
DeepProtoType.java
public class DeepProtoType implements Cloneable, Serializable {
private static final long serialVersionUID = 1L;
public String name;// String 类型
public DeepCloneableTarget deepCloneableTarget;// 引用类型
@Override
protected Object clone() throws CloneNotSupportedException {
Object deep = null;
deep = super.clone();// 完成基本数据类型和String的克隆
DeepProtoType deepProtoType = (DeepProtoType) deep;// 引用类型的克隆
deepProtoType.deepCloneableTarget = (DeepCloneableTarget) deepCloneableTarget
.clone();
return deepProtoType;
}
}
Client.java
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
DeepProtoType deepProtoType = new DeepProtoType();
deepProtoType.name = "ZhangSan";
deepProtoType.deepCloneableTarget = new DeepCloneableTarget();
DeepProtoType deepProtoType1 = (DeepProtoType) deepProtoType.clone();// 克隆
DeepProtoType deepProtoType2 = (DeepProtoType) deepProtoType.clone();
DeepProtoType deepProtoType3 = (DeepProtoType) deepProtoType.clone();
System.out.println("deepProtoType deepCloneableTarget hashCode "
+ deepProtoType.deepCloneableTarget.hashCode());
System.out.println("deepProtoType1 deepCloneableTarget hashCode "
+ deepProtoType1.deepCloneableTarget.hashCode());
System.out.println("deepProtoType2 deepCloneableTarget hashCode "
+ deepProtoType2.deepCloneableTarget.hashCode());
System.out.println("deepProtoType3 deepCloneableTarget hashCode "
+ deepProtoType3.deepCloneableTarget.hashCode());
}
}
结果如下:
实现方式2:通过对象序列化实现深拷贝。(推荐)
在DeepProtoType.java中新增方法deepClone()如下:
public Object deepClone() {
// 创建流对象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
// 序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);// 将当前对象以对象流的方式输出
// 反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
DeepProtoType copyObj = (DeepProtoType) ois.readObject();
return copyObj;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
try {
ois.close();
bis.close();
oos.close();
bos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Client.java
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
DeepProtoType dp1 = new DeepProtoType();
dp1.name = "ZhangSan";
dp1.deepCloneableTarget = new DeepCloneableTarget();
DeepProtoType dp2 = (DeepProtoType) dp1.deepClone();
System.out.println("dp1.name=" + dp1.name
+ ",dp1.deepCloneableTarget.hashCode="
+ dp1.deepCloneableTarget.hashCode());
System.out.println("dp2.name=" + dp2.name
+ ",dp2.deepCloneableTarget.hashCode="
+ dp2.deepCloneableTarget.hashCode());
}
}
结果如下:
原型模式的应用场景
原型模式通常适用于以下场景。
- 对象之间相同或相似,即只是个别的几个属性不同的时候。
- 对象的创建过程比较麻烦,但复制比较简单的时候。
原型模式需要为每一个类配备一个克隆方法,对于已经存在的类如果需要改造,则需要修改源码,违背了ocp原则。
原型模式的扩展
原型模式可扩展为带原型管理器的原型模式,它在原型模式的基础上增加了一个原型管理器 PrototypeManager 类。该类用 HashMap 保存多个复制的原型,Client 类可以通过管理器的 get(String id) 方法从中获取复制的原型。其结构图如下 所示:
Shape.java
public interface Shape extends Cloneable {
public Object clone(); // 拷贝
public void countArea(); // 计算面积
}
Circle.java
import java.util.Scanner;
public class Circle implements Shape {// 圆
@Override
public void countArea() {
int r = 0;
System.out.print("这是一个圆,请输入圆的半径:");
Scanner input = new Scanner(System.in);
r = input.nextInt();
System.out.println("该圆的面积=" + 3.1415 * r * r);
}
public Object clone() {
Circle circle = null;
try {
circle = (Circle) super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("拷贝圆失败!");
}
return circle;
}
}
Square.java
import java.util.Scanner;
public class Square implements Shape {// 正方形
@Override
public void countArea() {
int a = 0;
System.out.print("这是一个正方形,请输入它的边长:");
Scanner input = new Scanner(System.in);
a = input.nextInt();
System.out.println("该正方形的面积=" + a * a);
}
public Object clone() {
Square square = null;
try {
square = (Square) super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("拷贝正方形失败!");
}
return square;
}
}
ProtoTypeManager.java
import java.util.HashMap;
public class ProtoTypeManager {
HashMap<String, Shape> hp = new HashMap<String, Shape>();
public ProtoTypeManager() {
hp.put("Circle", new Circle());
hp.put("Square", new Square());
}
public void addShape(String key, Shape obj) {
hp.put(key, obj);
}
public Shape getShape(String key) {
Shape temp = hp.get(key);
return (Shape) temp.clone();
}
}
ProtoTypeShape.java
public class ProtoTypeShape {
public static void main(String[] args) {
ProtoTypeManager pm = new ProtoTypeManager();
Shape shape1 = (Circle) pm.getShape("Circle");
shape1.countArea();
Shape shape2 = (Square) pm.getShape("Square");
shape2.countArea();
}
}
结果如下: