明輝手游網(wǎng)中心:是一個免費提供流行視頻軟件教程、在線學習分享的學習平臺!

IssueVision 學習筆記(3)-----設(shè)計模式之OBSERVER(觀察者)模式

[摘要]這次我們來看看IssueVision中的對設(shè)計模式的應(yīng)用,IssueVision主要使用了OBSERVER(觀察者)模式和COMMAND(命令)模式.今天就來看看OBSERVER(觀察者)模式在IssueVision中的應(yīng)用,它在IssueVision中扮演著重要角色. "四人幫...
這次我們來看看IssueVision中的對設(shè)計模式的應(yīng)用,IssueVision主要使用了OBSERVER(觀察者)模式和COMMAND(命令)模式.今天就來看看OBSERVER(觀察者)模式在IssueVision中的應(yīng)用,它在IssueVision中扮演著重要角色.

"四人幫"GoF是這樣定義OBSERVER(觀察者)模式的------定義對象間的一種一對多的關(guān)系,當一個對象的狀態(tài)發(fā)生改變時,所有依賴它的對象都得到通知,并被自動更新.
從定義可以看出,OBSERVER(觀察者)模式邏輯上需要兩組對象來實現(xiàn).首先它必需要有發(fā)布者(Publish),也可稱為被觀察的目標(Subject)(習慣上都稱它為目標Subject,后面我們都稱它作目標Subject),另外就是訂閱者(Subscribe),習慣上稱為觀察者(Observer).一個目標對象對應(yīng)多個觀察者對象,目標對象發(fā)生變化時,所有在目標對象中注冊的觀察者對象會得到通知,自動更新自己.

在應(yīng)用程序開發(fā)過程中,往往都要求用戶界面和業(yè)務(wù)邏輯分離,劃定清晰的界限.因為應(yīng)用程序要求能快速的更改用戶界面并且不能對應(yīng)用程序其他部分產(chǎn)生連帶影響,而且業(yè)務(wù)邏輯也會發(fā)生變化并要求這一切變化與用戶界面無關(guān).觀察者(Observer)就是解決此問題最常用的設(shè)計模式,它非常有助于在系統(tǒng)中各個對象之間劃分清晰的界限.

下圖最好的展示了這種形式,觀者者(observer)們(表格對象,柱狀圖對象和餅狀圖對象)都依賴數(shù)據(jù)對象Subject,所以數(shù)據(jù)對象Subject的所有改變都會通知它們.但它們互相之間并不知道對方的存在,表格對象不知道其他表格對象或者其他柱狀圖對象的存在.對于數(shù)據(jù)對象Subject可以有任何多的觀察者,這些觀察者都能在Subject對象發(fā)生改變時得到通知,使自己的狀態(tài)與Subject同步:





好吧,概念就說這么多吧,我們來看一下OBSERVER(觀察者)模式的實現(xiàn).通用的OBSERVER(觀察者)模式的實現(xiàn)我在這就不說了,可以參考GoF的設(shè)計模式和Java與模式等書.我們就來專注于.NET框架中OBSERVER(觀察者)模式的實現(xiàn).

.NET框架引入了委托和事件,它們提供了更新,功能更強大的方法來實現(xiàn)OBSERVER(觀察者)模式.(關(guān)于委托和事件的更多內(nèi)容請參考相關(guān)文檔).如果你不熟悉委托和事件,實現(xiàn)OBSERVER(觀察者)模式則需要作很多工作(像在Java中實現(xiàn)那樣).下面我就以IssueVision中的實現(xiàn)來簡單講述一下.

在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;
}
}
}

大家可能發(fā)現(xiàn),這兩個接口幾乎為空,什么都沒有定義,那這兩個接口的作用是什么呢?其實定義這兩個接口的作用主要為編碼的"規(guī)范化",只要類實現(xiàn)了這兩個接口任何一個,那么就代表此類就實現(xiàn)了OBSERVER(觀察者)模式,并且很明顯的知道誰是Subject,誰是Observer.

IssueVision中的IssueSubject組件就實現(xiàn)ISubject接口,它是IssueVision中實現(xiàn)OBSERVER(觀察者)模式最主要的部分,也是IssueVision中最最復雜的一個類.我們慢慢一點一點來分析它(當然之分析OBSERVER(觀察者)模式相關(guān)部分):

IssueSubject.cs

public class IssueSubject : Component, ISubject

首先定義類IssueSubject繼承自Component和ISubject,從這里和類名可以看出,此類是作為OBSERVER(觀察者)模式中的目標(Subject)實現(xiàn)的.目標(Subject)對象要求實現(xiàn)對觀察者(Observer)的注冊(Register)和通知,Component類就是用來實現(xiàn)注冊(Register)的.大多數(shù)目標(Subject)對象的實現(xiàn)并不是并非將觀察者(Observer)的引用直接存儲在自己的實例變量中,而是將此人物委托給一個單獨的對象(通常為一個容器)Component類就是這樣一個容器,下面代碼顯示了如何用Component類容器來存儲對觀察者(Observer)對象的引用注冊(Register)

public IssueSubject(IContainer Container) : this()
{
Container.Add(this);
}

我們來看一下它是如何完成注冊(Register)的,首先我們找到一個觀察者(Observer)對象的實現(xiàn),Controls/IssueTreeView 用戶控件就是一個觀察者(Observer)對象.它實現(xiàn)了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中調(diào)用此用戶控件代碼如下:

private IssueSubject m_issueSubject = null;

.....
m_issueSubject = new IssueSubject(this.components); //調(diào)用IssueSubject的構(gòu)造函數(shù)


paneStaff.Subject = m_issueSubject;

通過調(diào)用IssueSubject的構(gòu)造函數(shù),是此觀察者(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)對象都能得到這個改變的通知,從而激活相應(yīng)的處理.

再看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的事件,并交給相關(guān)方法處理事件
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);
}
}


通過這么簡單的幾個步驟,就實現(xiàn)了OBSERVER(觀察者)模式,.NET框架提供的委托和事件機制很大的簡化了模式的實現(xiàn).

CopyRight © YellowWee 2004. All Right Reserved.