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

.NET 設計模式精解(十九):觀察者模式——從緊耦合到鬆耦合的重構之路

     編輯:關於.NET

在軟體建構過程中,常需建立「通知依賴關係」:目標物件狀態變更時,自動通知所有依賴的觀察者物件。若依賴過於緊密,系統難以應對變化。觀察者模式透過面向物件抽象,將此依賴弱化為穩定的鬆耦合關係,是實現發布-訂閱機制的經典模式。

一、模式意圖與結構

意圖:定義物件間一對多依賴,當一個物件狀態改變,所有依賴物件自動獲得通知並更新。 (GoF《設計模式》)

結構圖:

觀察者模式結構圖

二、生活實例:拍賣會

拍賣師 (目標) 觀察投標人 (觀察者) 舉牌報價,每次新報價改變當前價格並廣播給所有投標人:

拍賣會觀察者模式示意

三、漸進式重構演示:從緊耦合到觀察者模式

階段 1:直接耦合 (反面教材)

public class Microsoft {  // 目標
    private Investor _investor;  // 直接依賴具體觀察者
    public void Update() => _investor.SendData(this);
}
public class Investor {  // 觀察者
    public void SendData(Microsoft ms) => Console.WriteLine(...);
}

缺點:雙向依賴、新增觀察者類型 (如 Mobile) 需修改目標類別,違反開放-封閉原則。

階段 2:抽象觀察者介面

public interface IObserver { void SendData(Microsoft ms); }
public class Investor : IObserver { ... }
public class Microsoft {
    private IObserver _investor;  // 依賴抽象
    public void Update() => _investor.SendData(this);
}

進步:目標不再依賴具體觀察者,但仍只能單一觀察者,新增類型仍需修改目標。

階段 3:觀察者集合 (雛形)

public class Microsoft {
    private List<IObserver> _observers = new List<IObserver>();
    public void AddObserver(IObserver o) => _observers.Add(o);
    public void RemoveObserver(IObserver o) => _observers.Remove(o);
    public void Update() => _observers.ForEach(o => o.SendData(this));
}

支援多觀察者,但觀察者仍依賴具體目標 Microsoft。

階段 4:抽象目標基類 (完整觀察者模式)

public abstract class Stock {
    private List<IObserver> _observers = new List<IObserver>();
    public void AddObserver(IObserver o) => _observers.Add(o);
    public void RemoveObserver(IObserver o) => _observers.Remove(o);
    public void Update() => _observers.ForEach(o => o.SendData(this));
    public string Symbol { get; }
    public double Price { get; }
}
public class Microsoft : Stock { ... }
public interface IObserver { void SendData(Stock stock); }
public class Investor : IObserver { ... }

達成:目標與觀察者雙向解耦,符合開放-封閉原則,系統可維護性與擴充性大幅提升。

完整觀察者模式結構

四、推模式 vs 拉模式

模式機制優點缺點
推模式通知時帶參數傳遞完整資訊觀察者被動接收,與目標鬆耦合資訊可能冗餘;參數變更影響所有觀察者
拉模式通知無參數,觀察者主動向目標取回所需資訊按需取用,資訊精確觀察者需持有目標參考,耦合稍強

文中提供完整拉模式程式碼範例 (略)。

五、.NET 事件與委派實作觀察者模式

C# 事件/委派是更優雅的原生實作:委派扮演抽象觀察者介面,事件提供者為目標物件,耦合度更低。

public delegate void NotifyEventHandler(object sender);
public class Stock {
    public event NotifyEventHandler NotifyEvent;
    public void Update() => OnNotifyChange();
    protected virtual void OnNotifyChange() => NotifyEvent?.Invoke(this);
}
public class Investor {
    public void SendData(object obj) { if (obj is Stock s) Console.WriteLine(...); }
}
// 用戶端
var stock = new Stock("Microsoft", 120);
stock.NotifyEvent += new Investor("Jom").SendData;
stock.Update();

六、效果、實現要點與適用性

  • 鬆耦合:目標不知觀察者細節,觀察者自行決定訂閱。
  • 自動傳播:通知自動廣播至所有訂閱者。
  • C# 事件/委派:比介面更鬆耦合的原生實作。
  • 適用場景:抽象模型雙面依賴、一對多通知、目標不應假定觀察者具體類型。

七、總結

觀察者模式將一對多通知依賴轉化為穩定的鬆耦合關係,大幅提升系統可維護性與擴充性,完美契合開放-封閉原則。.NET 開發者應優先考慮事件/委派原生實作。

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