码迷,mamicode.com
首页 > 其他好文 > 详细

设计模式(创建型)之原型模式(Prototype Pattern)

时间:2015-04-28 22:51:35      阅读:265      评论:0      收藏:0      [点我收藏+]

标签:设计模式   pattern   java   原型模式   

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN。因为CSDN也支持MarkDown语法了,牛逼啊!

概述

原型模式是一种创建型设计模式,它通过复制一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的原型,这个原型是可定制的。原型模式多用于创建复杂的或者耗时的实例, 因为这种情况下,复制一个已经存在的实例可以使程序运行更高效,或者创建值相等,只是命名不一样的同类数据。

原型模式要求对象实现一个可以“克隆”自身的接口,这样就可以通过复制一个实例对象本身来创建一个新的实例。这样一来,通过原型实例创建新的对象,就不再需要关心这个实例本身的类型,只要实现了克隆自身的方法,就可以通过这个方法来获取新的对象,而无须再去通过new来创建。

核心

概念: 使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。

表现形式分类:

  • 简单形式
  • 登记形式

两种表现形式仅仅是原型模式的实现不一样。

重点:

简单形式的原型模式结构重要核心模块:

(Client)客户角色

客户类提出创建对象的请求。

(Prototype)抽象原型角色

这是一个抽象角色,通常由一个Java接口或Java抽象类实现。此角色给出所有的具体原型类所需的接口。

(Concrete Prototype)具体原型角色

被复制的对象,此角色需要实现抽象的原型角色所要求的接口。

(PrototypeManager)原型管理器角色

该角色的作用是创建具体原型类的对象,并记录每一个被创建的对象。

登记形式的原型模式结构重要核心模块:

(Client)客户角色

客户类提出创建对象的请求。

(Prototype)抽象原型角色

这是一个抽象角色,通常由一个Java接口或Java抽象类实现。此角色给出所有的具体原型类所需的接口。

(Concrete Prototype)具体原型角色

被复制的对象,此角色需要实现抽象的原型角色所要求的接口。

(PrototypeManager)原型管理器角色

该角色的作用是创建具体原型类的对象,并记录每一个被创建的对象。

使用场景

资源优化场景,类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。

性能和安全要求的场景,通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。

一个对象多个修改者的场景,一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。

在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与Java融为浑然一体,大家可以信手捏来。

程序猿实例

简单形式的原型模式 的基础实例,如下代码严格按照上面简单形式的原型模式的三要素编写实现,是一个基本的结构表示而已。

package yanbober.github.io;

//抽象原型角色
interface IPrototype {
    Object clones();
}
//具体原型角色
class ConcretePrototypeLowImpl implements IPrototype {
    @Override
    public Object clones() {
        IPrototype iPrototype = new ConcretePrototypeLowImpl();
        return iPrototype;
    }
}

class ConcretePrototypeHighImpl implements IPrototype {
    @Override
    public Object clones() {
        IPrototype iPrototype = new ConcretePrototypeHighImpl();
        return iPrototype;
    }
}
//客户端角色
public class Main {
    public static void main(String[] args) {
        IPrototype iPrototype = new ConcretePrototypeLowImpl();
        for (int index=0; index<20; index++) {
            ConcretePrototypeLowImpl clone = (ConcretePrototypeLowImpl) iPrototype.clones();
        }

        iPrototype = new ConcretePrototypeHighImpl();
        for (int index=0; index<20; index++) {
            ConcretePrototypeHighImpl clone = (ConcretePrototypeHighImpl) iPrototype.clones();
        }
    }
}


登记形式的原型模式 的基础实例,如下代码严格按照上面登记形式的原型模式的四要素编写实现,是一个基本的结构表示而已。

package yanbober.github.io;

import java.util.HashMap;
import java.util.Map;

//抽象原型角色
interface IPrototype {
    Object clones();

    void setTemp(String temp);
    String getTemp();

    void print();
}
//具体原型角色
class ConcretePrototypeLowImpl implements IPrototype {
    private String mTemp;

