物件導向設計建議我們將系統切割成一群互相協同合作的物件,以便提高個別物件的可重用性。在這種做法之下,有些物件的狀態與行為會相依於其他物件,並需要因為其他物件的狀態改變而做出反應。
觀察者模式可能會導致內存洩漏,遺失Listener的問題,因為在基本實現它需要顯式註冊和註銷明確,如在Dispose模式,因為主體持有以觀察員強引用,保持它們活著。這可以由對象保持到觀察者弱引用被防止。
如何得知物件狀態改變?
物件狀態異動的頻率無法預料。
為了達到狀態一致性而將物件緊密耦合在一起,將會降低物件的重複使用性。
不能限制資料相依物件的個數,可能有任意數量的物件都有興趣想要知道物件的狀態。
當某物件狀態改變時,其相依物件必須適時自動被通知。
Solution:定義一對多的相依關係,當物件改變狀態的時候,讓它的相依者可以自動收到更新通知。此模式規範了兩種角色(介面):擁有狀態的物件(Subject)以及它的相依者(Observer),也就是想得知Subject狀態改變的物件們(Observer可以有一個以上)。
Subject角色允許Observer向它註冊或取消註冊(attach、detach),以便獲得或解除狀態改變的通知。當Subject要將狀態改變的事實通知Observer的時候,Subject會呼叫自己的notify方法通知Observer。Observer角色需實作update方法,以便讓Subject在notify方法中可以通知到各種不同類別的Observer(ConcreteObserver)。
下面範例的結構代碼演示了其註冊的對象通知和更新的狀態變化Observer模式。
using System;
using System.Collections.Generic;
namespace
DoFactory.GangOfFour.Observer.Structural
{
/// <summary>
/// Observer Design Pattern.
/// </summary>
class MainApp
{
/// <summary>
/// 程式進入控制台應用程序的起始。
/// </summary>
static void Main()
{
// 配置 Observer pattern
ConcreteSubject s = new ConcreteSubject();
s.Attach(new ConcreteObserver(s, "X"));
s.Attach(new ConcreteObserver(s, "Y"));
s.Attach(new ConcreteObserver(s, "Z"));
// 改變主題和通知觀察者群
s.SubjectState = "ABC";
s.Notify();
// 等待User
Console.ReadKey();
}
}
/// <summary>
/// 'Subject' abstract 的 class
/// </summary>
abstract class Subject
{
private List<Observer> _observers = new List<Observer>();
public void Attach(Observer observer)
{
_observers.Add(observer);
}
public void Detach(Observer observer)
{
_observers.Remove(observer);
}
public void Notify()
{
foreach (Observer o in _observers)
{
o.Update();
}
}
}
/// <summary>
/// 'ConcreteSubject' 的 class
/// </summary>
class ConcreteSubject : Subject
{
private string _subjectState;
// subject state的存取
public string SubjectState
{
get { return _subjectState; }
set { _subjectState = value; }
}
}
/// <summary>
/// 'Observer' 的 abstract class
/// </summary>
abstract class Observer
{
public abstract void Update();
}
/// <summary>
/// 'ConcreteObserver' 的 class
/// </summary>
class ConcreteObserver : Observer
{
private string _name;
private string _observerState;
private ConcreteSubject _subject;
// 建構函數
public ConcreteObserver(
ConcreteSubject subject, string name)
{
this._subject = subject;
this._name = name;
}
public override void Update()
{
_observerState = _subject.SubjectState;
Console.WriteLine("Observer {0}'s new state is {1}",
_name, _observerState);
}
// subject存取
public ConcreteSubject Subject
{
get { return _subject; }
set { _subject = value; }
}
}
}
下面的程式範例用真實情況來呈現,程式碼演示中,以Microsoft註冊的投資者被告知每一個股票的價值發生變化時觀察者模式。
using System;
using System.Collections.Generic;
namespace
DoFactory.GangOfFour.Observer.RealWorld
{
/// <summary>
/// Observer Design Pattern.
/// </summary>
class MainApp
{
/// <summary>
/// 程式進入控制台應用程序的起始。
/// </summary>
static void Main()
{
// 產生 Microsoft stock 和 附上 投資者群
Microsoft microsoft = new Microsoft("Microsoft", 120.00);
microsoft.Attach(new Investor("Sorros"));
microsoft.Attach(new Investor("Berkshire"));
// 價格波動會通知投資者
microsoft.Price = 120.10;
microsoft.Price = 121.00;
microsoft.Price = 120.50;
microsoft.Price = 120.75;
// 等待User
Console.ReadKey();
}
}
/// <summary>
/// The 'Subject' abstract class
/// </summary>
abstract class Stock
{
private string _symbol;
private double _price;
private List<IInvestor> _investors = new List<IInvestor>();
// 建構函數
public Stock(string symbol, double price)
{
this._symbol = symbol;
this._price = price;
}
public void Attach(IInvestor investor)
{
_investors.Add(investor);
}
public void Detach(IInvestor investor)
{
_investors.Remove(investor);
}
public void Notify()
{
foreach (IInvestor investor in _investors)
{
investor.Update(this);
}
Console.WriteLine("");
}
// price存取
public double Price
{
get { return _price; }
set
{
if (_price != value)
{
_price = value;
Notify();
}
}
}
// 取得 symbol
public string Symbol
{
get { return _symbol; }
}
}
/// <summary>
/// 'ConcreteSubject' 的 class
/// </summary>
class Microsoft : Stock
{
// 建構函數
public Micrososft(string symbol, double price)
: base(symbol, price)
{
}
}
/// <summary>
/// 'Observer' 的 interface
/// </summary>
interface IInvestor
{
void Update(Stock stock);
}
/// <summary>
/// 'ConcreteObserver' class
/// </summary>
class Investor : IInvestor
{
private string _name;
private Stock _stock;
// 建構函數
public Investor(string name)
{
this._name = name;
}
public void Update(Stock stock)
{
Console.WriteLine("Notified {0} of {1}'s " +
"change to {2:C}", _name, stock.Symbol,
stock.Price);
}
// 變數 stock 的存取
public Stock Stock
{
get { return _stock; }
set { _stock = value; }
}
}
}
-雲遊山水為知已逍遙一生而忘齡- 電腦神手
沒有留言:
張貼留言