認識觀察者模式(設計模式)[轉載]

認識觀察者模式(設計模式)
-、開題
 
設計模式就好比天屠龍記裏的兵法書武穆遺書哦,看了設計模式就會驚呼,軟件設計中你已經在不知不覺使用,或者碰到了但尚未解決的,或者至今還沒意識到的問題,上邊都有了經驗總結;學習了想必事半功倍,具體還要在實踐中不斷嘗試。
       設計模式一書已經被存儲到大家知識庫裏並且逐步重視起來基礎上,這次我們來認識和討論一下觀察者模式,
    觀察者模式:
    這裏不多說,空間留給大家,可以從觀察者模式的背景、意圖、適用範圍、優點、缺點、最好結合例子談談。
 
二討論
Charley Chen跟貼:
實現觀察者模式有很多形式,比較直觀的一種是使用一種註冊——通知——撤銷註冊的形式。下面的三個圖詳細的描述了這樣一種過程:
1:觀察者(Observer)將自己註冊到被觀察對象(Subject)中,被觀察對象將觀察者存放在一個容器(Container)裏。


2:被觀察對象發生了某種變化(如圖中的AskPriceChanged),從容器中得到所有註冊過的觀察者,將變化通知觀察者。
 
此主題相關圖片如下:

3:觀察者告訴被觀察者要撤銷觀察,被觀察者從容器中將觀察者去除。
 
此主題相關圖片如下:

觀察者將自己註冊到被觀察者的容器中時,被觀察者不應該過問觀察者的具體類型,而是應該使用觀察者的接口。這樣的優點是:假定程序中還有別的觀察者,那麼只要這個觀察者也是相同的接口實現即可。一個被觀察者可以對應多個觀察者,當被觀察者發生變化的時候,他可以將消息一一通知給所有的觀察者。基於接口,而不是具體的實現——這一點爲程序提供了更大的靈活性。
(一)通過別名從不同角度來理解
    發佈-訂閱(Publish/Subscribe)模式:狀態變更的動作基本上是由被觀察者發出,因此這個別名比觀察者更加貼切 
    模型-視圖(Model/View)模式:該模式重要的應用場合,也是最爲適合的應用場合,常常作爲示例
    -監聽器(Source/Listener)模式:?
    從屬者(Dependents)模式:?
(二)C#.NET中可以通過DelegateEvent機制簡化Observer模式
     詳見下,對於.NET還不是很熟,例子摘錄自C#設計模式
(三)Java中內建了類java.util.Observerable和接口java.util.Observer,是對Observer的簡單實現
       詳見下,鏈了一篇介紹
四、 C#中的DelegateEvent
實際上在C#中實現Observer模式沒有這麼辛苦,.NET中提供了DelegateEvent機制,我們可以利用這種機制簡化Observer模式。關於DelegateEvent的使用方法請參考相關文檔。改進後的Observer模式實現如下:
// Observer pattern -- Structural example  
using System;

//Delegate
delegate void UpdateDelegate(); 

//Subject
class Subject
{
  
public event UpdateDelegate UpdateHandler;
  
  
// Methods
  public void Attach( UpdateDelegate ud )
  
{
    UpdateHandler += ud;
  }

  
public void Detach( UpdateDelegate ud )
  
{
    UpdateHandler -= ud;
  }
  
  
public void Notify()
  
{
    
if(UpdateHandler != null) UpdateHandler();
  }

}

//ConcreteSubject
class ConcreteSubject : Subject
{
  
// Fields
  private string subjectState;

  
// Properties
  public string SubjectState
  
{
    
getreturn subjectState; }
    
set{ subjectState = value; }
  }
}

// "ConcreteObserver"
class ConcreteObserver
{
  
// Fields
  private string name;
  
private string observerState;
  
private ConcreteSubject subject;

  
// Constructors
  public ConcreteObserver( ConcreteSubject subject,  
    
string name )
  
{
    
this.subject = subject;
    
this.name = name;
  }

  
// Methods
  public void Update()
  
{
    observerState = subject.SubjectState;
    Console.WriteLine( "Observer {0}'s new state is {1}",
      name, observerState );
  }

  
// Properties
  public ConcreteSubject Subject
  
{
    
get return subject; }
    
set { subject = value; }
  }
}

// "ConcreteObserver"
class AnotherObserver
{
  
// Methods
  public void Show()
  
{
    Console.WriteLine("AnotherObserver got an Notification!");
  }
}

public class Client

  
public static void Main(string[] args)
  

    ConcreteSubject s = 
new ConcreteSubject();
    ConcreteObserver o1 = 
new ConcreteObserver(s, "1");
    ConcreteObserver o2 = 
new ConcreteObserver(s, "2");
    AnotherObserver o3 = 
