顿搜
原型模式(Prototype)——23种设计模式之创建型模式
在软件系统中,有时候面临的产品类是动态变化的,而且这个产品类具有一定的等级结构。 这时如果用工厂模式,则与产品类等级结构平 行的工厂方法类也要随着这种变化而变化,显 然不大合适。
以一个已有的对象作为原型,通过它来创建新的对象。 在增加新的对象的时候,新对象的细节创建工作由自 己来负责,从而使新对象的创建过程与框架隔离开来
一、类图表示
原型模式涉及“客户端”(Client)角色、“抽象原型(Prototype)角色” 、“ 具体原型(Concrete Prototype)角色”及“原型管理器(PrototypeManager)角色” 。
- 客户端”(Client):让一个原型克隆自身从而创建一个新的对象。
- 抽象原型(Prototype):声明一个克隆自身的接口
- 具体原型(Concrete Prototype):实现一个克隆自身的接口
- 原型管理器(PrototypeManager):在登记形式原型模式中创建具体原型类对象,并记录每一个被创建的对象。
1、简单形式的原型模式
2、登记形式的原型模式
二、代码示例
1、简单形式的原型模式
//原型类
public class Prototype implements Cloneable {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public Object clone(){
try {
return super.clone();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}//原型子类
public class ConcretePrototype extends Prototype {
public ConcretePrototype(String name) {
setName(name);
}
}//客户端
public class Test {
public static void main(String[] args) {
Prototype pro = new ConcretePrototype("张三");
Prototype pro2 = (Prototype)pro.clone();
System.out.println(pro.getName());
System.out.println(pro2.getName());
}
}对于JAVA中克隆方法(clone),具体详情请参考JAVA语言中克隆CLONE方法详解,浅克隆与深克隆的研究
2、登记形式的原型模式
当一个系统中原型数目不固定时,使用一个原型管理器保持一个可用原型的注册表
//抽象原型角色
public interface Prototype{
public Prototype clone();
public String getName();
public void setName(String name);
}//具体原型角色1
public class ConcretePrototype1 implements Prototype {
private String name;
public Prototype clone(){
ConcretePrototype1 prototype = new ConcretePrototype1();
prototype.setName(this.name);
return prototype;
}
public String toString(){
return "Now in Prototype1 , name = " + this.name;
}
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
}//具体原型模式2
public class ConcretePrototype2 implements Prototype {
private String name;
public Prototype clone(){
ConcretePrototype2 prototype = new ConcretePrototype2();
prototype.setName(this.name);
return prototype;
}
public String toString(){
return "Now in Prototype2 , name = " + this.name;
}
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
}原型管理器角色保持一个聚集,作为对所有原型对象的登记,这个角色提供必要的方法,供外界增加新的原型对象和取得已经登记过的原型对象
//原型管理器角色
public class PrototypeManager {
/**
* 用来记录原型的编号和原型实例的对应关系
*/
private static Map<String,Prototype> map = new HashMap<String,Prototype>();
/**
* 私有化构造方法,避免外部创建实例
*/
private PrototypeManager(){}
/**
* 向原型管理器里面添加或是修改某个原型注册
* @param prototypeId 原型编号
* @param prototype 原型实例
*/
public synchronized static void setPrototype(String prototypeId , Prototype prototype){
map.put(prototypeId, prototype);
}
/**
* 从原型管理器里面删除某个原型注册
* @param prototypeId 原型编号
*/
public synchronized static void removePrototype(String prototypeId){
map.remove(prototypeId);
}
/**
* 获取某个原型编号对应的原型实例
* @param prototypeId 原型编号
* @return 原型编号对应的原型实例
* @throws Exception 如果原型编号对应的实例不存在,则抛出异常
*/
public synchronized static Prototype getPrototype(String prototypeId) throws Exception{
Prototype prototype = map.get(prototypeId);
if(prototype == null){
throw new Exception("您希望获取的原型还没有注册或已被销毁");
}
return prototype;
}
}//客户端角色
public class Client {
public static void main(String[]args){
try{
Prototype p1 = new ConcretePrototype1();
PrototypeManager.setPrototype("p1", p1);
//获取原型来创建对象
Prototype p3 = PrototypeManager.getPrototype("p1").clone();
p3.setName("张三");
System.out.println("第一个实例:" + p3);
//有人动态的切换了实现
Prototype p2 = new ConcretePrototype2();
PrototypeManager.setPrototype("p1", p2);
//重新获取原型来创建对象
Prototype p4 = PrototypeManager.getPrototype("p1").clone();
p4.setName("李四");
System.out.println("第二个实例:" + p4);
//有人注销了这个原型
PrototypeManager.removePrototype("p1");
//再次获取原型来创建对象
Prototype p5 = PrototypeManager.getPrototype("p1").clone();
p5.setName("王五");
System.out.println("第三个实例:" + p5);
}catch(Exception e){
e.printStackTrace();
}
}
}三、两种形式的比较
简单形式和登记形式各有所长。
1+ 当需要创建的原型对象数目较少而且比较固定的话, 可采用简单形式,原型对象的引用可由客户端自己保存
- 如果要创建的原型对象数目不固定的话,可采用登记形式,由原型管理器保存对原型对象的引用。在复制 一个对象之前,客户端可先查看管理员对象是否已有 一个满足要求的原型对象,如果有,则直接从管理员类取得这个对象引用;如果没有,客户端就需要自行 复制此原型对象。
四、与工厂方法模式的关系
对于产品结构可能会有经常性变化 的系统来说,采用工厂模式就不太方便
- 大量使用Composite和Decorator模式的设计通常从Prototype模式处获益
- Prototype与Abstract Factory往往是相互竞争的,但 是它们也可以一起使用。Abstract Factory可以存储 一个被克隆的原型的集合,并且返回产品对象
五、使用原型模式原因
原型模式能够提高效率
- 原型模式应用于很多软件中,如果每次创建一个对象,要花大量时间。
- 通过原型模式可 以大大提高对象的创建效率。
- 如果你有一个对象,有着复杂的状态。你希望得到 另一个拷贝,但是同时具有一模一样的状态,如果选择从头创建一个新的对象,你将不得不重新一个 一个地设臵这些状态。有的时候这很麻烦,有的时 候这不可能做到。这种时候,使用原型模式,调用 一个克隆方法,就变成非常好的办法。
六、优缺点分析
1、优点
对客户隐藏了具体的产品类,使客户无需改变即可使用与特定应 用相关的类。
- 原型模式允许动态地增加或减少产品类,具有给一个应用软件加载新功能的能力。
- 改变值以指定新对象,改变结构以指定新对象
- 减少子类的构造,用类动态配置应用
- 原型模式提供简化的创建结构。工厂方法常需要有一个与产品类 相同的等级结构,而原型模式不需要。
- 产品类不需要非得有任何事先确定的等级结构,因为原型模式适 用于任何的等级结构。
2、缺点
每个Prototype的子类都必须实现Clone操作。
- 配备克隆方法需要对类的功能进行通盘考虑
- 当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候,克隆比较麻烦
七、应用场景
资源优化场景,性能和安全要求的场景,一个对象多个修改者的场景
- 当一个系统应该独立于它的产品创建、构成和 表示时
- 当要实例化的类是在运行时刻指定时,例如动态装载
- 为了避免创建一个与产品类层次平行的工厂类 层次时
- 当一个类的实例只能有几个不同状态组合中的一种时
- 假设一个系统的产品类是动态加载的,而且产品类具 有一定的等级结构。