    @Override
    public Object clones() {
        IPrototype iPrototype = new ConcretePrototypeLowImpl();
        iPrototype.setTemp(mTemp);
        return iPrototype;
    }

    @Override
    public void setTemp(String temp) {
        this.mTemp = temp;
    }

    @Override
    public String getTemp() {
        return this.mTemp;
    }

    @Override
    public void print() {
        System.out.println("ConcretePrototypeLowImpl#"+this.hashCode()+"# temp="+this.mTemp);
    }
}

class ConcretePrototypeHighImpl implements IPrototype {
    private String mTemp;

    @Override
    public Object clones() {
        IPrototype iPrototype = new ConcretePrototypeHighImpl();
        iPrototype.setTemp(mTemp);
        return iPrototype;
    }

    @Override
    public void setTemp(String temp) {
        this.mTemp = temp;
    }

    @Override
    public String getTemp() {
        return this.mTemp;
    }

    @Override
    public void print() {
        System.out.println("ConcretePrototypeHighImpl#"+this.hashCode()+"# temp="+this.mTemp);
    }
}
//原型管理器角色
class PrototypeManager {
    private static Map<String, IPrototype> keymap = new HashMap<>();

    private PrototypeManager() {
        //null
    }

    public synchronized static void setPrototype(String key, IPrototype value) {
        keymap.put(key, value);
    }

    public synchronized static IPrototype getPrototype(String key) {
        IPrototype iPrototype = keymap.get(key);

        if (iPrototype == null) {
            throw new IllegalArgumentException("Can‘t find your key in the manager map!");
        }
        return iPrototype;
    }

    public synchronized static void removePrototype(String key) {
        keymap.remove(key);
    }
}
//客户端角色
public class Main {
    public static void main(String[] args) {
        IPrototype iPrototype = new ConcretePrototypeLowImpl();
        iPrototype.print();
        PrototypeManager.setPrototype("iPrototype", iPrototype);
        IPrototype clone = (IPrototype) PrototypeManager.getPrototype("iPrototype").clones();
        clone.setTemp("clone step 1!");
        clone.print();

        iPrototype = new ConcretePrototypeHighImpl();
        iPrototype.print();
        PrototypeManager.setPrototype("iPrototype", iPrototype);
        clone = (IPrototype) PrototypeManager.getPrototype("iPrototype").clones();
        clone.setTemp("clone step 2!");
        clone.print();

        PrototypeManager.removePrototype("iPrototype");
    }
}

运行结果如下:
ConcretePrototypeLowImpl#1163157884# temp=null
ConcretePrototypeLowImpl#1956725890# temp=clone step 1!
ConcretePrototypeHighImpl#356573597# temp=null
ConcretePrototypeHighImpl#1735600054# temp=clone step 2!

代码反思: 简单形式和登记形式的原型模式各有其长处和短处。如果需要创建的原型对象数目较少而且比较固定的话,可以采取第一种形式。在这种情况下,原型对象的引用可以由客户端自己保存。如果要创建的原型对象数目不固定的话,可以采取第二种形式。在这种情况下,客户端不保存对原型对象的引用,这个任务被交给管理员对象。在复制一个原型对象之前,客户端可以查看管理员对象是否已经有一个满足要求的原型对象。如果有,可以直接从管理员类取得这个对象引用;如果没有,客户端就需要自行复制此原型对象。

Java中提供的原型模式

Object类的protected Object clone()方法

Java的Object类提供protected Object clone()方法对对象进行复制,子类当然也可以把这个方法置换掉,提供满足自己需要的复制方法。对象的复制有一个基本问题,就是对象通常都有对其他的对象的引用。当使用Object类的clone()方法来复制一个对象时,此对象对其他对象的引用也同时会被复制一份。

Cloneable接口

Java语言提供的Cloneable接口只起一个作用,就是在运行时期通知Java虚拟机可以安全地在这个类上使用clone()方法。通过调用这个clone()方法可以得到一个对象的复制。由于Object类本身并不实现Cloneable接口,因此如果所考虑的类没有实现Cloneable接口时,调用clone()方法会抛出CloneNotSupportedException异常。

