- 观察者模式定义了对象之间一个主题对多应多个观察者的依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
- 角色:
- 主题(Subject):主题是一个接口,该接口规定了具体主题需要实现的方法
- 观察者(Observer):观察者是一个接口,该接口规定了具体观察者用来更新数据的方法。
- 具体主题(ConcreteSubject):具体主题是实现主题接口类的一个实例,该实例包含有可以经常发生变化的数据。具体主题使用一个集合,比如ArrayList,存放观察者的引用,以便数据变化时通知具体的观察者。
- 具体观察者(Concrete Observer):具体观察者是实现观察者接口的一个实例。具体观察者包含有可以存放具体主题引用的主题接口变量,以便具体观察者让具体主题将自己添加到具体主题的集合中,使自己成为它的观察者,或让这个具体的主题将自己从具体的主题中的观察者列表中删除,使自己不再是它的观察者。
- 一对多关系:
- 利用观察者模式,主题是具有状态的对象,并可以控制这些状态。也就是说,有“一个”具有状态的主题。另一方面,观察者使用这些状态,虽然这些状态并不属于他们。有许多的观察者,依赖主题来告诉他们状态何时改变了。这就产生一个关系:“一个”主题对应“多个”观察者的关系。
- Demo背景
- 设计一个气象观测站,测量温度、湿度、气压等,会有多种公告板如气温布告板,舒适度布告板,天气预报布告板等等。每当天气数据变化时,这些布告板的数据就需要相应自动更新。
- 代码实现
- 主题接口:ISubject
public interface ISubject
{
void RegisterObserver(IObserver o);
void RemoveObserver(IObserver o);
void NotifyObserver();
}
- 观察者接口:IObserver
public interface IObserver
{
void Update(float temp, float humidity, float pressure);
}
- 具体主题:WeatherData
public class WeatherData:ISubject
{
private ArrayList observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData()
{
observers = new ArrayList();
}
public void RegisterObserver(IObserver o)
{
observers.Add(o);
}
public void RemoveObserver(IObserver o)
{
int i = observers.IndexOf(o);
if (i >= 0)
{
observers.RemoveAt(i);
}
}
public void NotifyObserver()
{
for (int i = 0; i < observers.Count; i++)
{
IObserver observer = (IObserver)observers[i];
observer.Update(temperature, humidity, pressure);
}
}
public void messurementsChanged()
{
NotifyObserver();
}
public void SetMessureMents(float temperature, float humidity, float pressure)
{
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
messurementsChanged();
}
}
- 具体观察者:CurrentConditionDisplay,ForecastDisplay
public interface IDisplayElement
{
void Display();
}
public class CurrentConditionDisplay : IObserver, IDisplayElement
{
private float temperature;
private float humidity;
private float pressure;
private ISubject weatherData;
public void Display()
{
Console.WriteLine($"公告板1 当前天气 => temperature:{temperature},humidity:{humidity},pressure:{pressure}");
}
public void Update(float temperature, float humidity, float pressure)
{
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
Display();
}
public CurrentConditionDisplay(ISubject weatherData)
{
this.weatherData = weatherData;
weatherData.RegisterObserver(this);
}
}
public class ForecastDisplay : IObserver, IDisplayElement
{
private float temperature;
private float humidity;
private float pressure;
private ISubject weatherData;
public void Display()
{
Console.WriteLine($"公告板2 明天天气 => temperature:{temperature},humidity:{humidity},pressure:{pressure}");
}
public void Update(float temperature, float humidity, float pressure)
{
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
Display();
}
public ForecastDisplay(ISubject weatherData)
{
this.weatherData = weatherData;
weatherData.RegisterObserver(this);
}
}
-
- 测试代码与运行结果
static void Main(string[] args)
{
WeatherData weatherData = new WeatherData();
Console.WriteLine("--------公告板1加入观察者-------");
CurrentConditionDisplay display1 = new CurrentConditionDisplay(weatherData);
weatherData.SetMessureMents(10, 20, 30);
Console.WriteLine("--------公告板2加入观察者-------");
ForecastDisplay display2 = new ForecastDisplay(weatherData);
weatherData.SetMessureMents(15, 25, 35);
Console.WriteLine("--------公告板1退出观察者-------");
weatherData.RemoveObserver(display1);
weatherData.SetMessureMents(19, 29, 39);
}
- 优点:
- 具体主题和具体观察者是松耦合关系。由于主题(Subject)接口仅仅依赖于观察者(Observer)接口,因此具体主题只是知道它的观察者是实现观察者(Observer)接口的某个类的实例,但不需要知道具体是哪个类。同样,由于观察者仅仅依赖于主题(Subject)接口,因此具体观察者只是知道它依赖的主题是实现主题(subject)接口的某个类的实例,但不需要知道具体是哪个类。
- 观察模式满足“开-闭原则”。主题(Subject)接口仅仅依赖于观察者(Observer)接口,这样,我们就可以让创建具体主题的类也仅仅是依赖于观察者(Observer)接口,因此如果增加新的实现观察者(Observer)接口的类,不必修改创建具体主题的类的代码。同样,创建具体观察者的类仅仅依赖于主题(Observer)接口,如果增加新的实现主题(Subject)接口的类,也不必修改创建具体观察者类的代码。
- 源码地址: https://github.com/DonyGu/DesignPatterns
转载于:https://www.cnblogs.com/donyblog/p/11370690.html