TypechoJoeTheme

IT技术分享

统计

原型模式(Prototype)——23种设计模式之创建型模式

2016-03-29
/
0 评论
/
640 阅读
/
正在检测是否收录...
03/29

在软件系统中,有时候面临的产品类是动态变化的,而且这个产品类具有一定的等级结构。 这时如果用工厂模式,则与产品类等级结构平 行的工厂方法类也要随着这种变化而变化,显 然不大合适。

以一个已有的对象作为原型,通过它来创建新的对象。 在增加新的对象的时候,新对象的细节创建工作由自 己来负责,从而使新对象的创建过程与框架隔离开来

一、类图表示

原型模式涉及“客户端”(Client)角色、“抽象原型(Prototype)角色” 、“ 具体原型(Concrete Prototype)角色”及“原型管理器(PrototypeManager)角色” 。

  • 客户端”(Client):让一个原型克隆自身从而创建一个新的对象。
  • 抽象原型(Prototype):声明一个克隆自身的接口
  • 具体原型(Concrete Prototype):实现一个克隆自身的接口
  • 原型管理器(PrototypeManager):在登记形式原型模式中创建具体原型类对象,并记录每一个被创建的对象。

1、简单形式的原型模式

java-prototype-model-1

2、登记形式的原型模式

java-prototype-model-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操作。

  • 配备克隆方法需要对类的功能进行通盘考虑
  • 当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候,克隆比较麻烦

七、应用场景

资源优化场景,性能和安全要求的场景,一个对象多个修改者的场景

  • 当一个系统应该独立于它的产品创建、构成和 表示时
  • 当要实例化的类是在运行时刻指定时,例如动态装载
  • 为了避免创建一个与产品类层次平行的工厂类 层次时
  • 当一个类的实例只能有几个不同状态组合中的一种时
  • 假设一个系统的产品类是动态加载的,而且产品类具 有一定的等级结构。
朗读
赞 · 0
版权属于:

IT技术分享

本文链接:

https://idunso.com/archives/1795/(转载时请注明本文出处及文章链接)