您的位置:首页 > 其它

《Head First Design Patterns》笔记二:观察者模式(Observer Pattern)

2009-04-03 14:59 671 查看
第二篇 观察者模式
好久没写文章了,为我的懒惰害臊一下.

首先还是需求,我们现在有个新项目,做一个气象站的项目,负责提供气象资料(temperature, humidity, pressure) 给天气显示板,这里有2个显示板,一个是状态显示板,一个统计显示板.



ok,这个很简单,我们立马开始动手做uml图



每当气象站获得新信息的时候,就开始调用UpdateDisplay方法,更新气象显示板的资料,如下代码

public class WeatherData
{
public IDisplay display1, display2;
double _temperature;
public Double Temperature
{
get { return _temperature; }
set { _temperature = value; MessurementChanged();}
}
double _humidity;
public Double Humidty
{
get { return _humidity; }
set { _humidity = value; MessurementChanged();}
}
double _pressure;
public Double Pressure
{
get { return _pressure; }
set { _pressure = value;}
}
public void MessurementChanged()
{
UpdateDisplay();
}
public void UpdateDisplay()
{
display1.Temperature = this.Temperature; display1.Humidty = this.Humidty;
display2.Temperature = this.Temperature; display2.Humidty = this.Humidty;
}
}


ok,这么做工作的很好,但是,现在又新的显示板加入,怎么办呢,只好更改WeatherData类,加入新的支持,那么如果显示板更改需求,不再需要temperature温度 ,而需要pressure大气压的数值呢,还是要更改WeatherData类,这样,根本无法做到封装的目的,只能面对无穷无尽的修改和重编译.

这里我们就需要用到观察者模式了,什么是观察者模式,有句话是这么形容的: publish+subscriber=Observer Pattern,或则成为 subject(主题) +observer(观察者), 观察者模式是很重要的一个模式,我们想象下显示生活报纸订阅系统是怎么做的,用户随时订阅报纸,也可以随时退订.只要用户订阅了,那么一有新报纸,就会通知用户,并发送新资料给用户.还有在c#里我们经常用到的delegate,event之类,一开始我常感到很惊奇,这些是怎么做到的呢,其实这些就是观察者模式的一个演化.发布者状态一发生变化,订阅者就接受到消息. 发布者并不知道订阅者的具体情况,他们互相十分独立,这也是面向对象的一个重要原则:尽量使关联事物之间以松耦合链接.观察者模式图示如下.


当观察者注册的时候,主题把观察者放到一个容器里面.

当观察者退出的时候,主题把观察者从容器里面移除.

当主题状态一发生变化,主题首先从容器里面获得观察者的集合,然后把消息通知这些观察者.

观察者收到消息,就可以做自己想做的.

我们了解了观察者模式, 那么我们回到气象站项目 , 这里气象站中心就是主题,气象显示板就是观察者.我们可以画uml图如下



具体代码如下,分8个类

第一部分,主题3个类(为了方便观看,把3个文件放在一起)

//file:IObservable.cs
using System;
public interface IObservable
{
void RegisterObserver(IObserver observer);
void UnRegisterObserver(IObserver observer);
void NotifyObserver();
}
//file:Subject.cs
using System;
using System.Collections.Generic;
public class Subject:IObservable
{
private List<IObserver> observerList=new List<IObserver>();
public void RegisterObserver(IObserver observer)
{
if (!observerList.Contains(observer)) observerList.Add(observer);
}
public void UnRegisterObserver(IObserver observer)
{
if (observerList.Contains(observer)) observerList.Remove(observer);
}
public void NotifyObserver()
{
foreach(IObserver observer in observerList)
{
observer.Update(this);
}
}
}
//file WeatherData.cs
using System;
public class WeatherData:Subject
{
private double _temperature;
public double Temperature
{
get{return _temperature;}
set{_temperature=value;MeasurementChanged();}
}
private double _humidity;
public double Humidity
{
get{return _humidity;}
set{_humidity=value;MeasurementChanged();}
}
private double _pressure;
public double Pressure
{
get{return _pressure;}
set{_pressure=value;MeasurementChanged();}
}
public WeatherData(double temperature,double humidity,double pressure)
{
this._temperature=temperature;
this._humidity=humidity;
this._pressure=pressure;
}

public void MeasurementChanged()
{
NotifyObserver();

}
}


第二部分,观察者部分分4个类,如下

//file:IObserver.cs
using System;
public interface IObserver
{
void Update(object sender);
}
//file:IDisplay.cs
using System;
public interface IDisplay
{
void Display();
}
//file:GeneralDisplay.cs
using System;
public class GeneralDisplay:IObserver,IDisplay
{
private double temperature,humidity;
public void Update(object sender)
{
if (sender is WeatherData)
{
WeatherData wd=(WeatherData)sender;
this.temperature=wd.Temperature;
this.humidity=wd.Humidity;
Display();
}

}
public void Display()
{
Console.WriteLine("current temperature is {0} C,humidity is{1}",temperature,humidity);
}
}
//file:StatisticsDisplay.cs
using System;
using System.Collections.Generic;
public class StatisticsDisplay:IObserver,IDisplay
{
private List<double> temperatureList=new List<double>();
public void Update(object sender)
{
if (sender is WeatherData)
{
WeatherData wd=(WeatherData)sender;
temperatureList.Add(wd.Temperature);
temperatureList.Sort();
Display();
}
}
public void Display()
{
if (temperatureList.Count>0)
Console.WriteLine("min/max/avg temperature is {0},{1},{2}",MinTempe(),MaxTempe(),AvgTempe());
}
private double MinTempe()
{
if (temperatureList.Count>0)return temperatureList[0];
else return 0;
}
private double MaxTempe()
{
if (temperatureList.Count>0)return temperatureList[temperatureList.Count-1];
else return 0;
}
private double AvgTempe()
{
if (temperatureList.Count>0)
{
double sum=0;
foreach(double d in temperatureList)
{
sum+=d;
}
return sum/temperatureList.Count;
}
else return 0;
}
}


