首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >设计模式之原型模式

设计模式之原型模式

作者头像
爱撒谎的男孩
发布2018-05-25 16:11:05
3750
发布2018-05-25 16:11:05
举报
文章被收录于专栏:码猿技术专栏码猿技术专栏

创建型模式之原型模式

定义

  • 原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
  • 这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要 在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
  • 原型模式可以分为浅克隆和深度克隆

角色

角色
角色

角色

java语言中实现克隆的两种方式

  1. 直接创建一个对象,然后设置成员变量的值
Obj obj=new Obj(); //创建一个新的对象
obj.setName(this.name);   //设置其中变量的值
obj.setAge(this.age);
  1. 实现cloneable接口

浅克隆

  • 如果克隆的对象的成员变量是值类型的,比如int,double那么使用浅克隆就可以实现克隆完整的原型对象,但是如果其中的成员变量有引用类型的,那么这个引用类型的克隆过去的其实是地址,克隆对象的这个引用类型变量改变了,那么原来变量的值也是会改变的。
  • 简单的说,浅克隆只能复制值类型的,对于引用类型的数据只能复制地址
浅克隆
浅克隆

浅克隆

实例

  • 一个公司出版周报,那么这个周报的格式一般是相同的,只是将其中的内容稍作修改即可。但是一开始没有这个原型,员工每周都需要重新手写这个周报,现在有了这个周报的原型,只需要在这个clone这个原型,然后在其基础上修改即可。
周报
周报

周报

  • 其中的Cloneable就是抽象原型类
  • 附件类(这个是一个引用类型的对象,验证浅克隆只是复制其中的地址,如果两个对象中的任何一个改变了这个变量的值,那么另外一个也会随之改变)
/*
 * 附件类,这个是周报的附件
 */
public class Attachment {
	private String name; // 名称

	public Attachment(String name) {
		super();
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}
  • 周报的类(其中实现了Cloneable接口)
    • 其中的clone()方法返回的就是一个克隆的对象,因此我们调用这个方法克隆一个新的对象
/*
 * 这个是周报类,这个类是实现接口Prototype这个接口的
 */
public class WeeklyLog implements Cloneable {
	private String name; // 姓名
	private String date; // 日期
	private String content; // 内容
	private Attachment attachment;  //附件,是一个引用对象,这个只能实现浅克隆
	public WeeklyLog() {
		super();
	}
	/**
	 * 构造方法
	 */
	public WeeklyLog(String name, String date, String content) {
		super();
		this.name = name;
		this.date = date;
		this.content = content;
	}
	/**
	 * 提供一个clone方法,返回的是一个clone对象
	 */
	public WeeklyLog clone() {
		Object object = null; // 创建一个Object对象
		try {
			object = super.clone(); // 直接调用clone方法,复制对象
			return (WeeklyLog) object; // 返回即可
		} catch (CloneNotSupportedException e) {
			System.out.println("这个对象不能复制.....");
			return null;
		}
	}

}
  • 测试类
    • 测试浅克隆的值类型是是否完成复制了
    • 测试引用类型的值能否完成克隆,还是只是复制了一个引用地址
    • 从结果来看,对象是完成复制了,因为判断两个对象的地址是不一样的,但是其中的引用类型的成员变量没有完成复制,只是复制了一个地址
public class Client {
	public static void main(String[] args) throws CloneNotSupportedException {

		WeeklyLog p1 = new WeeklyLog("陈加兵", "第一周", "获得劳动模范的称号..."); // 创建一个对象
		Attachment attachment = new Attachment("消息");
		p1.setAttachment(attachment); // 添加附件
		WeeklyLog p2 = p1.clone();
		System.out.println(p1 == p2); // 判断是否正确
		p2.setName("Jack"); // 修改P2对象的内容
		p2.setDate("第二周");
		p2.setContent("工作认真.....");
		System.out.println(p2.getName());
		// 返回true,可以知道这两个附件的地址是一样的
		System.out.println(p1.getAttachment() == p2.getAttachment());
	}
}

总结

