(一)什么是原型模式
在正常的开发过程中,百分之99的对象实例都是通过new进行创建的,但是在实际的场景中,还是存在一些不通过new生成对象实例的应用场景。
比如:需要生成大量类似的对象实例时,如果都通过new去创建,性能并不好。
又比如:通过很复杂的方式生成了一个对象,现在要再创建出一个类似的对象出来。
上面的两种应用场景都可以通过“拷贝”这个动作来实现,这种根据一个已有的原型对象,拷贝生成新的对象的方式,就是设计模式中的原型模式(prototype)。
在Java中,可以通过Cloneable接口和clone方法很好地实现原型模式。
(二)原型模式的简单例子
首先通过一个简单的例子让大家快速了解原型模式。
过年的时候公司给每个人发了一份邮件,这个邮件里除了署名和入职年份不一样以外,其他的内容都是相同的。比如:张三,这是你入职的第一年,祝你新年快乐XXXXX;李四,这是你入职的第二年,祝你新年快乐XXXXX。
这里的每一份邮件都可以理解为一个对象,而所有的对象实例都是相似的对象实例,因此就可以实用原型模式来拷贝生成这些重复的对象。
首先创建一个邮件对象,需要实现Cloneable接口:
publicclassMailimplementsCloneable{ privateStringname; privateStringcontent; privateIntegeryears; Mail(Stringname,Stringcontent,Integeryears){ this.name=name; this.content=content; this.years=years; System.out.println("对象创建成功"); } privatevoidshowMail(){ System.out.println(name+",这是你入职的第"+years+"年,"+content); } protectedObjectclone() throwsCloneNotSupportedException { System.out.println("拷贝成功"); returnsuper.clone(); } publicvoidsetContent(Stringcontent) { this.content=content; } publicvoidsetName(Stringname) { this.name=name; } publicvoidsetYears(Integeryears) { this.years=years; } }
接下来在使用的时候先通过new创建一个基础对象实例,其他的均通过clone进行拷贝:
publicstaticvoidmain(String[] args) throwsCloneNotSupportedException { Mailmail=newMail("张三", "祝你新年快乐XXXXXX", 1); mail.showMail(); MailcloneMail= (Mail) mail.clone(); cloneMail.setName("李四"); cloneMail.setYears(2); mail.showMail(); }
这样一个简单的原型模式就实现了。
(三)基于管理器的原型模式
上面的例子可以增加对原型模式的理解,在实际开发中,更常用的是使用一个管理器去管理原型对象,可以在管理器类中通过一个HashMap保存原型对象。以水果店为例子。
首先定义了一个接口叫做Fruit,里面有两个方法:,shape方法用于展示水果的外观,createFruit方法用于通过克隆创建原型对象实例。
publicinterfaceFruitextendsCloneable{ voidshape(); ObjectcreateFruit() throwsException; }
接着定义管理器,管理器由HashMap进行管理,提供注册和创建实例的功能:
publicclassFruitManager { privateHashMap<String,Fruit>managerMap=newHashMap<>(); publicvoidregister(Stringname,Fruitfruit){ managerMap.put(name,fruit); } publicFruitcreate(Stringname) throwsException{ Fruitfruit=managerMap.get(name); return (Fruit) fruit.createFruit(); } }
下一步就是定义两个具体的对象,需要注意的是createFruit()方法使用了clone()进行拷贝:
publicclassAppleimplementsFruit{ publicvoidshape() { System.out.println("苹果是红色的圆形水果"); } publicObjectcreateFruit() throwsException { returnsuper.clone(); } } publicclassOrangeimplementsFruit{ publicvoidshape() { System.out.println("橘子是黄色的圆形水果"); } publicObjectcreateFruit() throwsException{ returnsuper.clone(); } }
最后写一个测试方法:
publicstaticvoidmain(String[] args) throwsException { FruitManagermanager=newFruitManager(); //注册到管理器中manager.register("apple",newApple()); manager.register("orange",newOrange()); //通过create方法不断拷贝新的对象Fruitapple=manager.create("apple"); apple.shape(); Fruitorange=manager.create("orange"); orange.shape(); }
(四)原型模式在源码中的应用
其实在很多地方都用到了原型模式,比如最常用的ArrayList,同样定义了clone方法供调用者进行拷贝。
抛去Cloneable相关的接口,一些对象之间的复制操作也是原型模式的实践,比如Spring里BeanUtils.copyProperties()这个方法,同样实现了根据一个已有的原型对象,拷贝生成新的对象。
(五)总结
本章内容主要通过两个例子讲解了原型模式的概念以及应用,我是鱼仔,我们下期再见!