最后就是程序入口,让我们看一下具体效果了,如下

using System;
public class Program
{
public static void Main()
{
WeatherData wd=new WeatherData(20,80,40);
IObserver genDis=new GeneralDisplay();
IObserver staDis=new StatisticsDisplay();
wd.RegisterObserver(genDis);
wd.RegisterObserver(staDis);
wd.Temperature=25;
wd.Temperature=30;
wd.Humidity=70;
Console.ReadLine();
}
}


运行结果如下:

current temperature is 25 C,humidity is80
min/max/avg temperature is 25,25,25
current temperature is 30 C,humidity is80
min/max/avg temperature is 25,30,27.5
current temperature is 30 C,humidity is70
min/max/avg temperature is 25,30,28.3333333333333

总结

当事物之间存在一对多的关系,当“一”变化的时候,其他事物将自动收到消息并更新,这种模式称为观察者模式。

思考

是否感觉有点触发事件的感觉,不错,在c#里面使用delegate和event可以使以上代码更简练.我们通过使用delegate的方式修改代码,分为5个类,Idisplay,GenerealDisplay,StatisticsDisplay,WeatherData,Program.代码如下:

//file 1 IDisplay.cs
using System;
public interface IDisplay
{
void OnUpdate(object sender,EventArgs args);
void Display();
}
//file 2 GeneralDisplay.cs
using System;
public class GeneralDisplay:IDisplay
{
private double temperature,humidity;
public void OnUpdate(object sender,EventArgs args)
{
if (sender is WeatherData)
{
WeatherData wd=(WeatherData)sender;
temperature=wd.Temperature;
humidity=wd.Humidity;
Display();
}
}
public void Display()
{
Console.WriteLine("current temperature is {0} C,humidity is {1}",temperature,humidity);
}

}
//file 3 StatisticsDisplay.cs
using System;
using System.Collections.Generic;
public class StatisticsDisplay:IDisplay
{
private List<double> temperatureLists=new List<double>();
public void OnUpdate(object sender,EventArgs args)
{
if (sender is WeatherData)
{
WeatherData wd=(WeatherData)sender;
temperatureLists.Add(wd.Temperature);
temperatureLists.Sort();
Display();
}
}
public void Display()
{
Console.WriteLine("min/max/avg temperature is {0},{1},{2}",GetMinTemp(),GetMaxTemp(),GetAvgTemp());
}
public double GetAvgTemp()
{
int count=temperatureLists.Count;
double sum=0;
if (count>0)
{
for(int i=0;i<count;i++)
{
sum+=temperatureLists[i];
}
return sum/count;
}
else return 0;
}
public double GetMinTemp()
{
if (temperatureLists.Count>0) return temperatureLists[0];
else return 0;
}
public double GetMaxTemp()
{
if (temperatureLists.Count>0) return temperatureLists[temperatureLists.Count-1];
else return 0;
}

}
//file 4 WeatherData.cs
using System;
public class WeatherData
{
public delegate void UpdateMethod(object sender,EventArgs args);
public event UpdateMethod  Update;
private double _temperature;
public double Temperature{get{return _temperature;}set{_temperature=value;MeasurementChanged();}}
private double _humidity;
public double Humidity{get{return _humidity;} set {_humidity=value;MeasurementChanged();}}
private double _pressure;
public double Pressure{get {return _pressure;}set{_pressure=value;MeasurementChanged();}}
public WeatherData(double temperature,double humidity,double pressure)
{
this._temperature=temperature;
this._humidity=humidity;
this._pressure=pressure;
}
public void MeasurementChanged()
{
if (Update!=null) Update(this,new EventArgs());
}
}
//file 5 Program.cs
using System;
public class program
{
public static void Main()
{
WeatherData wd=new WeatherData(20,80,40);
IDisplay dis1=new GeneralDisplay();
IDisplay dis2=new StatisticsDisplay();
wd.Update+=dis1.OnUpdate;
wd.Update+=dis2.OnUpdate;
wd.Temperature=25;
wd.Temperature=30;
wd.Humidity=70;
}
}


这里关键的地方时WeathData类添加的event,delegata,event后台工作我的理解其实就是一种Observer模式.程序运行结果同以前的代码一致,对照着看,可以更好的理解delegate,event和observer模式.

下一篇:《Head First Design Patterns》笔记三:装饰者模式(Decorator Pattern)

上一篇:《Head First Design Patterns》笔记一:策略模式(Strategy Pattern)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