设计模式-创建型之原型模式
来源:互联网 发布:数据选择器的工作原理 编辑:程序博客网 时间:2024/05/18 02:12
模式动机
在软件开发中,有时某些对象创建的过程比较复杂,而且需要频繁创建,那么我们可不可以直接Ctrl C再Ctrl V呢?答案是可以的,原型模式就是解决这类问题,它通过复制一个对象本身,从而克隆出多个与原型对象一模一样的对象出来。
模式定义
用原型实例指定创建对象的种类,然后通过复制这个原型对象创建出新的对象,无须知道创建的细节,这便是原型模式。
Java中的克隆
java中所有的类都继承与java.lang.Object,这个类中有一个本地方法clone(),可以将一个java对象复制一份,因此在java中可以使用这个方法来实现对象的克隆,java中的原型模式的实现也因此变得很简单。另外,要想使用clone(),必须实现Cloneable接口,表示这个java类是支持复制的,如果一个类没有实现这个接口,但却调用了clone()方法,那将会报出CloneNotSupportedException的异常。
模式结构
原型模式主要由以下两部分组成:
抽象原型类:如Prototype
具体原型类:如ConcretePrototypeA
代码实例
抽象原型类
public interface Prototype { public Object copy();}
具体原型类
public class Email implements Prototype,Cloneable{ private String title; @Override public Email copy() { try { return (Email) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; }}
客户端测试类
public class Client { public static void main(String[] args) { Email e1 = new Email(); e1.setTitle("我是邮件一"); Email e2 = e1.copy(); System.out.println(e2.getTitle()); }}
运行客户端测试类,输出为
我是邮件一
至此,一个最简单的原型模式已经完成。
深克隆和浅克隆
在上面的例子中,我们的原型对象Email非常简单,里面只有一个title属性,假如里面包含各种类型,甚至包含一个对象,此时又该如何克隆呢?此时,有两种不同的克隆方式,浅克隆和深克隆。
浅克隆也叫浅度拷贝或者浅度复制:只复制原对象里的8种基本类型及其封装类和String类型,对于对象类型,不会复制,新对象中仍然是原来的引用。
深克隆也叫深度拷贝或者深度复制:不仅复制对象里的8种基本类型及其封装类和String类型,同时也复制原对象类型,完全产生新对象。下面通过实例来说明:
- 浅克隆
public class Attachment { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; }}
public class Email implements Prototype,Cloneable{ private String title; private int intA; private Integer integerA; private Attachment attachment;//这里是另外的一个对象 @Override public Email copy() { try { return (Email) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public int getIntA() { return intA; } public void setIntA(int intA) { this.intA = intA; } public Integer getIntegerA() { return integerA; } public void setIntegerA(Integer integerA) { this.integerA = integerA; } public Attachment getAttachment() { return attachment; } public void setAttachment(Attachment attachment) { this.attachment = attachment; }}
public class Client { public static void main(String[] args) { Email e1 = new Email(); e1.setTitle("我是title"); e1.setIntA(1); e1.setIntegerA(new Integer(1)); Attachment attachment1 = new Attachment(); attachment1.setName("附件1"); e1.setAttachment(attachment1); Email e2 = e1.copy(); System.out.println(e1.getAttachment() == e2.getAttachment());//true,说明是同一个引用,并没有复制一个新的对象 e1.setTitle("title发生改变"); System.out.println(e1.getTitle());//title发生改变 System.out.println(e2.getTitle());//我是title //克隆对象e2的title并没有发生改变,说明String类型是复制了的 e1.setIntA(2); System.out.println(e1.getIntA());//2 System.out.println(e2.getIntA());//1 //克隆对象e2的intA并没有发生改变,说明int类型是复制了的 e1.setIntegerA(new Integer(2)); System.out.println(e1.getIntegerA());//2 System.out.println(e2.getIntegerA());//1 //克隆对象e2的integerA并没有发生改变,说明Integer类型是复制了的 e1.getAttachment().setName("attachment的name发生改变"); System.out.println(e1.getAttachment().getName());//attachment的name发生改变 System.out.println(e2.getAttachment().getName());//attachment的name发生改变 //e1修改了attachment的name,e2的attachment的name也跟着变了,说明attachment并没有被复制,e1的attachment跟e2的attachment仍然是同一个。说明对于对象类型,调用clone()方法,并不会被复制,这就是浅克隆 }}
- 深克隆
//这里跟上面略有不同,这里需要实现Serializable接口,让其可以序列化,因为克隆的时候需要写到流里面 public class Attachment implements Serializable{ private static final long serialVersionUID = 1L; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; }}
//这里也要实现Serializable接口public class Email implements Prototype,Serializable{ private static final long serialVersionUID = 1L; private String title; private int intA; private Integer integerA; private Attachment attachment; @Override public Email copy() { try { //这里的克隆方法跟浅克隆就完全不一样了,浅克隆是直接调用clone()方法即可,而深度克隆需要将对象写入流,然后再从流中取出来 //先把对象写入流 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); //然后把对象从流里取出来 byte b[] = bos.toByteArray(); ByteArrayInputStream bis = new ByteArrayInputStream(b); ObjectInputStream ois = new ObjectInputStream(bis); return (Email)ois.readObject(); } catch (Exception e) { e.printStackTrace(); } return null; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public int getIntA() { return intA; } public void setIntA(int intA) { this.intA = intA; } public Integer getIntegerA() { return integerA; } public void setIntegerA(Integer integerA) { this.integerA = integerA; } public Attachment getAttachment() { return attachment; } public void setAttachment(Attachment attachment) { this.attachment = attachment; }}
public class Client { public static void main(String[] args) { Email e1 = new Email(); e1.setTitle("我是title"); e1.setIntA(1); e1.setIntegerA(new Integer(1)); Attachment attachment1 = new Attachment(); attachment1.setName("附件1"); e1.setAttachment(attachment1); Email e2 = e1.copy(); System.out.println(e1.getAttachment() == e2.getAttachment());//false,说明不是同一个引用!!!而是一个全新的对象!!! e1.setTitle("title发生改变"); System.out.println(e1.getTitle());//title发生改变 System.out.println(e2.getTitle());//我是title //克隆对象e2的title并没有发生改变,说明String类型是复制了的 e1.setIntA(2); System.out.println(e1.getIntA());//2 System.out.println(e2.getIntA());//1 //克隆对象e2的intA并没有发生改变,说明int类型是复制了的 e1.setIntegerA(new Integer(2)); System.out.println(e1.getIntegerA());//2 System.out.println(e2.getIntegerA());//1 //克隆对象e2的integerA并没有发生改变,说明Integer类型是复制了的 e1.getAttachment().setName("attachment的name发生改变"); System.out.println(e1.getAttachment().getName());//attachment的name发生改变 System.out.println(e2.getAttachment().getName());//附件1 //e1修改了attachment的name,e2的attachment的name却没有随之改变,依然保持原来的值"附件1",说明attachment也被复制了,是一个全新的对象,这就是深克隆 }}
对比上面两种代码可以看出,浅克隆和深克隆主要有以下不同:
- 浅克隆不会复制对象类型的属性,深克隆要复制
- 实现方式不一样,浅克隆是调用clone(),深克隆是使用输入输出流;
- 浅克隆要实现Cloneable接口,而深克隆不用,但深克隆中原型对象以及里面所包含的对象,要实现Serializable接口
模式优点
- 当创建的对象比较复杂时,原型模式可以简化创建对象的过程,直接通过一个实例复制得到;
- 浅克隆是调用clone()方法,这是一个本地(native)方法,直接操作的内存中的二进制流,深度克隆也是通过输入输出流进行复制,性能上比通过new要好,所以当复制对象较大时,通过原型模式可以大大提升性能。
总结
主要讲了几点,一是java中的clone()方法,必须要实现Cloneable接口;二是深克隆和浅克隆,主要区别在于复制对象类型时,浅克隆不会复制,而深克隆会复制;三是模式的优点,简化创建过程,同时提高了性能。
- 设计模式-创建型之原型模式
- JAVA设计模式创建型模式之原型模式
- 设计模式之------创建型模式(五)-----原型模式
- (4)设计模式之原型模式(创建型模式)
- Java设计模式之创建型模式--原型模式
- 【设计模式】创建型模式-原型模式
- 设计模式 - 创建型模式 - 原型模式
- 设计模式->创建型模式->原型模式
- (创建模式)设计模式之Prototype(原型)
- 设计模式笔记--创建型模式之四--原型prototype
- java设计模式(创建型)之原型模式
- 设计模式之Prototype(原型模式)对象创建型
- 设计模式(创建型)之原型模式(Prototype Pattern)
- 创建型:设计模式之原型模式(五)
- java设计模式——创建型之原型模式
- 【设计模式】创建型模式之原型Prototype
- 设计模式之对象创建型 — prototype 原型模式
- 设计模式-创建型-原型
- Mcrypt PHP extension
- Appium源码分析(四)-swipe
- 百度ueditor编辑器代码高亮
- 链表
- C# 中的 delegate, Lambda 表达式 和 event
- 设计模式-创建型之原型模式
- Android自动化测试(UiAutomator)简要介绍
- 彻底删除jdk - java -version和环境变量设置无效的可能情况
- 虚函数及虚继承总结
- UIautomator输入中文
- poj2744Long Long Message【后缀数组】
- php 学习入门
- uva 10405 Longest Common Subsequence
- 以符合人类阅读的方式打印php数组