我正在阅读(奇妙的)书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
?
答案 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
另一个很好的资源: