Alternator(置换器)—对象行为型模式

John Liebenau 著    透明

 

1.意图

 

       在一个层次化结构(hierarchical structure)中允许多个候选子树(alternative subtree),并且对客户(client)隐藏这个结构的细节内部。

 

2.动机

 

       假设我们正在开发一个证券(stock)交易应用系统。我们的主要目的是为交易者(trader)提供对证券组——股票(portfolios[1]——的可视化访问,交易者可以通过它买入、卖出股票。我们将使用一个面向对象的GUI框架来构建这个应用程序的图形用户界面,其中的每个窗口(window)都将包含一个可视化元件的体系(hierarchy)。组织这个应用程序的一条简单的途径是:在每支股票自己的窗口中显示它。通过选择一个窗口,交易者就可以选择一支股票进行处理。在通常情况下,创建股票与窗口之间的映射,这样就可以很好的满足我们的需要。但是,交易者的工作环境又向我们的应用程序提出了新的需求。

       交易者的工作环境中已经存在另外的一些应用程序,它们对相关的证券信息进行显示和分析。一个典型的交易者可能需要同时使用多个工作站,或者需要显示与他/她的工作相关的新闻。新增加的视频显示和额外的窗口需求不可避免的造成了信息超载(information overload)。如果我们的应用程序只提供交易者需要的信息和当时所需的最小数量的窗口,以降低信息超载造成的影响,交易者会更喜欢使用它。

       我们的证券交易系统需要提供这样一个机制:使用一个窗口选择并逐个显示股票信息。于是我们找到了问题的根本:窗口必须可以提供几种可选的视图(view)供交易者选择。下面的图展示了在三支股票的

情况下我们的证券交易系统的元件体系。


       我们的证券交易系统的图形用户界面(GUI)只有一个窗口,它包含了一个菜单条(menu bar)、显示当前股票概要信息的标签(label)、以及显示股票信息的几个窗口部件(widget)。我们还需要一个机制用以:

l         管理股票窗口部件的放置和尺寸。

l         管理股票窗口部件的可见性。

l         管理对股票窗口部件的选择。

通过创建Window类的一个子类——这个子类提供处理我们的特定情况的定制算法——我们可以为我们的窗口对象实现上面的机制。但是,这个解决方案还有几个缺陷:它强迫我们在每个需要可选视图的地方写一个Window的新子类;它阻碍了我们在原来的GUI框架基础上发展新的算法。


       为了解决这个问题,我们可以用一个置换器(Alternator)对象来管理对可选元件组的选择、可见性和排列。对于元件来说,置换器既是拥有者(place holder)、又是容器(container)。通过引入置换器元件来管理可选视图,我们对上图进行了扩展,得到下面的一个图:

 


       下面的类图展示了一个小规模的图形用户界面框架的类结构。这个框架提供了Viewable类,它为所有的GUI对象定义了通用接口。View类维护一个Viewable对象的引用,Viewable对象组成了View对象的内容。LabelButtonViewAlternator等类则对Viewable接口进行扩展,它们也有各自特有的操作。ViewAlternator类维护它的可选元件及当前选择元件的引用。View类只把ViewAlternator对象看成另外一个Viewable对象,并且View类对一组可选元件的放置是一无所知的。


 


3.适用性

 

以下情况适用Alternator模式

l         一个对象是一个体系的一部分。

l         一个对象由几个子对象组成,但在同一时刻只使用一个子对象。

l         你希望单个对象在同一逻辑空间表示多个对象。

l         你需要从多个可选件中进行选择。

l         可选对象拥有相同的接口。


4.结构


 

5.参与者

 

• AbstractInterface (Viewable) —— 抽象接口

- AlternatorAlternate提供公用接口。

- 提供使用(engage)和挂起(disengage)操作(比如showhide),并将之作为公共接口的一部分。

• Alternator (ViewAlternator) —— 置换器

- 提供设置选项、选择选项的接口。

- 提供一个机制,用以从一个选项转换到另一个(也就是说,挂起一个元件,使用另一个)。

• Alternate (Button,CheckBox,...) —— 可选件

- 提供AbstractInterface的一个具体实现。

- 可以提供与其他Alternate不同的附加接口。

• AbstractClient (View) —— 抽象客户

- 通过AbstractInterface使用AlternatorAlternate

• ConcreteClient (SpecificView) —— 具体客户

- 通过AlternatorAlternate自己的特定接口使用它们。

-          可能有创建、配置AlternateAlternator的责任。

 

6.协作

 

l         Client使用AbstractInterface对对象进行操作。如果被操作的是一个Alternate对象,它直接处理Client的请求;如果被操作的是一个Alternator对象,它将这个请求转发给自己当前使用的Alternate对象。

l         Client也可以使用Alternator类和Alternate类的特定接口。特别的,客户可以用Alternate特有的接口对它进行配置,然后用一个Alternate对象列表配置Alternator对象。Alternator当前使用的Alternate对象也由客户指定。

 

7.效果

 

Alternator模式有如下益处:

l         让几个可选对象看起来象一个对象。这可以使使用这些元件的对象系统的算法结构得到简化,因为这些算法可以以统一的形式对待所有的元件。

l         透明的管理可选对象的激活和非激活。这些可选对象在对象体系中占用同一个逻辑空间。在GUI的环境中,我们所说的“逻辑空间”也就是元件在屏幕上的排列。当一个新的可选对象被选中时,Alternator自动将先前使用的可选对象挂起。

Alternator模式有如下的不足:

l         可能过分要求可选件一般化(general)。Alternate通过它们的公共接口而被客户操作。这个依赖于类体系的接口可能非常一般化。如果客户需要向下转换或者维护附加引用,他可能需要访问具体可选对象的特有接口。

l         可能禁止或阻碍对被挂起的可选对象的访问。Alternator的设计使它在体系中看起来是一个单独的元件,但一些客户可能需要在访问当前使用的可选对象的同时也访问被挂起的那些。这会破坏Alternator作为单独元件的视感,从而抵消本模式的一些优点。

 

8.实现

 

       在使用Alternator模式时,需要注意以下几个实现要点:

l         可选件的创建和置换器的配置。典型的情况是:客户程序直接或者使用Abstract Factory模式[GHJV95]创建可选件和置换器,然后用合适的可选元件对置换器进行配置。在另外一些情况下,可能需要由置换器负责创建它自己的可选元件。典型情况下,置换器会把它们的可选元件封装起来,不允许客户程序对它们进行任何直接访问。

l         保持使用和挂起行为的一致性。作为置换器的元件应该在它们的使用/挂起方法与其他行为之间保持一致性。一般来说,元件应该有一些“开关”形式的变量。在“动机”一节讨论的例子中,屏幕上的GUI元件或者是可见的,或者是不可见的。可见的元件可以接收输入,不可见的元件则不能。

l         维护可选件的引用。在强类型语言(C++java)的实现中,Alternator只为体系中的元件提供一个公用接口(本例中的AbstractInterface)。如果你需要使用某个可选件特有的方法,你或者需要这个可选件的引用(与这个可选件类型相同),或者需要做向下类型转换。

l         管理可选件的状态。有时候,可选件之间是完全独立的。只有当它们被使用的时候,它们的状态才会被更新。另一些时候,可选件之间有一定的相互依赖。在这种情况下,当前使用的可选件的状态被更新的时候,与之相关的其他可选件的状态也必须同时更新。这可以由Alternator元件来负责处理:如果一个操作改变了可选件的状态,置换器将把这个操作转发给每个可选件。以我们在“动机”一节中描述的ViewAlternator类为例,如果这个类实现了一个方法来设置它的x坐标或者y坐标,则所有可选件的坐标都需要同时被更新:

void ViewAlternator::setX(int x)

{

for (vector<Viewable*>::iterator alternate( alternatives.begin() ); alternate != alternatives.end(); alternate++)

(*alternate)->setX( x );

}

l         Composite模式实现Alternator模式。Alternator模式被用在层次化对象体系中,这些对象体系可以用Composite模式来实现[GHJV95]Alternator模式可以看成是Composite模式的一个形式,但不同之处是Alternator模式引入了这样的一个概念:选择一个子对象,使用(或者说激活)它;其它的子对象都是非激活的。

l         同时使用多个可选件。在某些情况下,可能需要同时使用多个可选件。为了实现这一目的,置换器要维护一个“当前使用元件”的列表,而不再是单个引用。此外还必须添加新的方法,用以设置当前使用的可选件。

 

9.代码示例

 

       下面的示例代码是“动机”一节讨论的例子的程序片段。抽象类Viewable为所有GUI元件指定了通用接口。

class Viewable

{

public:

// ...

// Modifiers

virtual void setX(int x);

virtual void setY(int y);

virtual void setHeight(int h);

virtual void setWidth(int w);

// Selectors

virtual int getX()const;

virtual int getY()const;

virtual int getHeight()const;

virtual int getWidth()const;

// Actions

virtual void arrange()=0;

virtual void show()=0;

virtual void hide()=0;

};

       Button类扩展了Viewable抽象类,提供了按钮元件特有的方法。Button类对Viewable类中声明的纯虚函数做了实现。

class Button: public Viewable

{

public:

// ...

// Modifiers

void setLabel(const string& lbl);

void setCommand(Command* cmd);

// Action

void doCommand();

};

       ViewAlternator类是一个GUI元件,它负责管理其他的GUI元件(也管理其他的ViewAlternator元件)。ViewAlternator类允许客户选择他们想要显示和激活的可选元件,而其他的可选件就被自动隐藏起来并且失去活性。

class ViewAlternator : public Viewable

{

private:

vector<Viewable*> alternatives;

Viewable* current;

public:

// ...

// Viewable: Actions

void arrange();

// Modifiers

void setAlternatives(vector<Viewable*>& alt);

void setCurrent(Viewable* v);

// Selectors

Viewable* getCurrent()const;

int getNumAlternatives()const;

};

ViewAlternator对象将函数调用转发给当前使用的可选件。

void ViewAlternator::arrange()

{

if ( current )

current->arrange();

}

       在选择可选件的时候,ViewAlternator使用(显示)一个可选件、并且挂起(隐藏)其他的可选件。

void ViewAlternator::setAlternatives(const vector<Viewable*>& alt)

{

vector<Viewable*>::iterator alternate;

alternatives = alt;

alternate = alternatives.begin();

if ( alternate != alternatives.end() )

{

current = *alternate;

current->show();

alternate++;

}

for ( ; alternate != alternatives.end(); alternate++)

(*alternate)->hide();

}

       当客户选择一个新的可选件时,ViewAlternator隐藏以前使用的可选件,把新的设为可见。如果以前使用的可选件和新的是相同的,这个方法立刻返回,不做任何改动。

void ViewAlternator::setCurrent(Viewable* v)

{

if ( current != v )

{

for (vector<Viewable*>::iterator alternate( alternatives.begin() ); alternate != alternatives.end();    alternate++)

{

if ( *alternate == v )

{

current->hide();

current = *alternate;

current->show();

break;

}

}

}

}

 

10.已知应用

 

       GUI框架2.0——ITG内部使用的一个用户界面框架——中有几处用到了Alternator模式。这个GUI框架提供一个ViewAlternator类,用来管理可选的Viewable对象。Alternator模式的一个更具体的实现是Selector类,它可以根据用户选择,把自己当作ScrollingListCheckBoxOptionMenu或者MultipleChoice来显示。

       JAVA至少包含了Alternator模式的两个实例。JAVA Swing库中有一个名叫JTabbedPane的类[Topley98],这是一个可视化元件,通过对拥有一个标题和/或图标的标签(tab)的点击,用户可以在一组可视化元件之间切换。JAVA AWT库中有一个名叫CardLayout的类[CL98],这是一个可视化元件的容器,它在一个时刻只显示一个元件,将其他的元件都隐藏起来。CardLayout提供了一组方法,用于设置当前可见的元件。

       微软基础类(Microsoft Foundation ClassMFC)库提供了名叫CpropertySheet的类[Microsoft95],这也是一个容器,其中包含一个或多个CPropertyPage元件,这些CPropertyPage元件又分别包含其他窗口元件。通过点击与某个CPropertyPage相关联的标签,用户就可以选择这个CPropertyPage

       Motif也提供了一个名叫Notebook的窗口元件[OSF94],它包含了多个含有其他窗口元件的页面。通过点击标签,用户也可以选择任何一个页面。

 

11.相关模式

 

       Alternator模式可以用Composite模式实现。

       Alternator模式与Backup模式[ST95]相似,只是Alternator模式解决的问题更具普遍意义。Backup模式关注于为指定的函数提供候选函数,它们将在主函数失败的时候自动被使用;而Alternator模式关注于一个普遍意义上的任务:为候选项提供一个放置的地方、一种选择使用的方法。

       Alternator模式与Sponsor-Selector模式[Wallingford98]也有关系,这两个模式都处理对可选资源的选取行为,这样这些资源可以被动态的改变。这两个模式之间的区别在于:Alternator模式集中精力管理可选元件,使它们在对象体系中看起来就象一个元件,从而使处理这个体系的算法得到简化;Sponsor-Selector模式集中精力从一系列资源中为指定的任务选取最合适的资源。

       Alternator模式和State模式[GHJV95]也有相似之处,它们的置换器对象(State模式中的State)看起来都在运行时改变了它们自己的类,这取决于它当前使用的可选件(State模式中的Context)。这两个模式之间的区别在于:在State模式中,从一个状态到另一个状态的转变有一个预先规定的顺序;而Alternator模式对转化是一无所知的,它通过选择操作决定可选件的使用和挂起。

 

参考文献

 

[CL98] Chan, Patrick and Rosanna Lee. The Java Class Libraries Second Edition, Volume 2. pp 208-220, Addison-Wesly Longman Inc. 1998.

[GHJV95] Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesly Publishing Co. 1995. 中文版:《设计模式:可复用面向对象软件的基础》,李英军等译,机械工业出版社20009月。

[Microsoft95] Microsoft Foundation Class Library Reference: Part Two, Volume Four. pp 1350-1368, Microsoft Press. 1995.

[OSF94] OSF/Motif User’s Guide: Revision 2.0. pp 3.28-3.29, Open Software Foundation, Inc. 1994.

[ST95] Subramanian, Satish, and Wei-Tek Tsai. Backup Pattern: Designing Redundancy in Object-Oriented Software. Pattern Languages of Program Design 2, Addison-Wesly Longman Inc. 1996.

[Topley98] Topley, Kim. Core Java Foundation Classes. pp 566-582. Prentice Hall Inc. 1998.

[Wallingford98] Wallingford, Eugene. Sponsor-Selector. Pattern Languages of Program Design 3, Addison-Wesly Longman Inc. 1998.

 

[1]

译者注:我把“stock”译为“证券”、“portfolio”译为“股票”,以区别这两个词。