這次我們來看看issuevision中的對設計模式的應用,issuevision主要使用了observer(觀察者)模式和command(命令)模式.今天就來看看observer(觀察者)模式在issuevision中的應用,它在issuevision中扮演著重要角色.
"四人幫"gof是這樣定義observer(觀察者)模式的------定義對象間的一種一對多的關系,當一個對象的狀態發生改變時,所有依賴它的對象都得到通知,并被自動更新.
從定義可以看出,observer(觀察者)模式邏輯上需要兩組對象來實現.首先它必需要有發布者(publish),也可稱為被觀察的目標(subject)(習慣上都稱它為目標subject,后面我們都稱它作目標subject),另外就是訂閱者(subscribe),習慣上稱為觀察者(observer).一個目標對象對應多個觀察者對象,目標對象發生變化時,所有在目標對象中注冊的觀察者對象會得到通知,自動更新自己.
在應用程序開發過程中,往往都要求用戶界面和業務邏輯分離,劃定清晰的界限.因為應用程序要求能快速的更改用戶界面并且不能對應用程序其他部分產生連帶影響,而且業務邏輯也會發生變化并要求這一切變化與用戶界面無關.觀察者(observer)就是解決此問題最常用的設計模式,它非常有助于在系統中各個對象之間劃分清晰的界限.
下圖最好的展示了這種形式,觀者者(observer)們(表格對象,柱狀圖對象和餅狀圖對象)都依賴數據對象subject,所以數據對象subject的所有改變都會通知它們.但它們互相之間并不知道對方的存在,表格對象不知道其他表格對象或者其他柱狀圖對象的存在.對于數據對象subject可以有任何多的觀察者,這些觀察者都能在subject對象發生改變時得到通知,使自己的狀態與subject同步:
好吧,概念就說這么多吧,我們來看一下observer(觀察者)模式的實現.通用的observer(觀察者)模式的實現我在這就不說了,可以參考gof的設計模式和java與模式等書.我們就來專注于.net框架中observer(觀察者)模式的實現.
.net框架引入了委托和事件,它們提供了更新,功能更強大的方法來實現observer(觀察者)模式.(關于委托和事件的更多內容請參考相關文檔).如果你不熟悉委托和事件,實現observer(觀察者)模式則需要作很多工作(像在java中實現那樣).下面我就以issuevision中的實現來簡單講述一下.
在issuevision中patterns文件夾下有兩個文件isubject.cs和iobserver.cs.它們分別定義了目標(subject)對象和觀者者(observer)對象的接口,代碼如下:
isubject.cs
namespace issuevision
{
// isubject is a simple marker interface that supports the implementation
// of the observer pattern in issuevision.
public interface isubject
{
}
}
iobserver.cs
namespace issuevision
{
// iobserver is a simple interface that supports the implementation of the
// observer pattern in issuevision.
public interface iobserver
{
isubject subject
{
set;
}
}
}
大家可能發現,這兩個接口幾乎為空,什么都沒有定義,那這兩個接口的作用是什么呢?其實定義這兩個接口的作用主要為編碼的"規范化",只要類實現了這兩個接口任何一個,那么就代表此類就實現了observer(觀察者)模式,并且很明顯的知道誰是subject,誰是observer.
issuevision中的issuesubject組件就實現isubject接口,它是issuevision中實現observer(觀察者)模式最主要的部分,也是issuevision中最最復雜的一個類.我們慢慢一點一點來分析它(當然之分析observer(觀察者)模式相關部分):
issuesubject.cs
public class issuesubject : component, isubject
首先定義類issuesubject繼承自component和isubject,從這里和類名可以看出,此類是作為observer(觀察者)模式中的目標(subject)實現的.目標(subject)對象要求實現對觀察者(observer)的注冊(register)和通知,component類就是用來實現注冊(register)的.大多數目標(subject)對象的實現并不是并非將觀察者(observer)的引用直接存儲在自己的實例變量中,而是將此人物委托給一個單獨的對象(通常為一個容器)component類就是這樣一個容器,下面代碼顯示了如何用component類容器來存儲對觀察者(observer)對象的引用注冊(register)
public issuesubject(icontainer container) : this()
{
container.add(this);
}
我們來看一下它是如何完成注冊(register)的,首先我們找到一個觀察者(observer)對象的實現,controls/issuetreeview 用戶控件就是一個觀察者(observer)對象.它實現了iobserver接口.
controls/issuetreeview.cs
namespace issuevision
{
// the issuetreeview user control implements the view selection ui for issuevision
public class issuetreeview : usercontrol, iobserver
{
.....
private treeview trvviews;
private imagelist images;
private issuesubject m_subject = null;
private const string m_fontname = "tahoma";
private icontainer components;
public virtual isubject subject //isubject接口的方法
{
set
{
m_subject = (issuesubject)value;
.....
}
}
pane/staffpane用戶控件引用了此issuetreeview,它同樣也是觀察者(observer)對象.
pane/staffpane.cs
namespace issuevision
{
public class staffpane : usercontrol, iobserver
{
....
private issuetreeview itvviews;
private icontainer components = null;
// iobserver.subject
public isubject subject
{
set
{
itvviews.subject = (issuesubject)value;
}
}
在mainform.cs中調用此用戶控件代碼如下:
private issuesubject m_issuesubject = null;
.....
m_issuesubject = new issuesubject(this.components); //調用issuesubject的構造函數
panestaff.subject = m_issuesubject;
通過調用issuesubject的構造函數,是此觀察者(observer)對象注冊到了目標(subject)對象中.(通過容器的container.add())
這樣就完成了observer(觀察者)模式的第一步,觀察者(observer)對象的注冊.下面我們來看第二步,目標(subject)對象如何通知觀察者(observer)對象
這就需要使用到委托和事件了.在回來看issuesubject
issuesubject.cs
public class issuesubject : component, isubject
{
#region delagate and event declarations
.......
public delegate void conflictdatachangedeventhandler(object sender, eventargs e);
public delegate void lookupdatachangedeventhandler(object sender, eventargs e);
// conflictdatachanged changes when a conflict is resolved, or new conflicts are
// detected.
public virtual event conflictdatachangedeventhandler conflictdatachanged;
// lookupdatachanged is raised when lookup data is downloaded from the server
public virtual event lookupdatachangedeventhandler lookupdatachanged;
......
在issuesubject中申明委托和事件,觀察者(observer)對象登記這些事件,那么當issuesubject改變后,激活一個事件,那么所有的觀察者(observer)對象都能得到這個改變的通知,從而激活相應的處理.
再看controls/issuetreeview.cs
namespace issuevision
{
// the issuetreeview user control implements the view selection ui for issuevision
public class issuetreeview : usercontrol, iobserver
{
.....
private treeview trvviews;
private issuesubject m_subject = null;
private icontainer components;
public virtual isubject subject //isubject接口的方法
{
set
{
m_subject = (issuesubject)value;
//登記issuesubject的事件,并交給相關方法處理事件
m_subject.lookupdatachanged += new issuesubject.lookupdatachangedeventhandler(this.subject_lookupdatachanged);
m_subject.conflictdatachanged += new issuesubject.conflictdatachangedeventhandler(this.subject_conflictdatachanged);
}
}
最后,在issuesubject中激活這些事件.
issuesubject.cs
private void loadissuedata()
{
.......
m_dataset.datasetname = "issuesubject";
if (lookupdatachanged != null)
{
lookupdatachanged(this, eventargs.empty);
}
}
通過這么簡單的幾個步驟,就實現了observer(觀察者)模式,.net框架提供的委托和事件機制很大的簡化了模式的實現.
copyright © yellowwee 2004. all right reserved.