本笔记是《Pattern-Oriented Software Architecture vol.1 A system of patterns》原书[page 295-300]的山寨翻译:),包括了View Handler模式的[动态]和[实现]两小节。
-------------------------------------------------
[动态]
[296]
场景1 显示了View Hanlder如何创建一个新的View,此场景由四个阶段组成:
- 客户端(可能是用户或者另一个系统组件)调用View Handler打开一个特定的View。
- View Handler实例并初始化这个View,为实现更改的传播机制,就像Publisher-Subscribe模式一样,此View在其相应的supplier中注册。
- View Handler将此新的View添加到其内部的已经打开的View列表中。
- View Handler调用View的函数显示此View,此View打开一个新的窗口,从它的supplier中获取数据,准备这些数据,并把他们呈现给用户。
[图]
场景2 图示了View Handler是如何排列View的,我们简单地假设只有两个视图是被打开的。这个场景被分为三个阶段:
- 用户执行排列所有已经打开窗口的命令,此请求被发送给View Handler。
- View Handler计算每个已打开View的大小和位置,并调用他们的resize和move方法。
[297]
- 每个View都改变自己的位置和大小,设子相应的剪切区域,并刷新它显示给用户的图像。我们这里假定View都缓存了需要显示的图像,如果没有缓存,则View在重新显示它自己之前还需要从他们相关联的supplier中重新获取数据。
[图]
[实现]
实现View Handler的结构由4步骤组成。我们假定supplier已经存在,并且包括了一套适于传播更改的机制。
1 识别所有的View。识别出需要提供的视图类型,并确定用户如何操控每个独立的View。
2 描述一个所有View的公共接口,此接口应该包括:打开,关闭,显示,更新和操控view的功能,也可以提供初始化View的功能,用特定的supplier中的数据配置view。将此接口封装进一个抽象类中,可以为例如视图更新这样的功能提供缺省实现。
在我们的文档编辑器的例子中,定义了类AbstractView,在此类中的protected的方法包括了显示和删除窗口的功能,也包括显示窗口内容的功能,在public方法中包括对View:初始化、打开、关闭、移动、缩放、拖动和更新操作。
[298]
class Abstractview {
protected:
// Draw the view
virtual void displayData() = 0;
virtual void displayWindow(Rectang1e boundary) = 0;
virtual void eraseWindow() = 0;
public:
// Constructor and Destructor
AbstractView ( ) {} ;
~AbstractView() { } ;
// Initialize the view
void initialize() = 0;
// View handling with default implementation
virtual void open(Rectang1e boundary) { /* . . . */ } ;
virtual void close() ( /* . . . * / };
virtual void move(Point point) { /* ... */ } ;
virtual void size(Rectang1e boundary) { /* ... */ } ;
virtual void drag(Rectang1e boundary) { /* ... */ } ;
virtual void update () { /* . . . */ } ;
};
3 实现每个View。对于每个在第一步骤中识别出的特定类型,都从Abstractview继承一个独立的View类,在特定View中实现如我们例子中的displayData()一样的特定方法,并重写(override)缺省实现不满足需求的方法。
如果View Handler实现了特定的协调和更新策略,则View必须把所有影响其他View的事件通知给View Handler知道。例如,调整一个View的大小有可能会将之前遮挡到的其他View的某部分显示出来,如果是View Handler来协调这些View的更新,则调整的View应该把此调整事件通知给View Handler知道。Publisher-Subscribe模式能帮助实现这种更改通知机制。
在解决方案一节我们的例子中,我们实现了3个View类:EditView,LayoutView和ThumbnailView,他们都不需要重写从AbstractView中继承方法的缺省实现。
4 定义View Handler。可以像Factory Methods模式[GHJV95]一样实现创建View的方法。Client能指定他们所需要的View,但是他们不能控制如何创建View,View Handler负责实例并初始化正确的View组件。
[299]
View Handler在内部维护所有已经打开的View的引用,Iterator模式[GHJV95]对实现此功能有所帮助。View Handler也许还维护如屏幕上窗口当前位置和大小这样的View附加信息,这些信息在View Handler提供的如窗口克隆这样的管理功能中会用到。
View Handler也许需要实现应用程序特定的View协调策略,例如,一个View呈现另一个View的信息,动作类似的日志信息。当排列两个相互依赖的View时,需要将他们挨着排放;或者当他们都被最小化的时候,打开一个View将把另一个也打开。
更新策略是View协作的另一个例子,也许必须优先更新特定View,如:显示告警的View也许需要在打开其他View之前更新,在这种情况下,supplier将改动通知给View handler而不是依赖于他的View,View Handler采取一定的更新策略将此请求转发给受影响的View。协调View更新的View Handler通常在他们的公共接口中提供一个更新函数。
为了使协作策略能更换,可以采用strategy模式[GHJV95]来实现这些策略。Mediator模式[GHJV95]为实现View协作提供帮助,如:在所有打开的View中广播刷新请求。也可以用Singleton模式[GHJV95]确保View Handler只能被实例化一次。
在我们的例子文档编辑器中的View Handler提供了打开、关闭、排列、置前、克隆View的功能。在View Handler内部维护了所有已经打开的View的引用,还包括他们的位置和大小、是否已经最小化等信息。
class ViewHandler {
// Data structures
struct ViewInfo {
Abstractview* view;
Rectangle boundary;
boo1 iconized;
} ;
[300]
Container<ViewInfo*> myviews;
// The singleton instance
static ViewHandler* theViewHandler;
// Constructor and Destructor
ViewHandler ( ) ;
~ViewHandler();
public:
// Singleton constructor
static ViewHandler* makeViewHandler0 ;
// Open and close views
void open (Abstractview* view) ;
void close(AbstractView* view);
// Top, clone, and tile views
void top (AbstractView* view) ;
void clone(); // Clones the top-most view
void tile ( ) ;
} ;
以下代码为创建新View提供示例,defaultBoundary是Rectangle类的对象,定义了每个新窗口的缺省位置和大小,这段代码实现了[动态]一节中的场景一。
void ViewHandler::openView(AbstractView* view) {
ViewInfo* viewInfo = new ViewInfo ( ) ;
// Add the view to the list of open views
viewInfo ->view = view;
viewInfo->boundary = defaultBoundary;
viewInfo->iconized = false;
myviews.add (viewInfo) ;
// Initialize the view and open it
view->initialize () ;
view->open(defaultBoundary);
};