2013-12-12 45 views
3

我正在閱讀(奇妙)書籍Head First Design Patterns,需要對觀察者模式進行一些說明。下面的一小段代碼模擬了一個監聽天氣模式更新的設備(CurrentConditionDisplay)。C中的觀察者模式#

接口:

public interface ISubject 
{ 
    void RegisterObserver(IObserver obs); 
    void RemoveObserver(IObserver obs); 
    void NotifyObservers(); 
} 
public interface IDisplay 
{ 
    string Display(); 
} 
public interface IObserver 
{ 
    void Update(float temperature, float humidity, float pressure); 
} 

觀察

public class CurrentConditionDisplay : IObserver, IDisplay 
{ 
    private float temperature; 
    private float humidity; 
    private float pressure; 
    private ISubject weatherData; 
    public CurrentConditionDisplay(ISubject weatherData) 
    { 
     this.weatherData = weatherData; 
     this.weatherData.RegisterObserver(this); 

    } 
    public string Display() 
    { 
     StringBuilder sb = new StringBuilder(); 
     sb.AppendLine("Welcome to Current Condition Display..."); 
     sb.AppendLine(this.temperature.ToString()); 
     sb.AppendLine(this.humidity.ToString()); 
     sb.AppendLine(this.pressure.ToString()); 
     return sb.ToString(); 
    } 

    public void Update(float temperature, float humidity, float pressure) 
    { 
     this.temperature = temperature; 
     this.humidity = humidity; 
     this.pressure = pressure; 
    } 
} 

主題

public class WeatherData : ISubject 
{ 
    private List<IObserver> observersList; 
    private float temperature; 
    private float humidity; 
    private float pressure; 

    public WeatherData() 
    { 
     observersList = new List<IObserver>(); 
    } 
    public void RegisterObserver(IObserver obs) 
    { 
     observersList.Add(obs); 
    } 

    public void RemoveObserver(IObserver obs) 
    { 
     int index = observersList.IndexOf(obs); 
     if (index >= 0) 
     { 
      observersList.RemoveAt(index); 
     } 
    } 
    public void MeasurementsChanged() 
    { 
     Console.WriteLine("There is new data available..."); 
     NotifyObservers(); 
    } 
    public void NotifyObservers() 
    { 
     foreach (IObserver observer in observersList) 
     { 
      observer.Update(temperature, humidity, pressure); 
     } 
    } 
    public void SetMeasurements(float temperature, float humidity, float pressure) 
    { 
     this.temperature = temperature; 
     this.humidity = humidity; 
     this.pressure = pressure; 
     MeasurementsChanged(); 
    } 
} 

要在我的Program.cs中使用這些類,我instnce的WeatherData創建一次,並且通過該對象作爲參數給CurrentConditionDisplay的構造函數。我用這個當前設置看到的一個問題是IObserver有一個方法Update,它需要temperature, humidity, pressure作爲參數。我不能保證Subject(WeatherData)必須首先擁有這些字段。我是否應該添加另一個接口或抽象基類,以確保在調用SetMeasurements時,在該方法中更新的所有字段實際上都在Observer

+7

僅供參考,C#有一個內置的實現觀察者模式。請參閱[事件](http://msdn.microsoft.com/en-us/library/awbftdfh.aspx) –

+0

*您確定所有在該方法中更新的字段實際上都在Observer中,這意味着什麼* ?多次閱讀並且無法理解您的問題 –

+2

@JohnSaunders這是一本關於設計模式的書。目標是瞭解觀察者的想法,而不是學習C#功能) –

回答

1

我覺得你做同樣的事情......具有相當通用的冠冕堂皇IObserver接口有觀測WeatherData感到噁心時,真正只有適用於特定的方法簽名!

我寧願有這樣的事情:

public interface IObserver<T> 
{ 
    void Update(T updatedData); 
} 

隨着觀察者會是這個樣子(這裏剪掉一些額外的代碼):

public class CurrentConditionDisplay : IObserver<WeatherUpdate>, IDisplay 
{ 
    public CurrentConditionDisplay(ISubject<WeatherUpdate> weatherData) 
    { 
     this.weatherData = weatherData; 
     this.weatherData.RegisterObserver(this); 
    } 

    public void Update(WeatherUpdate update) 
    { 
     this.temperature = update.Temperature; 
     this.humidity = update.Humidity; 
     this.pressure = update.Pressure; 
    } 
} 

而只是爲了讓自己清楚,我的通用T對於IObserver<T>將是封裝天氣更新的對象:

public WeatherUpdate 
{ 
    public float Temperature; 
    public float Humidity; 
    public float Pressure; 
} 

而且ISubject將不得不改變,包括通用參數,以及:

public interface ISubject<T> 
{ 
    void RegisterObserver(IObserver<T> obs); 
    void RemoveObserver(IObserver<T> obs); 
    void NotifyObservers(); 
} 
+0

我認爲這也是一個好主意。如果每個設備有幾種不同類型的更新,這將很容易允許「CurrentDisplayUpdate」或「StatsDisplayUpdate」或你有什麼。 – wootscootinboogie

1

如果要強制執行此操作,可以執行的操作是在ISubject界面中定義溫度,溼度和壓力的屬性(請參見http://msdn.microsoft.com/en-us/library/64syzecx.aspx)。

然後調整IObserver接口(以及實現它的類)中的Update方法 - 可以刪除參數。將CurrentConditionDisplay類的Update方法更改爲從實現ISubject的對象的屬性中查找溫度,溼度,壓力值。

1

不,IObserver不具有溫度,溼度和壓力的領域。

當談到接口時,記住接口與客戶的需求(即呼叫者,你的情況爲WeatherData類)更緊密地聯繫在一起,而不是他們的實現。

我的意思是,你應該首先從WeatherData類的需求角度來看界面 - 這個類使用IObserver界面來通知其他人對溫度,溼度和壓力的變化, 不需要需要從觀察者那裏獲得溫度,溼度和壓力(它會對這些信息做什麼?)。

實際上,IObserver的某些實現可能不會持久存儲這些信息 - 例如某種日誌記錄觀察者可能會記錄這些更改,然後完全丟棄此信息。在這種情況下,將這些屬性添加到接口會迫使觀察者進入實現成員,這些實現成員隱藏觀察者,也不需要實現!

定義接口時,總是考慮調用者需要使用的方法和屬性 - 其他一切都是實現接口的類的實現細節。

+0

謝謝:)。因此,作爲設計選擇,您是否同意或不同意觀察者和主題都實現的具有要更新的屬性/字段的新界面或抽象類? – wootscootinboogie

+0

@wootscootinboogie好的問題,但搞清楚什麼時候類應該相互繼承是一個不同的(更復雜的)主題。在這種情況下,我會說不,他們不需要一個通用的基類或接口(一般來說,不應該因爲它們具有相同的成員而添加基類型)。直觀地看到[Liskov Substitution Principle](http://stackoverflow.com/questions/56860/what-is-the-liskov-substitution-principle) – Justin

+0

我會猜想他們需要明確的共同抽象,因爲他們DID有共同的成員。這個小小的事實會讓我頭疼。 – wootscootinboogie