Java中克隆需要满足的基本条件

clone()方法将对象复制了一份并返还给调用者,所以clone()方法需要满足以下的描述:

  • 对任何的对象OBJ,都有:OBJ.clone()!=OBJ。换言之,克隆对象与原对象不是同一个对象。

  • 对任何的对象OBJ,都有:OBJ.clone().getClass() == OBJ.getClass(),换言之,克隆对象与原对象的类型一样。

  • 如果对象OBJ的equals()方法定义其恰当的话,那么OBJ.clone().equals(OBJ)应当成立的。

在JAVA语言的API中,凡是提供了clone()方法的类,都满足上面的这些条件。JAVA语言的设计师在设计自己的clone()方法时,也应当遵守着三个条件。一般来说,上面的三个条件中的前两个是必需的,而第三个是可选的。

Java的浅克隆和深克隆

无论你是自己实现克隆方法,还是采用Java提供的克隆方法,都存在一个浅度克隆和深度克隆的问题。

浅度克隆

只负责克隆按值传递的数据(比如基本数据类型,还有一个特殊的String类型),而不复制它所引用的对象,换言之,所有的对其他对象的引用都仍然指向原来的对象。

深度克隆

除了浅度克隆要克隆的值外,还负责克隆引用类型的数据。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深度克隆把要复制的对象所引用的对象都复制了一遍,而这种对被引用到的对象的复制叫做间接复制。

深度克隆要深入到多少层,是一个不易确定的问题。在决定以深度克隆的方式复制一个对象的时候,必须决定对间接复制的对象时采取浅度克隆还是继续采用深度克隆。因此,在采取深度克隆时,需要决定多深才算深。此外,在深度克隆的过程中,很可能会出现循环引用的问题,必须小心处理。

注意事项

使用Java的API实现的原型模式复制对象不会调用类的构造方法。因为对象的复制是通过调用Object类的clone方法来完成的,它直接在内存中复制数据,因此不会调用到类的构造方法。不但构造方法中的代码不会执行,甚至连访问权限都对原型模式无效。还记得单例模式吗?单例模式中,只要将构造方法的访问权限设置为private型,就可以实现单例。但是clone方法直接无视构造方法的权限,所以,单例模式与原型模式是冲突的,在使用时要特别注意。

使用Java提供的模板进行原型模式设计

使用Java提供的API实现原型模式的原型类有如下两大要点:

实现Cloneable接口

在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。

重写Object类中的clone方法

Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此,Prototype类需要将clone方法的作用域修改为public类型。

代码实战一把

如下是使用Java的API实现的原型模式,不多解释:

package yanbober.github.io;

class IPrototype implements Cloneable {
    public IPrototype clone(){
        IPrototype prototype = null;
        try{
            prototype = (IPrototype)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
        }
        return prototype; 
    }
}

class ConcretePrototypeImpl extends IPrototype{
    public void print(){
        System.out.println("I‘m Impl!!!");
    }
}

public class Main {
    public static void main(String[] args){
        ConcretePrototypeImpl cp = new ConcretePrototypeImpl();
        for(int i=0; i< 100; i++){
            ConcretePrototypeImpl clonecp = (ConcretePrototypeImpl)cp.clone();
            clonecp.print();
        }
    }
}

总结一把

原型模式优点如下:

  • 创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率。
  • 扩展性较好,在原型模式中提供了抽象原型类,在客户端可以针对抽象原型类进行编程。
  • 可以使用深克隆的方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用(如恢复到某一历史状态),可辅助实现撤销操作。

原型模式缺点如下:

  • 需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时,需要修改源代码,违背了“开闭原则”。
  • 在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重的嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来可能会比较麻烦。

设计模式(创建型)之原型模式(Prototype Pattern)

标签:设计模式   pattern   java   原型模式   

原文地址:http://blog.csdn.net/yanbober/article/details/45338829

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!