首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

.NET 設計模式實戰:觀察者模式從生活案例到事件委派演進

     編輯:關於.NET

以生活故事引入觀察者模式核心思想,從經典 GoF 實作演進到 .NET 原生 事件-委派 機制,展現設計模式解決「解耦」與「封裝變化」的本質。

一、故事場景:小雪與追求者們

小雪(被觀察者)有多位追求者(觀察者),隊伍動態變動。傳統模式:追求者不斷輪詢詢問位置,小雪不斷回應,雙方精力耗盡。Nokia 方案:手機內建訂閱清單,位置變化時自動發送簡訊通知所有訂閱者。雙方恢復正常生活,唯有狀態變化時才通訊。

二、觀察者模式定義

定義物件間一對多依賴,當一物件狀態改變,所有依賴物件自動收到通知並有機會回應。執行期可動態增減觀察者。

三、經典 GoF 實作 (C#)

1. 觀察者介面


public interface IBoy {
    void Show(string address); // 統一通知協定
}

2. 具體觀察者


public class BoyA : IBoy {
    public void Show(string address) { Console.WriteLine("A:" + address); /* 假設轉韓文 */ }
}
public class BoyB : IBoy {
    public void Show(string address) { Console.WriteLine("B:" + address); /* 假設轉英文 */ }
}

3. 被觀察者 (GPRS 手機)


public class GPRSMobile {
    private List<IBoy> boys = new List<IBoy>();
    private string address = "";

    public void AddBoy(IBoy b) { boys.Add(b); }
    public void RemoveBoy(IBoy b) { boys.Remove(b); }

    private void Notify(string address) {
        for (int i = 0; i < boys.Count; i++) boys[i].Show(address);
    }

    public void OnAddressChanged(string newAddress) { Notify(newAddress); }
}

4. 兩大規律

  1. 被觀察者持有觀察者清單 (List<IBoy>)
  2. 所有觀察者實作統一介面 (IBoy)

5. 優勢

  • 被觀察者僅依賴抽象介面,新增觀察者零修改
  • 觀察者實作細節對被觀察者透明
  • 符合「面向介面程式設計」與「依賴倒置原則」

四、現實對應:股票行情通知服務

投資人訂閱關注股票,價格變動時簡訊推送,無需守著交易大廳或電腦,正常工作生活不受干擾。

五、.NET 原生實作:事件-委派

.NET 以 委派簽名 取代介面,事件 封裝發布/訂閱機制,型別安全 (委派含參數簽名,避免 C 風格函數指標的執行期錯誤)。

範例:數字循環觸發處理

傳統寫法 (高耦合):迴圈與處理邏輯混雜,未知處理方式難擴充。


for (int i = 0; i <= 100; i++) if (i == input) { Console.WriteLine(i); MessageBox.Show(i.ToString()); /* 其他處理 */ }

.NET 事件-委派寫法 (低耦合):


public delegate void NumberEventHandler(object sender, NumberEventArgs e);

public class NumberEventArgs : EventArgs {
    public int Number { get; set; }
    public NumberEventArgs(int number) { Number = number; }
}

public class Subject {
    public event NumberEventHandler NumberReached;
    public void DoWithLoop(int number) {
        for (int i = 0; i <= 100; i++)
            if (i == number) { OnNumberReached(new NumberEventArgs(i)); }
    }
    protected virtual void OnNumberReached(NumberEventArgs e) {
        if (NumberReached != null) NumberReached(this, e);
    }
}

public class MainProgram {
    public static void Main() {
        Subject s = new Subject();
        s.NumberReached += new NumberEventHandler(msgbox_NumberReached); // 靜態方法
        MainProgram mp = new MainProgram();
        s.NumberReached += new NumberEventHandler(mp.console_NumberReached); // 實例方法
        s.DoWithLoop(input);
    }
    void console_NumberReached(object sender, NumberEventArgs e) { Console.WriteLine(e.Number); }
    static void msgbox_NumberReached(object sender, NumberEventArgs e) { MessageBox.Show(e.Number.ToString()); }
}

關鍵差異:觸發點 (OnNumberReached) 與處理邏輯 (console_NumberReached, msgbox_NumberReached) 完全分離,迴圈不知有多少處理者,擴充零修改觸發端。

六、總結與思考

設計模式核心在解決解耦與封裝變化,服務於可維護性、可擴充性、靈活性。學習重點不在背誦原型,而在理解適用場景與目的,內化為自身設計直覺。

「學習數學非背誦定理,而是循歷史理解前人如何分析解決問題,學到『漁』而非只求『魚』」

另一視角:初學者雖知原則卻不知如何落地專案,或需大量實踐 (量變) 促成質變,先不論過度設計,寫到一定程度自然領悟精髓。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved