C#中利用代理實(shí)現(xiàn)觀察者設(shè)計(jì)模式詳解
界面開(kāi)發(fā)中,經(jīng)常使用觀察者設(shè)計(jì)模式來(lái)實(shí)現(xiàn)文檔/視圖模式,當(dāng)文檔內(nèi)容改變時(shí),作為觀察者的用戶視圖必須相應(yīng)作出調(diào)整以向用戶呈現(xiàn)文檔的狀態(tài)。由于語(yǔ)言機(jī)制的不同,觀察者設(shè)計(jì)模式在不同的語(yǔ)言中實(shí)現(xiàn)方法也不盡相同。
在MFC的文檔/視圖模式中,每當(dāng)文檔內(nèi)容改變都需要調(diào)用UpdateAllView函數(shù)來(lái)更新視圖,該函數(shù)會(huì)遍歷文檔的每一個(gè)視圖,調(diào)用每個(gè)視圖的更新函數(shù)來(lái)更新視圖,為此文檔須登記每一個(gè)使用該文檔的視圖。C#中觀察者設(shè)計(jì)模式的實(shí)現(xiàn)也可以采用這種方法,但C#提供的代理(delegate)機(jī)制為實(shí)現(xiàn)觀察者模式提供了更好的方法,該方法和MFC中的方法類似 ,只不過(guò)將視圖向文檔注冊(cè)這一行為改變?yōu)闉槲臋n類的代理生成實(shí)例而已,下面看具體實(shí)現(xiàn)方法。
先做如下假定:
1、文檔類為UserData;
2、視圖類為View,實(shí)際應(yīng)用中該View可能是一個(gè)Form,也可能是一個(gè)UserControl,可能有多個(gè)視圖,但每一個(gè)和文檔的對(duì)應(yīng)方式都是相同的;
3、主窗體為MainForm;
參與觀察者模式的三方分別為:發(fā)布者(數(shù)據(jù)/文檔類)、訂閱者(視圖類)以及主窗體(MainForm),下面分別介紹各方如何實(shí)施以配合觀察者模式的實(shí)現(xiàn)!
發(fā)布者:
發(fā)布者的任務(wù)是定義數(shù)據(jù)并在數(shù)據(jù)改變時(shí)通知訂閱者。通知的實(shí)現(xiàn)可以使用普通代理,也可以使用事件,首先在UserData中創(chuàng)建代理和事件,每一個(gè)事件在UserData類相應(yīng)屬性改變時(shí)觸發(fā),看下面的代碼:
public delegate void UserNameChangedEventHander(object sender, EventArgs e); //聲明代理
public event UserNameChangedEventHander NameChanged; //聲明事件
private string m_userName;
public string UserName//定義屬性
{
get
{
return m_userName;
}
set
{
if (m_userName != value)
{
m_userName = value;
NameChanged(this, EventArgs.Empty); //觸發(fā)事件
}
}
}
上述代碼首先聲明了代理,然后聲明了代理對(duì)應(yīng)的事件(事件也算一種特殊的代理),這些代理實(shí)例的生成將在視圖中進(jìn)行,然后在屬性的set函數(shù)中觸發(fā)事件,該事件將在各個(gè)訂閱者中得到響應(yīng)。
訂閱者:
訂閱者的任務(wù)是響應(yīng)發(fā)布者發(fā)布的數(shù)據(jù)改變通知,呈現(xiàn)給用戶實(shí)時(shí)(相對(duì)來(lái)說(shuō))的系統(tǒng)狀態(tài)。
看下面的代碼:
private UserData m_userData = null;
public UserData UserDataObj //定義數(shù)據(jù)(文檔)對(duì)象
{
get
{
return m_userData;
}
set
{
m_userData = (UserData)value; //下面一行添加數(shù)據(jù)對(duì)象事件響應(yīng)函數(shù)
m_userData.NameChanged += new UserData.UserNameChangedEventHander(UserNameChanged);
}
}
private void UserNameChanged(object sender,EventArgs e) //定義數(shù)據(jù)對(duì)象事件響應(yīng)函數(shù)
{
this.tbName.Text = m_userData.UserName;//根據(jù)數(shù)據(jù)對(duì)象更新內(nèi)容
this.Invalidate(); //重繪視圖
}
上述代碼首先在視圖類中定義一數(shù)據(jù)對(duì)象屬性,并在屬性的set函數(shù)中添加對(duì)數(shù)據(jù)對(duì)象所發(fā)布通知的響應(yīng)。接下來(lái)定義了響應(yīng)數(shù)據(jù)對(duì)象通知的函數(shù),在該函數(shù)中更新視圖數(shù)據(jù)并重繪。
主窗體:
主窗體的任務(wù)是定義一個(gè)相當(dāng)于全局的數(shù)據(jù)對(duì)象,將其賦予每個(gè)訂閱該對(duì)象的視圖,并在需要的時(shí)候改變數(shù)據(jù)對(duì)象內(nèi)容。
看下面的代碼:
private UserData m_userData; //發(fā)布者
private View m_view;//訂閱者
private void MainForm_Load(object sender, EventArgs e)
{
m_userData = new UserData(); //生成實(shí)例
m_view = new View();
m_view.UserDataObj = m_userData; //為訂閱者指定發(fā)布者
m_view.Show(); //顯示
m_userData.UserName = "ZPY"; //改變發(fā)布者數(shù)據(jù)
m_view.TopMost = true;
}
在框架窗體類中分別生成發(fā)布者和訂閱者的實(shí)例,然后將發(fā)布者實(shí)例賦值給訂閱者的數(shù)據(jù)對(duì)象屬性,由于C#中類的傳遞默認(rèn)采用引用傳遞的方式,因此在賦值過(guò)程中并不生成臨時(shí)對(duì)象,MainForm中的m_userData和View中的m_userData所指為同一對(duì)象。接下來(lái)在主窗體中改變發(fā)布者數(shù)據(jù),通過(guò)C#的代理(delegate)機(jī)制,訂閱者即能更新自己。
小結(jié)
MFC為開(kāi)發(fā)者搭好了框架,盡管作了許多的開(kāi)發(fā),可能很多人還是不太了解什么是所謂的觀察者模式,C#提供了全開(kāi)放的設(shè)計(jì),可能辛苦些,但不再摸不著頭腦,條理感覺(jué)更清晰些,封裝性感覺(jué)也比MFC好些!
學(xué)習(xí)模式注重精髓而非模板,本文為了便于說(shuō)明假定了三方并對(duì)三方功能進(jìn)行了劃分,實(shí)際應(yīng)用并不拘泥于此。如果情況合適將數(shù)據(jù)(文檔)類設(shè)計(jì)為單件模式也是一種很不錯(cuò)的選擇!總之一句話:掌握精髓,盡情發(fā)揮!
相關(guān)文章
C#?將Excel轉(zhuǎn)為PDF時(shí)自定義表格紙張大小的代碼思路
這篇文章主要介紹了C#?將Excel轉(zhuǎn)為PDF時(shí)自定義表格紙張大小的代碼思路,轉(zhuǎn)換前的頁(yè)面大小設(shè)置為該版本中寫(xiě)入的新功能,在舊版本和免費(fèi)版本中暫不支持,感興趣的朋友跟隨小編一起看看實(shí)例代碼2021-11-11C#實(shí)現(xiàn)Excel合并單元格數(shù)據(jù)導(dǎo)入數(shù)據(jù)集詳解
這篇文章主要為大家詳細(xì)介紹了C#如何實(shí)現(xiàn)Excel合并單元格數(shù)據(jù)導(dǎo)入數(shù)據(jù)集,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-01-01C#面向?qū)ο笤O(shè)計(jì)原則之組合/聚合復(fù)用原則
這篇文章介紹了C#面向?qū)ο笤O(shè)計(jì)原則之組合/聚合復(fù)用原則,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03WinForm項(xiàng)目開(kāi)發(fā)中Excel用法實(shí)例解析
這篇文章主要介紹了WinForm項(xiàng)目開(kāi)發(fā)中Excel用法,非常實(shí)用,需要的朋友可以參考下2014-08-08C#中數(shù)組、ArrayList、List、Dictionary的用法與區(qū)別淺析(存取數(shù)據(jù))
在工作中經(jīng)常遇到C#數(shù)組、ArrayList、List、Dictionary存取數(shù)據(jù),但是該選擇哪種類型進(jìn)行存儲(chǔ)數(shù)據(jù)呢?很迷茫,今天小編抽空給大家整理下這方面的內(nèi)容,需要的朋友參考下吧2017-02-02使用數(shù)字簽名實(shí)現(xiàn)數(shù)據(jù)庫(kù)記錄防篡改(Java實(shí)現(xiàn))
本文主要介紹了Java中使用數(shù)字簽名實(shí)現(xiàn)數(shù)據(jù)庫(kù)記錄防篡改的方法與步驟。具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-01-01混合語(yǔ)言編程—C#使用原生的Directx和OpenGL繪圖的方法
本文要說(shuō)的是混合C#和C/C++語(yǔ)言編程,在C#的Winform和WPF下使用原生的Direct和OpenGL進(jìn)行繪圖2013-09-09asp.net新聞列表生成靜態(tài)頁(yè)之批量和單頁(yè)生成
web程序的高訪問(wèn)量、大數(shù)據(jù)量、高效的用戶體驗(yàn)度,使靜態(tài)頁(yè)技術(shù)在越來(lái)越多的網(wǎng)站上發(fā)揮作用。這篇文章主要介紹asp.net新聞列表生成靜態(tài)頁(yè)之批量和單頁(yè)生成,有需要的朋友可以參考下2015-08-08