关键词:
Java设计模式-创建型设计模式-原型模式
从这一专栏开始将学习设计模式,上课学习和自己总结归纳的笔记将总结出来供大家参考。
参考书籍:《设计模式就该这样学》
其他文章:
文章目录
一、创建型设计模式
创建型模式对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离,对客户端代码需要调用对象的时候隐藏了类的实例化的创建细节。
其中包括:简单工厂模式(不在GoF23种设计模式中)、工厂方法模式、抽象工厂模式、建造者模式、原型模式、单例模式。
二、原型模式
1.原型模式定义
用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象。说大白话就是自己复制自己,通过原生对象复制出一个新的对象,这两个对象结构相同且相似,但不是通过new出来的(不调用构造函数)。
需要注意的是,原型对象自己不仅是个对象还是个工厂。并且通过克隆方式创建的对象是全新的对象,它们都是有自己的新的地址,通常对克隆模式所产生的新对象进行修改,是不会对原型对象造成任何影响的,每一个克隆对象都是相对独立的,通过不同的方式对克隆对象进行修改后,可以的到一系列相似但不完全相同的对象。
原型模式我们也称为克隆模式,即一个某个对象为原型克隆出来一个一模一样的对象,该对象的属性和原型对象一模一样。而且对于原型对象没有任何影响。原型模式的克隆方式有两种:浅克隆和深克隆。
在GoF23种设计模式中属于创建型设计模式:
其中包括:简单工厂模式(不在GoF23种设计模式中)、工厂方法模式、抽象工厂模式、建造者模式、原型模式、单例模式。
2.浅克隆和深克隆
浅克隆:复制对象时仅仅复制对象本身,包括基本属性,但该对象的属性引用其他对象时,该引用对象不会被复制,即拷贝出来的对象与被拷贝出来的对象中的属性引用的对象是同一个。
深克隆:被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深克隆把要复制的对象所引用的对象都复制了一遍。
3.原型模式的角色:
抽象原型类(Prototype):它是声明克隆方法的接口,是所有具体原型类的公共父类,它可以是抽象类也可以是接口,甚至还可以是具体实现类。
具体原型类(ConcretePrototype):它实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。
客户类(Client):让一个原型对象克隆自身从而创建一个新的对象,在客户类中只需要直接实例化或通过工厂方法等方式创建一个原型对象,再通过调用该对象的克隆方法即可得到多个相同的对象。由于客户类针对抽象原型类Prototype编程,因此用户可以根据需要选择具体原型类,系统具有较好的可扩展性,增加或更换具体原型类都很方便。
4.原型模式的特点
优点:
- 简化对象的创建过程,通过复制一个已有对象实例可以提高新实例的创建效率
- 扩展性好
- 提供了简化的创建结构,原型模式中的产品的复制是通过封装在原型类中的克隆方法实现的,无需专门的工厂类来创建产品
- 可以通过深克隆的方式保存对象的状态,使用原型模式将对象复制一份并其状态保存起来,以便在需要的时候使用,可辅助实现撤销操作
缺点:
- 需要为每一个类准备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有类进行改造时,需要修改原代码,违背了开闭原则
- 在实现深克隆时需要写较复杂的代码,而且当对象之间存在多重的嵌套引用时,为了实现深克隆,每一层对象对应的类必须支持深克隆,实现起来较繁琐.
适用环境:
- 创建新对象成本较大,新对象可以通过复制已有对象来获得,如果是相似对象,则可以对其成员变量修改
- 系统要保存对象的状态,而对象的状态变化很小
- 需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更方便
5.原型模式的类图
6.原型模式的代码实现
6.1Java中如何实现对象的克隆
在Java中可以直接使用Object提供的clone方法实现对象的克隆。但需要注意的是,能够实现克隆的java类必须实现一个标识接口:Cloneable,标识这个Java类支持复制。如果一个类没有实现这个接口而调用了clone方法,java编译器将抛出一个CloneNotSupportedException异常。
Clone方法的条件:
1.对于任何对象x,有x.clone()!=x
2.对于任何对象x,有x.clone().getClass()==x.getClass()
3.如果x的equals方法定义恰当,那么x.clone().equals(x)应当成立
6.2具体案例:
类图如下:
6.2.1浅克隆具体代码实现:
1.附件类Attachment
/**
* 附件类
*/
public class Attachment
private String name;
public String getName()
return name;
public void setName(String name)
this.name = name;
public void download()
System.out.println("下载附件,文件名为:" + this.name);
2.原型类WeeklyLog
/**
* WeeklyLog原型类
* @author WxrStart
* @create 2022-04-12 16:43
*/
public class WeeklyLog implements Cloneable
private Attachment attachment;
private String name;
private Date date;
private String content;
public Attachment getAttachment()
return attachment;
public void setAttachment(Attachment attachment)
this.attachment = attachment;
public String getName()
return name;
public void setName(String name)
this.name = name;
public Date getDate()
return date;
public void setDate(Date date)
this.date = date;
public String getContent()
return content;
public void setContent(String content)
this.content = content;
//使用clone()方法来实现浅克隆
public WeeklyLog clone()
Object o = null;
try
o = super.clone();
return (WeeklyLog) o;
catch (CloneNotSupportedException e)
System.out.println("不支持复制");
e.printStackTrace();
return null;
3.客户端测试类
**
* 客户端测试类
* @author WxrStart
* @create 2022-04-12 16:45
*/
public class Client
public static void main(String[] args)
WeeklyLog preLog,newLog;
Attachment attachment = new Attachment();
attachment.setName("week 1 attachment");
preLog = new WeeklyLog();
preLog.setContent("周报week1的内容");
preLog.setDate(new Date(System.currentTimeMillis()-7*24*3600*1000));
preLog.setName("周报week1");
preLog.setAttachment(attachment);
//浅克隆新的对象newLog
newLog = preLog.clone();
//这里就是判断通过克隆方式创建的对象是否是全新的对象,false为全新对象
System.out.println("周报地址(克隆的对象地址)是否相同?" + (preLog == newLog));
System.out.println("*********************************************************");
//通过下面几行代码,证实浅拷贝的特点
System.out.println("preLog和newLog的未更改之前的Attachment的Name");
System.out.println("preLog的Attachment的Name: "+preLog.getAttachment().getName());
System.out.println("newLog的Attachment的Name: "+newLog.getAttachment().getName());
//给newLog设置新的Attachment对象的属性Name
newLog.getAttachment().setName("week 2 attachment");
System.out.println("preLog和newLog的更改之后的Attachment的Name");
System.out.println("preLog的Attachment的Name: "+preLog.getAttachment().getName());
System.out.println("newLog的Attachment的Name: "+newLog.getAttachment().getName());
System.out.println("*********************************************************");
System.out.println("preLog和newLog的未更改之前的date属性");
System.out.println("preLog的date: "+preLog.getDate());
System.out.println("newLog的date: "+newLog.getDate());
//给newLog设置新的Attachment对象的属性Name
newLog.setDate(new Date());
System.out.println("preLog和newLog的更改之后的date属性");
System.out.println("preLog的date: "+preLog.getDate());
System.out.println("newLog的date: "+newLog.getDate());
测试结果:
很显然,浅拷贝成功了
1.被preLog克隆出来的newLog对象地址不一样。
2.复制对象时仅仅复制对象本身,包括基本属性(Date),但该对象的属性引用其他对象时(Attachment),该引用对象不会被复制,即拷贝出来的对象与被拷贝出来的对象中的属性引用的对象是同一个。所以Date修改后preLog和newLog的Date值不一样,但是Attachment的name值修改后会一样。
6.2.2深克隆具体代码实现:
很显然,在clone方法的时候有问题了,所以我们需要重新修改clone方法,但是如果还用clone的话很难做到,所以我们选择使用Java自带的序列化机制
1.附件类Attachment
/**
* 附件类
*/
public class Attachment implements Serializable
private String name;
public String getName()
return name;
public void setName(String name)
this.name = name;
public void download()
System.out.println("下载附件,文件名为:" + this.name);
2.原型类WeeklyLog
/**
* WeeklyLog原型类
* @author WxrStart
* @create 2022-04-12 16:43
*/
public class WeeklyLog implements Serializable
private Attachment attachment;
private String name;
private Date date;
private String content;
public Attachment getAttachment()
return attachment;
public void setAttachment(Attachment attachment)
this.attachment = attachment;
public String getName()
return name;
public void setName(String name)
this.name = name;
public Date getDate()
return date;
public void setDate(Date date)
this.date = date;
public String getContent()
return content;
public void setContent(String content)
this.content = content;
//使用序列化技术实现深克隆
public WeeklyLog deepClone() throws Exception
//1.创建一个字节数组输出流
ByteArrayOutputStream bos=new ByteArrayOutputStream();
//2.用对象输入流包装
ObjectOutputStream oos=new ObjectOutputStream(bos);
//3.将调用该方法的对象写出
oos.writeObject(this);
//4.创建一个字节数组输入流,读取刚刚输出到字节数组的对象this
ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
//5.用对象输入流包装
ObjectInputStream ois=new ObjectInputStream(bis);
//6.返回深克隆对象
return (WeeklyLog)ois.readObject;
3.客户端测试类
**
* 客户端测试类
* @author WxrStart
* @create 2022-04-12 16:45
*/
public class Client
public static void main(String[] args) throws Exception
WeeklyLog preLog,newLog;
Attachment attachment = new Attachment();
attachment.setName("week 1 attachment");
preLog = new WeeklyLog();
preLog.setContent("周报week1的内容");
preLog.setDate(new Date(System.currentTimeMillis()-7*24*3600*1000));
preLog.setName("周报week1");
preLog.setAttachment(attachment);
//浅克隆新的对象newLog
newLog = preLog.deepClone();
//这里就是判断通过克隆方式创建的对象是否是全新的对象,false为全新对象
System.out.println("周报地址(克隆的对象地址)是否相同?" + (preLog == newLog));
System.out.println("*********************************************************");
//通过下面几行代码,证实浅拷贝的特点
System.out.println("preLog和newLog的未更改之前的Attachment的Name");
System.out.println("preLog的Attachment的Name: "+preLog.getAttachment().getName());
System.out.println("newLog的Attachment的Name: "+newLog.getAttachment().getName());
//给newLog设置新的Attachment对象的属性Name
newLog.getAttachment().setName("week 2 attachment");
System.out.println("preLog和newLog的更改之后的Attachment的Name");
System.out.println("preLog的Attachment的Name: "+preLog.getAttachment().getName());
System.out.println("newLog的Attachment的Name: "+newLog.getAttachment().getName());
System.out.println("*********************************************************");
System.out.println("preLog和newLog的未更改之前的date属性");
System.out.println("preLog的date: "+preLog.getDate());
System.out.println("newLog的date: "+newLog.getDate());
//给newLog设置新的Attachment对象的属性Name
newLog.setDate(new Date());
System.out.println("preLog和newLog的更改之后的date属性");
System.out.println("preLog的date: "+preLog.getDate());
System.out.println("newLog的date: "+newLog.getDate());
测试结果:
很显然,深拷贝成功了
1.被preLog克隆出来的newLog对象地址不一样。
2.复制对象时不光复制对象本身,包括基本属性(Date)和该对象的属性引用其他对象 (Attachment),该引用对象也会被复制,即拷贝出来的对象与被拷贝出来的对象中的属性引用的对象不是同一个。所以Date修改后preLog和newLog的Date值不一样,但是Attachment的name值修改后也会不一样。
从零开始学习java设计模式|创建型模式篇:原型模式(代码片段)
在本讲,我们来学习一下创建型模式里面的第四个设计模式,即原型模式。概述原型模式就是指用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。这段话读起来有点绕,... 查看详情
创建型设计模式-原型模式(代码片段)
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。抽象原型类:规定了具体原型对象必须实现的clone()方法。JDK已经提供了java.lang.Cloneable抽象原型类。具体原型:实现抽象原... 查看详情
创建型设计模式-原型模式(代码片段)
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。抽象原型类:规定了具体原型对象必须实现的clone()方法。JDK已经提供了java.lang.Cloneable抽象原型类。具体原型:实现抽象原... 查看详情
创建型设计模式之原型模式(代码片段)
原型模式概述优缺点优点:1.java自带的原型模式是基于内存二进制流的拷贝,比直接new一个对象性能上提升许多2.使用原型模式将对象复制一份并将其状态保存起来,简化创建对象的过程,以便在需要的时候使用缺点:1.需要为... 查看详情
设计模式_创建型模式_原型模式(代码片段)
转载自:http://blog.csdn.net/lovelion 作者:刘伟 原型模式(Prototype Pattern):使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。 原型模式的工作原理很简单:将... 查看详情
设计模式-创建型模式_原型模式(代码片段)
文章目录创建型模式概述Case场景模拟⼯程BadImplBetterImpl(原型模式重构代码)小结创建型模式创建型模式提供创建对象的机制,能够提升已有代码的灵活性和可复⽤性。类型实现要点工厂方法定义⼀个创建对象的接⼝... 查看详情
设计模式-创建型模式_原型模式(代码片段)
文章目录创建型模式概述Case场景模拟⼯程BadImplBetterImpl(原型模式重构代码)小结创建型模式创建型模式提供创建对象的机制,能够提升已有代码的灵活性和可复⽤性。类型实现要点工厂方法定义⼀个创建对象的接⼝... 查看详情
创建型模式:原型模式(代码片段)
...的种类,并且通过拷贝这些原型创建新的对象。(来自《设计模式之禅》)又到了一个系列的最后一篇文章了,今天是创建型模式的最后一篇。什么是创建型模式呢?创建型模式是对类的实例化过程进行抽象,使对象的创建和使... 查看详情
创建型设计模式
一、单例模式二、工厂模式三、原型模式Java中Object类是所有类的超类,Object类提供了一个clone()方法,该方法可以将一个Java对象复制一份,但是需要实现clone的Java类必须实现一个接口Cloneable,该接口表示该类能够复制且具有复制... 查看详情
设计模式创建型原型模式(代码片段)
一.概述1.1用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。1.2结构原型模式包含如下角色:抽象原型类:规定了具体原型对象必须实现的的clone()方法。具体原型类:实... 查看详情
设计模式创建型原型模式(代码片段)
一.概述1.1用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。1.2结构原型模式包含如下角色:抽象原型类:规定了具体原型对象必须实现的的clone()方法。具体原型类:实... 查看详情
创建型设计-原型模式(代码片段)
...,就不重复造轮子哈。 原型模式是一种创建型设计模式,它通过复制一个已经存在的实例来返回新的实例,而不是新建实例.被复制的实例就是我们所称的原型,这个原型是可定制的。原型模式多用于创建复杂的或者 查看详情
扎实基础_设计模式_创建型_原型模式(代码片段)
创建型模式就是前面大佬总结出 对象的创建如何合理利用 最后得出来的一些解决方案比如现在有一个学生对象,我们实例化它的时候要两秒钟(前面代码是类,后面代码放在 staticvoidMain(string[]args)里面执行)publicclassStud... 查看详情
原型模式(创建型模式)(代码片段)
...的游戏设施建造系统相对变化较慢,本例中只有一个Build的创建方法,而Build内部的方法实现,该实现依赖与各种具体的实现,而这些实现变化的非常频繁,现在虽然只有现代风格和古典风格的房屋和道路的构建,而将来可能会卡通风格... 查看详情
设计模式原型模式(创建型)
定义:通过拷贝一个已经存在的实例来返回新的实例,而不是新建实例。被拷贝的实例就称为原型。类图原型类实现思路 (1)实现Cloneable接口。(在Java虚拟机中,只有实现了这个接口的类才可以被拷贝。) (2)重写Obj... 查看详情
designpattern-原型模式创建型(代码片段)
...动👋一、原型模式介绍原型模型(Prototype)是一种对象创建型模式,使用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象,主要用于创建重复的对象,同时又能保证性能。工作原理是将一个原... 查看详情
设计模式-创建型模式_原型模式(代码片段)
文章目录创建型模式概述Case场景模拟⼯程BadImplBetterImpl(原型模式重构代码)小结创建型模式创建型模式提供创建对象的机制,能够提升已有代码的灵活性和可复⽤性。类型实现要点工厂方法定义⼀个创建对象的接⼝... 查看详情
java设计模式之原型模式(代码片段)
...f0c;并且通过复制这些原型创建新的对象,属于创建型设计模式原型模式的核心在于复制原型对象。以系统中已存在的一个对象为原型,直接基于内存二进制流进行复制,不需要在经历耗时的对象初始化过程(不调... 查看详情