设计模式_原型模式

本文详细介绍了Java中的原型模式,包括基本介绍、深拷贝问题及解决、原型模式的应用场景和扩展。通过实例展示了如何实现深拷贝,包括重写clone方法和利用对象序列化。此外,还探讨了原型模式在对象创建复杂但复制简单情况下的优势,以及原型模式在系统设计中的应用。
摘要由CSDN通过智能技术生成

设计模式_原型模式

问题

​ 大量相同或相似对象的创建问题,用传统的构造函数来创建对象。过程如下:

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接口,该接口表示该类能够复制且具有复制的能力->原型模式。

基本介绍

原型模式:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。

原型模式是一种创建型设计模式,允许一个对象再创建一个可定制的对象,无需了解创建的细节信息。

原型模式的结构和实现

结构

图1

  1. 抽象原型类(Prototype):规定了具体原型对象必须实现的接口。
  2. 具体原型类(ConcretePrototype1/ConcretePrototype2):实现抽象原型类的 clone() 方法,它是可被复制的对象。
  3. 访问类(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());
	}
}

图2

深拷贝基本介绍
  • 复制对象的所有基本数据类型的成员变量
  • 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量为所引用的对象,直到该对象可达的所有对象。也就是说,深拷贝要对整个对象进行拷贝。

实现方式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());
	}
}

结果如下:

图3

实现方式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());
	}
}

结果如下:

图4

原型模式的应用场景

原型模式通常适用于以下场景。

  • 对象之间相同或相似,即只是个别的几个属性不同的时候。
  • 对象的创建过程比较麻烦,但复制比较简单的时候。

原型模式需要为每一个类配备一个克隆方法,对于已经存在的类如果需要改造,则需要修改源码,违背了ocp原则。

原型模式的扩展

原型模式可扩展为带原型管理器的原型模式,它在原型模式的基础上增加了一个原型管理器 PrototypeManager 类。该类用 HashMap 保存多个复制的原型,Client 类可以通过管理器的 get(String id) 方法从中获取复制的原型。其结构图如下 所示:

图5

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();
	}
}

结果如下:

图6

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值