I'm going through the (fantastic) book Head First Design Patterns and need some clarification on the observer pattern.  The following little bit of code simulates a device (CurrentConditionDisplay) that listens for updates on weather patterns.
interfaces:
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);
}
Observer
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;
    }
}
Subject
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();
    }
}
To use these classes in my Program.cs I'm creating once instnce of WeatherData and passing that object as parameter to the constructor of CurrentConditionDisplay.  A problem that I see with this current setup is that IObserver has one method Update which takes temperature, humidity, pressure as parameters.  I see no guarantee that the Subject (WeatherData) has to have these fields in the first place.  Should I add another interface or abstract base class to ensure that when SetMeasurements is called, all the fields being updated in that method are actually in the Observer?
I feel the same thing you do... having a rather generic sounding IObserver interface have a specific method signature that really only applies when observing WeatherData feels icky!
I'd much rather have something like this:
public interface IObserver<T>
{
    void Update(T updatedData);
}
With an observer that would look something like this (snipped some extra code here):
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;
    }
}
And just to make myself clear, my generic T for IObserver<T> would be an object that encapsulates a weather update:
public WeatherUpdate
{
    public float Temperature;
    public float Humidity;
    public float Pressure;
}
And ISubject would have to be changed to include the generic parameter as well:
public interface ISubject<T>
{
    void RegisterObserver(IObserver<T> obs);
    void RemoveObserver(IObserver<T> obs);
    void NotifyObservers();
}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With