new AnotherObserver();
    
    s.Attach(
new UpdateDelegate(o1.Update));
    s.Attach(
new UpdateDelegate(o2.Update));
    s.Attach(
new UpdateDelegate(o3.Show));

    s.SubjectState = "ABC";
    s.Notify();

    Console.WriteLine("--------------------------");
    s.Detach(
new UpdateDelegate(o1.Update));

    s.SubjectState = "DEF";
    s.Notify();
  }
}
其中,關鍵的代碼如下:
delegate void UpdateDelegate(); 
定義一個Delegate,用來規範函數結構。不管是ConcreteObserver類的Update方法還是AnotherObserver類的Show方法都符合該Delegate。這不象用Observer接口來規範必須使用Update方法那麼嚴格。只要符合Delegate所指定的方法結構的方法都可以在後面被事件所處理。
public event UpdateDelegate UpdateHandler;
定義一個事件,一旦觸發,可以調用一組符合UpdateDelegate規範的方法。
  public void Attach( UpdateDelegate ud )
  
{
    UpdateHandler += ud;
  }
訂閱事件。只要是一個滿足UpdateDelegate的方法,就可以進行訂閱操作(如下所示)。
    s.Attach(new UpdateDelegate(o1.Update));
    s.Attach(
new UpdateDelegate(o2.Update));
    s.Attach(
new UpdateDelegate(o3.Show));
Notify方法中:
  public void Notify()
  
{
    
if(UpdateHandler != null) UpdateHandler();
  }
只要UpdateHandler != null(表示有訂閱者),就可以觸發事件(UpdateHandler()),所有的訂閱者便會接到通知。
關於事件的使用,有一點還是值得注意:
一般我們認爲:全部的事件處理器都應該返回void並接受兩個參數:第一個參數爲一個對象,它代表產生事件的對象;第二個參數是從System.EventArgs中派生而來的類的對象。EventArgs是事件數據的基類,並代表了事件的細節。
摘自範亮的舉例:
  ConcreteSubject s = new ConcreteSubject();
  ConcreteObserver o1 = new ConcreteObserver(s, "1");
  ConcreteObserver o2 = 
new ConcreteObserver(s, "2");
  AnotherObserver o3 = 
new AnotherObserver();
    
  s.Attach(
new UpdateDelegate(o1.Update));
  s.Attach(
new UpdateDelegate(o2.Update));
  s.Attach(
new UpdateDelegate(o3.Show));
如果o1,o2 ,o3 有統一的接口去響應主題的UpdateHandler事件,那就可以去實現(繼承)同一接口IObserver,編程會變的更加簡練,當然這是錦上天花的事,例子已經能夠說明觀察者模式的思想.

下面我就隨便談談觀察者模式背景、意圖、適用範圍、優點、缺點...
先談背景:
還是舉個列子
A

Class A
{
    function funA()
    { 
     }
}
B

Class B
{
    function funB()
    {
       
     }
}
C

Class C
{
    function funC()
    { 
        A.funA();
        B.funB();
     }
}
如果觀察者模式可以總結成下面的應用:
定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時, 所有依賴於它的對象都得到通知並被自動更新。(摘自新版設計模式手冊[C#].pdf)
那上邊的例子是否看成觀察者模式的最原始的一種呢?
優點:
是實現了表示層和數據邏輯層的分離,並定義了穩定的更新消息傳遞機制,類別清晰,並抽象了更新接口,使得可以有各種各樣不同的表示層
缺點:
如果1NN很大時,花銷較大。
 
三:總結
觀察者模式的討論先告一段落,主要還要在實踐中慢慢體會.
1.
在分層設計中,同層類的交互,就是橫向調用比如在Bussiness層,如果有當一個對象的狀態發生改變時, 所有依賴於它的對象都得到通知並被自動更新的,我們可以嘗試觀察者模式,從而降低類之間的耦合性,我
寫的例子是一種強耦合的,一定程度上講破壞了類的高內聚低耦合及封閉原則.
2.C#
編程中Delegate-Event就是很好的觀察者模式原形,我們要多用它.

以下摘自C#設計模式一書,講的蠻好:
觀察者模式的效果有以下幾個優點:
1)觀察者模式在被觀察者和觀察者之間建立一個抽象的耦合。被觀察者角色所知道的只是一個具體現察者聚集,每一個具體現察者都符合一個抽象觀察者的接口。被觀察者並不認識任何一個具體觀察者,它只知道它們都有一個共同的接口。由於被觀察者和觀察者沒有緊密地耦合在一起,因此它們可以屬於不同的抽象化層次。
2)觀察者模式支持廣播通信。被觀察者會向所有的登記過的觀察者發出通知。
觀察者模式有下面的一些缺點:
1)如果一個被觀察者對象有很多直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。
2如果在被觀察者之間有循環依賴的話,被觀察者會觸發它們之間進行循環調用,導致系統崩潰。在使用觀察考模式時要特別注意這一點。
3)如果對觀察者的通知是通過另外的線程進行異步投遞的話,系統必須保證投遞是以自恰的方式進行的。
4)雖然觀察者模式可以隨時使觀察者知道所觀察的對象發生了變化,但是觀察者模式沒有相應的機制使觀察者知道所觀察的對象是怎麼發生變化的。
 
 
此主題相關圖片如下:
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章