本筆記是《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);
};