觀察者模式的概念:git
定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,它的全部依賴者都會收到通知並更新。c#
今天咱們就學着用一下這個觀察者模式,先想象下這個場景:當一個窗體(主窗體)內的值發生變化時,另外幾個窗體內的值也會發生相應的變化。這個最簡單的實現方式是,在子窗體類內建立一個公共方法,在主窗體內建立子窗體的實例。當值發生變化時調用子窗體的公共方法。還有一種簡單方法是用靜態屬性,至關於全局變量,這個只能針對小型應用。第一種有一個缺點,當增長子窗體時,咱們須要更改主窗體類的代碼。沒法實現動態地增長刪除對子窗體類中值的更新。這時能夠引入觀察者模式。
咱們先建立一個winform項目,添加FrmMain,FrmSub1,FrmSub2三個窗體,每一個窗體都添加一個textbox文本框,實現當主窗體中的文本框值發生變化時,其餘兩個子窗體的值也會發生相應的變化。這裏咱們參考《Head First設計模式》,先設計發佈者和訂閱者(這裏我感受仍是叫比較順口)。代碼以下:設計模式
public interface ISubject { /// <summary> /// 註冊訂閱者 /// </summary> /// <param name="observer"></param> void RegisterObserver(IObserver observer); /// <summary> /// 刪除訂閱者 /// </summary> /// <param name="observer"></param> void RemoveObserver(IObserver observer); /// <summary> /// 通知訂閱者 /// </summary> void NotifyObservers(); } public interface IObserver { /// <summary> /// 觀察者對發佈者的響應方法 /// </summary> /// <param name="message"></param> void Update(string message); }
修改FrmMain類,使其實現ISubject接口,再給文本框增長change事件,代碼以下:this
public partial class FrmMain : Form, ISubject { private string Message { get; set; } private List<IObserver> _observers = new List<IObserver>(); public FrmMain() { InitializeComponent(); } private void FrmMain_Load(object sender, EventArgs e) { FrmSub1 frmSub1 = new FrmSub1(this); FrmSub2 frmSub2 = new FrmSub2(this); frmSub1.Show(); frmSub2.Show(); } /// <summary> /// 文本框改變事件,調用通知訂閱者方法 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void txtMain_TextChanged(object sender, EventArgs e) { this.Message = txtMain.Text; NotifyObservers(); } /// <summary> /// 註冊訂閱者 /// </summary> /// <param name="observer"></param> public void RegisterObserver(IObserver observer) { _observers.Add(observer); } /// <summary> /// 移除訂閱者 /// </summary> /// <param name="observer"></param> public void RemoveObserver(IObserver observer) { _observers.Remove(observer); } /// <summary> /// 通知訂閱者 /// </summary> public void NotifyObservers() { foreach (IObserver observer in _observers) { observer.Update(Message); } } }
子窗體類(訂閱者)的代碼以下:設計
public partial class FrmSub1 : Form,IObserver { public FrmSub1(ISubject subject) { InitializeComponent(); //註冊 subject.RegisterObserver(this); } //當發佈者事件發生時,訂閱者須要執行的方法 public void Update(string message,bool isShown) { txtSub.Text = message; } }
上面能夠看到FrmMain又依賴了FrmSub1和FrmSub2,其實這是須要另外一個方法Show()。不過爲表現出高層不依賴底層,咱們改掉代碼,在NotifyObservers(bool isShown)方法中加入對子窗體是否show的判斷,從而實現了避免FrmMain依賴FrmSub1,FrmSub2
FrmMain類的代碼:code
public partial class FrmMain : Form, ISubject { private string Message { get; set; } private List<IObserver> _observers = new List<IObserver>(); public FrmMain() { InitializeComponent(); } private void FrmMain_Load(object sender, EventArgs e) { //FrmSub1 frmSub1 = new FrmSub1(this); //FrmSub2 frmSub2 = new FrmSub2(this); //frmSub1.Show(); //frmSub2.Show(); //這裏咱們使用了方法,擺脫了對子窗體的直接依賴 NotifyObservers(false); } /// <summary> /// 文本框改變事件,調用通知訂閱者方法 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void txtMain_TextChanged(object sender, EventArgs e) { this.Message = txtMain.Text; NotifyObservers(true); } /// <summary> /// 註冊訂閱者 /// </summary> /// <param name="observer"></param> public void RegisterObserver(IObserver observer) { _observers.Add(observer); } /// <summary> /// 移除訂閱者 /// </summary> /// <param name="observer"></param> public void RemoveObserver(IObserver observer) { _observers.Remove(observer); } /// <summary> /// 通知訂閱者 /// </summary> public void NotifyObservers(bool isShown) { foreach (IObserver observer in _observers) { observer.Update(Message,isShown); } } }
子窗體類的代碼:orm
public partial class FrmSub1 : Form,IObserver { public FrmSub1(ISubject subject) { InitializeComponent(); subject.RegisterObserver(this); } //這裏對Update作了改變,isShown爲true,表示事件觸發時子窗體已經顯示 public void Update(string message,bool isShown) { if (isShown) { txtSub.Text = message; } else { this.Show(); } } }
[STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); FrmMain frmMain = new FrmMain(); FrmSub1 frmSub1 = new FrmSub1(frmMain); FrmSub2 frmSub2 = new FrmSub2(frmMain); Application.Run(frmMain); }
完整源代碼參考:https://gitee.com/Alexander360/ProDotnetDesignPatternFramework45server