C#中的观察者模式

时间:2013-12-12 13:59:18

标签: c# design-patterns observer-pattern

我正在阅读(奇妙的)书Head First Design Patterns并需要对观察者模式进行一些澄清。以下一点代码模拟了一个侦听天气模式更新的设备(CurrentConditionDisplay)。

接口:

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中使用这些类,我将创建WeatherData的一次,并将该对象作为参数传递给CurrentConditionDisplay的构造函数。我在当前设置中看到的一个问题是,IObserver有一个方法Update,它将temperature, humidity, pressure作为参数。我看不出主题(WeatherData)必须首先包含这些字段。我应该添加另一个接口还是抽象基类,以确保在调用SetMeasurements时,该方法中更新的所有字段实际上都在Observer

4 个答案:

答案 0 :(得分:2)

我觉得你做同样的事情......拥有一个相当通用的声音IObserver界面有一个特定的方法签名,当观察WeatherData感觉icky时,它真正只适用于

我更喜欢这样的事情:

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();
}

答案 1 :(得分:1)

如果您想强制执行此操作,您可以做的是在ISubject界面中定义温度,湿度和压力的属性(请参阅http://msdn.microsoft.com/en-us/library/64syzecx.aspx)。

然后调整IObserver接口中的Update方法(以及实现它的类) - 您可以删除参数。更改CurrentConditionDisplay类的Update方法,以从实现ISubject的对象的属性中查找temp,humidity,pressure值。

答案 2 :(得分:1)

不,IObserver没有温度,湿度和压力的字段。

说到界面,重要的是要记住界面与客户的需求(即调用者,在你的情况下是WeatherData类)更紧密地联系在一起而不是他们的实现。

我的意思是你应该首先从WeatherData类的需要的角度来看待界面 - 这个类使用IObserver接口来通知其他人温度,湿度的变化和压力,仅此而已 - 需要从观察者那里获得温度,湿度和压力(这些信息会怎么做?)。

实际上IObserver的某些实现可能甚至不会保留此信息 - 例如某种日志记录观察者可能会记录这些更改,然后完全丢弃此信息。在这种情况下,将这些属性添加到接口会强制观察者进入实现成员,而观察者和实现实际上需要这些成员!

定义接口时,总是考虑调用者需要使用的方法和属性 - 其他所有内容都是实现接口的类的实现细节。

答案 3 :(得分:0)

您的权利,根据目前的实施情况,无法保证观察者具有温度,湿度和压力特性。无论 保证是什么,您将在调用更新方法时收到此信息。

其他阅读

为清楚起见,请考虑查看现实世界示例:

DoFactory.com: Observer Pattern

另一个很好的资源:

PluralSight.com: Design Patterns Library