  • 浅克隆对于值类型的数据可以复制成功,但是对于引用卡类型的数据只能复制一个地址,如果一个对象中的引用类型的变量的值改变了,那么另外一个也会随之改变

深度克隆

  • 浅克隆只能完成复制值类型,深度克隆可以完成复制引用类型和值类型
深度克隆
深度克隆

深度克隆

条件

  1. 引用类型的变量类实现序列化(实现Serializabl接口)
  2. 需要克隆的类实现序列化(实现Serializable接口)

为什么实现序列化

  • 因为深度克隆的实现的原理是使用输入和输出流,如果想要将一个对象使用输入和输出流克隆,必须序列化。

实现

  • 附件类(引用类型的成员变量,实现序列化)
/*
 * 附件类,这个是周报的附件
 */
public class Attachment implements Serializable{
	private static final long serialVersionUID = -799959163401886355L;
	private String name; // 名称
	public Attachment(String name) {
		super();
		this.name = name;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}
  • 周报类(需要克隆的类,因为其中有引用类型的成员变量,因此需要实现序列化)
/*
 * 这个是周报类,这个类是实现接口Prototype这个接口的
 */
public class WeeklyLog implements Serializable {
	private static final long serialVersionUID = -8782492113927035907L;
	private String name; // 姓名
	private String date; // 日期
	private String content; // 内容
	private Attachment attachment; // 附件,是一个引用对象,这个只能实现浅克隆
	public WeeklyLog() {
		super();
	}
	/**
	 * 构造方法
	 */
	public WeeklyLog(String name, String date, String content) {
		super();
		this.name = name;
		this.date = date;
		this.content = content;
	}
	/**
	 * 提供一个clone方法,返回的是一个clone对象
	 */
	public WeeklyLog clone() {
		// 将对象写入到对象流中
		ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
		try {
			ObjectOutputStream objectOutputStream = new ObjectOutputStream(
					arrayOutputStream); // 创建对象输出流
			objectOutputStream.writeObject(this); // 将这个类的对象写入到输出流中
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}

		// 将对象从流中读出
		ByteArrayInputStream arrayInputStream = new ByteArrayInputStream(
				arrayOutputStream.toByteArray());
		WeeklyLog weeklyLog;
		try {
			ObjectInputStream objectInputStream = new ObjectInputStream(
					arrayInputStream);// 新建对象输入流
			weeklyLog = (WeeklyLog) objectInputStream.readObject(); // 读取对象从流中
			return weeklyLog;
		} catch (IOException | ClassNotFoundException e) {
			e.printStackTrace();
			return null;
		}

	}

}
  • 测试类
    • 从中可以看出其中的附件地址是不同的,如果一个对象的附件变量改变了,那么另外一个将保持不变,因此实现了深度克隆,是两个完全不同的对象
public class Client {
	public static void main(String[] args) throws CloneNotSupportedException {

		WeeklyLog p1 = new WeeklyLog("陈加兵", "第一周", "获得劳动模范的称号..."); // 创建一个对象
		Attachment attachment = new Attachment("消息");
		p1.setAttachment(attachment); // 添加附件
		WeeklyLog p2 = p1.clone();
		System.out.println(p1 == p2); // 判断是否正确
		p2.setName("Jack"); // 修改P2对象的内容
		p2.setDate("第二周");
		p2.setContent("工作认真.....");
		System.out.println(p2.getName());
		//返回false,可以看出这个是不同的地址,因此完成了深克隆
		System.out.println(p1.getAttachment() == p2.getAttachment());
	}
}

总结

  • 因为深度克隆使用的是将对象写入输入和输出流中的,因此需要实现序列化,否则将不能完成

总结

  1. 浅克隆只能克隆对象中的值类型,不能克隆有引用类型成员变量的对象
  2. 使用深度克隆:
    • 引用类型的成员变量的类必须实现序列化
    • 需要克隆的类必须实现序列化
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-04-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 创建型模式之原型模式
    • 定义
      • 角色
        • java语言中实现克隆的两种方式
          • 浅克隆
            • 实例
            • 总结
          • 深度克隆
            • 条件
            • 为什么实现序列化
            • 实现
            • 总结
          • 总结
          相关产品与服务
          数据库
          云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档