模式总结:原型模式

类别:Java 点击:0 评论:0 推荐:

模式的本质就是不断重复出现的问题的可重用解决方案。一个完整的模式提供使用或者不使用该解决方案的理由,使用解决方案的结果,以及怎样实现该解决方案的建议。本文中模式总结仅描述问题的本质及其解决方案。这是最后一组与创建对象相关的模式。本文将重点讲解动态决定实例化哪个类,或者一个对象把职责委托给哪些对象。这些模式会告诉我们怎样组织和封装这些决定。该文将讨论原型模式(Prototype)。通过给类指定每种将要被创建的对象的原型实例,原型模式(Prototype)使得该类可以创建那些实现已知接口的对象。

假设你正在设计一个CAD(计算机辅助设计)程序,它允许用户根据图形符号画板来作图。该程序具有一套核心的内置图形符号。但是,使用这个程序的人们往往具有不同的特殊需求。那么这套核心图形符号将不能满足人们的这种特殊需求。这些人就希望程序具有额外的图形符号来适合他们的需求。由于这个程序的大多数用户都具有特殊的需求,所以程序必须尽可能的提供额外的图形符号集,使得用户可以根据他们的需要把它们添加到程序中去。

这种需求将会引起一个问题:怎样提供那些额外图形符号的画板。你可以很容易的组织如此之多的图形符号,包括核心的和额外的,它们都继承一个共同的祖先类。这给你的作图程序使用一致的方式来操作图形符号对象提供了支持。但是它并没有解决程序怎样创建对象的问题。创建诸如此类的对象通常比简单的实例化一个类要复杂得多。它可能还包括数据属性值的设定,或结合其他对象来组成一个复合对象。

一个解决方案就是给作图程序提供预先创建好的对象,然后把它们作为创建类似对象的原型。把对象用作原型,这就要求它们具有一个方法,一般称之为clone。clone方法返回一个对象,它是原始对象的一个新的拷贝。下面的类图描述了该方案是怎样组织的:

图1 Symbol原型

作图程序维护一组用作原型的Symbol对象。通过克隆这些原型对象,作图程序可以使用Symbol对象。SymbolBuilder创建Symbol对象并且向作图程序进行注册。

所有的Java类都从Object类那里继承一个叫做clone的方法。一个对象的clone方法返回该对象的一个拷贝。clone方法只有在对象类被授权的情况下才进行拷贝。如果一个类授权给它的实例可以克隆自己,那么该类必须实现Cloneable接口。因为Symbol类实现了Cloneable接口,所以作图程序可以克隆Symbol对象。并且作图程序管理这些Symbol对象并把它们组合成图。

一般来说,通过给类指定每种将要被创建的对象的原型实例,原型模式(Prototype)使得该类可以创建那些实现已知接口的对象。因此,克隆原型实例就可以创建新对象。

下图描述了一般情况下的原型模式(Prototype)的组织结构:

图2 原型模式(Prototype)

下面描述原型模式(Prototype)中的这些类和接口所扮演的角色:

l         Client

对原型模式(Prototype)的目的来说,client类表示程序的其他部分。client类需要创建它不了解的对象。Client类具有一个方法,可以调用它来给client对象的原型对象集合增加新的原型对象。在上图中,该方法的名称为registerPrototype。但是,在实际的实现中,反映被原型化的对象集合的名字被叫做为registerSymbol可能会更恰当一些。

l         Prototype

扮演Prototype角色的类实现PrototypeIF接口,并且可以被client克隆而达到被实例化的目的。扮演这种角色的类通常是抽象类,且它具有许多具体的子类。

l         PrototypeIF

所有的原型对象都要实现PrototypeIF接口。client类通过这个接口与原型对象进行交互。这种角色的接口应该扩展Cloneable接口,这样所有实现该接口的对象才能够被克隆。

l         PrototypeBuilder

PrototypeBuilder类对应于那些被实例化以给client对象提供原型对象的任何类。像这样的类应该具有这样一个名字,它必须能够表示它所能创建的原型对象的类型,例如SymbolBuilder。PrototypeBuilder对象创建Prototype对象。它把每次新创建的Prototype对象传递给Client对象的registerPrototype方法。

