此篇簡單介紹觀察者模式。
關注在 :
- 如何降低 "主題"與"觀察者" 之間的耦合
- 聚焦於高階模組與低階模組所依賴的抽象,而不是實作類別
要點 :
- 適度的使用介面
- 將演算法中可能的變化獨立出來
類別圖 :
主題 - 抽象類 :
using System;
using System.Collections.Generic;
using ObserverPatternSample.Observer;
namespace ObserverPatternSample.Subject
{
#region 所有主題都應繼此介面(Interface)
/// <summary>
/// 所有主題都應繼此介面(Interface)
/// </summary>
internal interface ISubject
{
/// <summary>
/// 新增觀察者
/// </summary>
/// <param name="observer">抽象的觀察者,不需知道其具體類別為何、實作的細節...</param>
void attach(Observer.IObserver observer);
/// <summary>
/// 移除觀察者
/// </summary>
/// <param name="observer">抽象的觀察者,不需知道其具體類別為何、實作的細節...</param>
void detach(Observer.IObserver observer);
/// <summary>
/// 當主題發生變化時,通知觀察者
/// </summary>
void notifyObserver();
/// <summary>
/// 取得主題狀態
/// </summary>
/// <returns>回傳當前主題狀態</returns>
string getState();
}
#endregion
}
主題 - 實作類 :
using System;
using System.Collections.Generic;
using ObserverPatternSample.Observer;
using System.Linq;
namespace ObserverPatternSample.Subject
{
#region ConcreteSubject 主題實作類
/// <summary>
/// 儲存向該主題(ConcretSubjectA)註冊的觀察者集合的參考
/// </summary>
sealed internal class ConcretSubjectA : ISubject
{
/// <summary>
/// 此為主題的內容
/// </summary>
private string _subjectContext = String.Empty;
/// <summary>
/// 使用List<>儲存向該主題註冊的觀察者
/// </summary>
private List<Observer.IObserver> _observers = null;
public ConcretSubjectA()
{
// 初始化 _observers
this._observers = new List<Observer.IObserver>();
}
/// <summary>
/// 取得主題狀態
/// </summary>
/// <returns>回傳當前主題狀態</returns>
public string getState()
{
if (String.IsNullOrEmpty(this._subjectContext)) return "主題內容未設定";
return this._subjectContext;
}
/// <summary>
/// 設定主題內容
/// </summary>
/// <param name="stateText">輸入內容字串</param>
public void setSubjectContext(string setText)
{
this._subjectContext = "設定主題內容: " + setText;
Console.WriteLine(this.GetType().Name+" 變更狀態");
Console.WriteLine(this._subjectContext);
Console.WriteLine();
// Note : 也可以在更新狀態後直接對各個使用者做更新
}
/// <summary>
/// 新增觀察者
/// </summary>
/// <param name="observer">抽象的觀察者,不需知道其具體類別為何、實作的細節...</param>
public void attach(IObserver observer)
{
if (observer != null) this._observers.Add(observer);
}
/// <summary>
/// 移除觀察者
/// </summary>
/// <param name="observer">抽象的觀察者,不需知道其具體類別為何、實作的細節...</param>
public void detach(IObserver observer)
{
if (observer != null)
if (this._observers.Contains(observer)) this._observers.Remove(observer);
}
/// <summary>
/// 當主題發生變化時,通知觀察者
/// </summary>
public void notifyObserver()
{
Console.WriteLine("----------------------------------------");
Console.WriteLine("| " + this.GetType().Name + " 通知給所有觀察者更新 |");
Console.WriteLine("----------------------------------------");
Console.WriteLine(" ▼");
Console.WriteLine();
// 使用Lamdba 原理大致上就是使用forEach探索List的集合後,使用指定的方法
if (this._observers.Any()) this._observers.ForEach(x => x.update());
else Console.WriteLine("無觀察者註冊該主題");
Console.WriteLine();
}
}
#endregion
}
觀察者 - 抽象類 :
using System;
namespace ObserverPatternSample.Observer
{
#region 所有觀察者都應繼承此介面(interface)
/// <summary>
/// 所有觀察者都應繼承此介面(interface)
/// </summary>
interface IObserver
{
// int myProperty { get; }
/// <summary>
/// 供 主題 狀態改變時予以通知每個有註冊的 觀察者
/// </summary>
void update();
}
#endregion
}
觀察者 - 實作類 :
using System;
namespace ObserverPatternSample.Observer
{
#region ConcreteObserver 觀察者實作類
/// <summary>
/// 實例化各個觀察者 且實作 IObserver 的方法
/// </summary>
sealed internal class ConcreteObserverA : IObserver
{
// public int myProperty{
// get{
// throw new NotImplementedException();
// }
// private set{
// }
// }
/// <summary>
/// 此為從主題更新的內容
/// </summary>
private string _observerContext = String.Empty;
/// <summary>
/// 儲存對應主題的參考
/// </summary>
private Subject.ISubject _concretSubject = null;
/// <summary>
/// 初始化需要有主題的引數傳入
/// </summary>
/// <param name="concreteSubject"> 請傳入主題(Subject) </param>
public ConcreteObserverA(Subject.ISubject concreteSubject)
{
this._concretSubject = concreteSubject;
}
/// <summary>
/// 從主題得知改變內容後更新自身內容且實作
/// </summary>
public void update()
{
// 從主題得知改變內容後更新自身內容
this._observerContext = this._concretSubject.getState();
Console.WriteLine(this.GetType().Name+" 收到通知");
Console.WriteLine("觀察者更新內容: "+this._observerContext);
Console.WriteLine();
}
}
#endregion
}
用戶端 :
using System;
namespace ObserverPatternSample
{
class Program
{
static void Main(string[] args)
{
// 實例主題
Subject.ConcretSubjectA subject1 = new Subject.ConcretSubjectA();
// 設置主題內容
subject1.setSubjectContext($"初始內容: 第一次更動內容");
// 使用抽象方式實例觀察者並初始化
Observer.IObserver observer1 = new Observer.ConcreteObserverA(subject1);
Observer.IObserver observer2 = new Observer.ConcreteObserverA(subject1);
Observer.IObserver observer3 = new Observer.ConcreteObserverA(subject1);
// 觀察者訂閱主題並註冊
subject1.attach(observer1);
subject1.attach(observer2);
subject1.attach(observer3);
// 變更主題內容
subject1.setSubjectContext("變更內容: 第二次更動內容");
// 通知已註冊的觀察者並更新
subject1.notifyObserver();
Console.WriteLine("觀察者取消註冊");
// 觀察者取消註冊
subject1.detach(observer1);
// 主題更新
subject1.notifyObserver();
Console.Read();
}
}
}
結果顯示 :
多多指教!! 歡迎交流!!
你不知道自己不知道,那你會以為你知道