对于某些岗位来讲,工做周报的内容会大同小异,若是用户每次都须要从空白的周报进行输入无疑会浪费用户不少的时间,若是周报可以按照用户的自定义来生成模板,或者从已有模板修改小部分获得新模板,这样用户的输入效率会大大提升。原型模式正是为解决这类问题而生。java
原型模式:使用原型实例指定建立对象的种类,而且经过克隆这些原型建立新的对象。apache
原型模式是一种对象建立型模式。编程
原型模式的工做原理很简单,将一个原型对象传给那个要发动建立的对象,这个要发动建立的对象经过请求原型对象克隆本身来实现建立过程。原型模式是一种另类的建立型模式,建立克隆对象的工厂就是原型类自身,工厂方法由克隆方法实现。数组
经过克隆方法建立的对象是全新的对象,它们在内存中拥有新的地址,一般对克隆多产生的对象进行的修改不会对原型对象形成任何的影响,每个克隆的对象都是相互独立的,经过不一样的方式对克隆对象进行修改以后,能够获得一系列类似但不彻底相同的对象。微信
Prototype
(抽象原型类):声明克隆方法的接口,是全部具体原型类的公共父类,能够是抽象类也能够是接口,还能是具体实现类ConcretePrototypr
(具体原型类):实如今抽象原型类中声明的克隆方法,在克隆方法中返回本身的一个克隆对象Client
(客户类):让一个原型对象克隆自身从而建立一个新的对象,在客户类中只须要直接实例化或经过工厂方法等方式建立一个原型对象,再经过调用该对象的克隆方法便可获得多个相同的对象。clone
的方法clone
clone
方法获取多个对象这里定义为接口:网络
interface Prototype { Prototype clone(); String getAttr(); void setAttr(String attr); }
实现抽象原型接口,核心在于如何实现clone
,在Java中clone
一般有两种实现方式:ide
clone()
方法通用的克隆实现方法是在具体原型类的克隆方法中实例化一个与自身类型相同的对象并将其返回,并将相关的参数传入新建立的对象中,保证成员变量相同。函数
代码以下:工具
class ConcretePrototype implements Prototype { private String attr; @Override public String getAttr() { return this.attr; } @Override public void setAttr(String attr) { this.attr = attr; } @Override public Prototype clone() { Prototype = new ConcretePrototype(); prototype.setAttr(attr); return prototype; } }
clone
java.lang.Object
提供了一个clone()
,能够将一个Java对象克隆一份,利用clone()
能够直接将对象克隆一份,可是必须实现Cloneable
接口,不然clone()
时会抛出CloneNotSupportedException
。测试
代码以下:
class ConcretePrototype implements Prototype,Cloneable { private String attr; public String getAttr() { return this.attr; } public void setAttr(String attr) { this.attr = attr; } public Prototype clone() { Object object = null; try { object = super.clone(); } catch (Exception e) { e.printStacktrace(); } return (Prototype)object; } }
通常而言,Java中的clone()
知足:
x
都有x.clone() != x
,也就是克隆的对象与原型对象不是同一个对象x
都有x.clone().getClass() == x.getClass()
,即克隆对象与原型对象的类型同样x
的equals()
定义恰当,那么x.clone().equals(x)
应该成立具体实现步骤以下:
clone()
,并声明为public
clone()
中调用super.clone()
Cloneable
接口客户类针对抽象原型类编程,经过实例化获取具体原型后,调用其中的clone
进行克隆:
public class Test { public static void main(String[] args) { Prototype prototype1 = new ConcretePrototype(); prototype1.setAttr("test"); Prototype prototype2 = prototype1.clone(); System.out.println(prototype1.getAttr() == prototype2.getAttr()); System.out.println(prototype1 == prototype2); } }
开发一个工做周报系统,工做周报的内容都大同小异,只有一些小地方存在差别,可是系统每次默认建立的都是空白报表,用户不断复制粘贴来填写重复内容。使用原型模式对其进行优化,快速建立相同或相似的工做周报。
设计以下:
Object
)WeeklyLog
代码以下:
public class Test { public static void main(String[] args) { WeeklyLog weeklyLog1 = new WeeklyLog(); weeklyLog1.setContent("content"); weeklyLog1.setName("Weekly log 1"); weeklyLog1.setDateTime(LocalDateTime.now()); System.out.println(weeklyLog1.getName()); System.out.println(weeklyLog1.getContent()); System.out.println(weeklyLog1.getDateTime()); WeeklyLog weeklyLog2 = weeklyLog1.clone(); weeklyLog2.setName("Weekly log 2"); System.out.println(weeklyLog2.getName()); System.out.println(weeklyLog2.getContent()); System.out.println(weeklyLog2.getDateTime()); } } class WeeklyLog implements Cloneable { private String name; private LocalDateTime dateTime; private String content; //getter and setter //... public WeeklyLog clone() { Object obj = null; try { obj = super.clone(); } catch(Exception e) { e.printStackTrace(); } return (WeeklyLog)obj; } }
通常来讲,工做周报可能会携带附件,使用上面的原型模式来进行工做周报的复制没有问题,可是附件(通常是另外一个类)不会进行复制。这是由于浅克隆与深克隆的缘由,下面具体来看一下。
在浅克隆中,若是原型对象的成员变量是值类型,将复制一份给克隆对象。(在Java中)值类型包括:
int
double
byte
boolean
char
float
long
short
也就是这些类型的值都会完整复制一份给克隆对象,对于引用类型,则将引用对象的地址复制一份给克隆对象。(在Java中)引用类型就是除了基本类型以外的全部类型,常见的有:
对于引用类型,原型对象与克隆对象指向相同的内存地址,也就是其实并无被复制,而是共享一份地址相同的值。
在Java中能够经过Object
的clone()
实现浅克隆,也就是上面例子的作法。
在深克隆中,不管变量是值类型仍是引用类型都会完整复制一份给克隆对象。
在Java中实现深克隆能够经过序列化等方式实现。序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个复制品,而原对象仍然存在于内存中。想要进行序列化必须实现Serializable
接口。
代码以下:
public class Test { public static void main(String[] args) { WeeklyLog weeklyLog1 = new WeeklyLog(); WeeklyLog weeklyLog2 = null; Attachement attachement = new Attachement(); weeklyLog1.setAttachement(attachement); try { weeklyLog2 = weeklyLog1.deepClone(); } catch(Exception e) { e.printStackTrace(); } System.out.println(weeklyLog1 == weeklyLog2); System.out.println(weeklyLog1.getAttachement() == weeklyLog2.getAttachement()); } } class Attachement implements Serializable { private String name; //getter and setter //... } class WeeklyLog implements Serializable { private String name; private LocalDateTime dateTime; private String content; private Attachement attachement; //getter and setter //... public WeeklyLog deepClone() throws IOException , ClassNotFoundException , OptionalDataException { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(this); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); return (WeeklyLog)objectInputStream.readObject(); } }
固然除了使用ByteArrayOutput/InputStream
以及ObjectInput/OutputStream
外,还能够利用如下工具类进行深克隆:
org.apache.commons.lang3.SerializationUtils.clone()
:须要实现Serializable
接口Gson
:无需实现Serializable
接口,toJson()+fromJson()
Jackson
:也是无需实现Serializable
接口,readValue()+writeValueAsString()
等原型管理器是将多个原型对象存储在一个集合中供客户端使用的专门负责克隆对象的工厂,其中定义了一个集合用于存储原型对象,若是须要某个原型对象的克隆,能够经过复制集合中对应的原型对象来获取。在原型管理器中针对抽象原型类进行编程。
结构图以下:
平常办公中会有许多公文须要建立,例如《可行性分析报告》,《立项建议书》,《软件需求规格说明书》,《项目进展报告》等,为了提升工做效率须要为各种公文建立模板,用户能够经过这些模板快速建立新的公文,这些公文模板进行统一的管理,系统根据用户的请求的不一样生成不一样的新公文。
首先是抽象原型以及具体原型的代码:
interface OfficialDocument extends Cloneable { OfficialDocument clone(); void display(); } //可行性分析报告 class FAR implements OfficialDocument { public OfficialDocument clone() { OfficialDocument far = null; try { far = (OfficialDocument)super.clone(); } catch(Exception e) { e.printStackTrace(); } return far; } public void display() { System.out.println("可行性分析报告"); } } //软件需求规格说明书 class SRS implements OfficialDocument { public OfficialDocument clone() { OfficialDocument srs = null; try { srs = (OfficialDocument)super.clone(); } catch(Exception e) { e.printStackTrace(); } return srs; } public void display() { System.out.println("软件需求规格说明书"); } }
接着是原型管理器的代码,使用枚举单例实现:
enum PrototypeManager { INSTANCE; private Hashtable<String,OfficialDocument> hashtable = new Hashtable<>(); private PrototypeManager() { add("far",new FAR()); add("srs",new SRS()); } public void add(String key,OfficialDocument document) { hashtable.put(key, document); } public OfficialDocument get(String key) { return ((OfficialDocument)hashtable.get(key)).clone(); } }
测试代码:
public class Test { public static void main(String[] args) { PrototypeManager manager = PrototypeManager.INSTANCE; OfficialDocument document1,document2,document3,document4; document1 = manager.get("far"); document1.display(); document2 = manager.get("far"); document2.display(); System.out.println(document1 == document2); document3 = manager.get("srs"); document3.display(); document4 = manager.get("srs"); document4.display(); System.out.println(document3 == document4); } }
若是以为文章好看,欢迎点赞。
同时欢迎关注微信公众号:氷泠之路。