该模式的示例代码是基于一个不同的情形。假设你正在编写一个交互角色的游戏比赛。也就是说,该游戏允许用户与模拟的人物角色进行交互。其中,游戏的一个需求就是玩家越来越厌烦与同一个角色进行交互,而希望与一个新角色进行交互。由于这个原因,你还需要给游戏开发一个附加部分,它由一些预先生成的角色所组成,并且需要开发一个程序来生成这些附加的角色。

游戏中的角色都是一些相关的类的实例,例如Hero,Fool,Villain和Monster。是什么使得同一个类的实例之间相互不同呢?答案就是给这些实例设置不同属性值,例如表示它们的图像,高度,宽度,智力和敏捷。

下面的类图描述了游戏中所包含的部分类:

图3 原型模式示例

清单1列出了CharacterIF接口代码,该接口扮演PrototypeIF角色。

 

清单1:CharacterIF接口

public interface CharacterIF extends Cloneable {

    public String getName() ;

    public void setName(String name) ;

    public Image getImage() ;

    public void setImage(Image image) ;

    public int getStrength() ;

    public void setStrength(int strength) ;

    ...

} // interface CharacterIF

 

下面是Character类代码,该抽象类扮演PrototypeIF角色。

 

清单2:Character类

public abstract class Character implements CharacterIFloneable {

...

    /**

     * Override clone to make it public.

     */

    public Object clone() {

        try {

            return super.clone();

        } catch (CloneNotSupportedException e) {

            // This should never happen because this class implements

            // Cloneable.

            throw new InternalError();

        } // try

    } // clone()

 

    public String getName() { return name; }

 

    public void setName(String name) { this.name = name; }

 

    public Image getImage() { return image; }

 

    public void setImage(Image image) { this.image = image; }

 

...

} // class Character

 

这个类的大部分代码都是简单的存取方法。比较不清楚地方法就是clone方法。所有的对象都从Object类那里继承一个clone方法。因为这个clone方法不是公共的,所以character类必须用一个public声明来覆盖它,这只是使得它可以被其他的类访问。

清单3是Hero类的源代码,这个类所扮演的其中一个角色就是Prototype:

 

清单3:Hero类

public class Hero extends Character {

    private int bravery;

...

    public int getBravery() { return bravery; }

 

    public void setBravery(int bravery) { this.bravery = bravery; }

} // class Hero

 

Monster类与Hero类相似。

下面就是CharacterManager类代码,它扮演Client角色:

 

清单4:CharacterManager类

public class CharacterManager {

    private Vector characters = new Vector();

...

    /**

     * Return a copy of random character from the collection.

     */

    Character getRandomCharacter() {

        int i = (int)(characters.size()*Math.random());

        return (Character)((Character)characters.elementAt(i)).clone();

    } // getRandomCharacter()

 

    /**

     * Add a prototypical object to the collection.

     */

    void addCharacter(Character character) {

        characters.addElement(character);

    } // addCharacter(Character)

...

} // class CharacterManager

 

清单5列出了CharacterLoader类的代码,它来完成PrototypeBuilder角色:

 

清单5:CharacterLoader类

/**

 * This class loads character objects and adds them to the

 * the CharacterManager.

 */

class CharacterLoader {

    private CharacterManager mgr;

 

    /**

     * Constructor

     * @param cm The CharacterManager that this object will work with.

     */

    CharacterLoader(CharacterManager cm) {

        mgr = cm;

    } // Constructor(CharacterManager)

 

    /**

     * Load character objects from the specified file.

     * Since failure only affects the rest of the program to the extent

     * that new character objects are not loaded, we need not throw any

     * exceptions.

     */

    int loadCharacters(String fname) {

        int objectCount = 0;    // The number of objects loaded

        // If construction of the InputStream fails, just return

        try {

            InputStream in;

            in = new FileInputStream(fname);

            in = new BufferedInputStream(in);

            ObjectInputStream oIn = new ObjectInputStream(in);

            while(true) {

                Object c = oIn.readObject();

                if (c instanceof Character) {

                    mgr.addCharacter((Character)c);

                } // if

            } // while

        } catch (Exception e) {

        } // try

        return objectCount;

    } // loadCharacters(String)

} // class CharacterLoader

本文地址:http://com.8s8s.com/it/it17026.htm