最近因為工作需要,使用到了剪貼簿的特殊功能。也翻閱了一些網上介紹的資料,發現要實作類別似FlashGet那樣下載工具中監視剪貼簿的實現方式,對我們程式設計有點借鑒的意義。
在Windows提供的剪貼簿API中,針對監視這塊,提供的是註冊機制。主要函數是SetClipboardViewer這個API函數。這個函數的聲明是這樣的(Delphi):
function SetClipboardViewer(hWndNewViewer: HWND): HWND; stdcall;
通過這個函數,將一個視窗控制代碼,註冊到系統剪貼簿中。可以稱註冊後的表單為一個Clipboard Viewer,眾多的Viewer形成一個Clipboard Viewer Chain。這個Chain是一個典型的鏈表,前一個記住下一個的指標。
註冊為Viewer的Handle所在的表單,通過處理WM_CHANGECBCHAIN和WM_DRAWCLIPBOARD兩個訊息,來處理所有來自剪貼簿的變化。處理這些訊息的時候,記住向下一個Viewer傳遞訊息,代碼類似於
SendMessage(hwndNextViewer, message, wParam, lParam);
我重點並不是要說明代碼如何編寫,只是簡單地介紹了這個“監控”的實現方式。我們很容易聯想到,這和設計模式中提到的Observer模式是非常類似的。最大的不同點在於,剪貼簿直接使用了訊息系統作為解耦的方式。
正因為有這個相似的地方,我才願意仔細分析一下這種實現方式的優點和缺點。
先來說說優點:
- 目標和觀察者之間完全解耦,不需要定義特定介面。
- 觀察者可以在任意Window上附加實現。這在很多表單應用的程式來說非常方便。
- 可以很方便地實現跨進程、線程的觀察者。這得益於標準的訊息機制。
- 跨語言實現沒有什麼問題。
- 使用PostMessage和SendMessage可以實現兩種完全不同的更新方式。Subject可以選擇等待和不等待Observer更新完成。
看了這些有點,最重要也是最核心的,就是解耦。再來分析一下缺點:
- 編程的過程,必須做到約定編程。約定編程和契約編程最大的差異在於沒有編譯機制。
- 在觀察者中可以方便地改變後續觀察者的行為,也可能完全破壞其他的正常功能。據說NetAnts曾經在這方面就有過BUG,導致其他程式無法粘貼資料。
- 一般我們的應用情境中,除了M-V(模型-視圖)的模式,還有M-M(模型-模型)的方式,針對第二種方式,這種實現方式不是很適合。
- 沒有訊息系統的程式中,不適合使用。比如控制台程式。
- 對回應時間要求很高的系統,不適合。因為訊息的回應時間不可控制。
分析完上面的這些有點和缺點,我其實更傾向於從這類設計中吸取Message這個設計項目。巧妙地使用Message,可以有意想不到的效果。
總結一下,設計模式的基本原則之一就是解耦,而Message的特性之一就是解耦。這也就是為什麼我們會發現剪貼簿的監視方式和我們學習的Observer模式很想像的根本。所以說他是模式一點也不為過。只不過少了點OO的味道。