场景:水加热器,如果水温到95以上时,加热器的警报器会响且水温显示屏会显示当前的温度。
实现一:加热行为时,如果水温(状态)发生改变,到95度以上,那会同时调用行为:警报和显示。
{
int _temperature;
public void BoilWater()
{
for (int i = 0; i < 101; i++)
{
if (i > 95)
{
_temperature = i;
MakeAlert(_temperature);
ShowMessage(_temperature);
}
}
}
public void MakeAlert(int iTemperature)
{
Console.WriteLine("alarm:"+iTemperature);
}
public void ShowMessage(int iTemperature)
{
Console.WriteLine("current temperature is:" + iTemperature);
}
}
以上实现了这个加热器,现在的情况是:当加热时,且加热到95度以上,那么会调用警报行为和显示行为。如果现在加热器
要添加一个行为:水温到95以上时,要跳动Jump。那么要在这个类中添加这个行为,同时更改BoilWater。
通过观察者模式,可以不用修改BoilWater。当加热器水温到达95度以上时,会同时通知三个行为:警报,显示,跳动。
观察者模式包括:subject主题,也就是被观察者;Observer,观察者。它可以描述为一种订阅和发布关系。
在以上场景中,加热器是被观察者,而三个行为是观察者,它们都依赖于被观察者的状态信息;而加热器加热到95度,会调用这三个行为,构成了对这三个行为的依赖。
实现二:通过观察者模式实现加热器
先把行为:警报和显示分离开来。
{
int _temperature;
public void BoilWater()
{
for (int i = 0; i < 101; i++)
{
if (i > 95)
{
_temperature = i;
}
}
}
}
public class Alert
{
public void MakeAlert(int iTemperature)
{
Console.WriteLine("alarm:" + iTemperature);
}
}
public class Display
{
public void ShowMessage(int iTemperature)
{
Console.WriteLine("current temperature is:" + iTemperature);
}
}
警报器类和显示屏类中的行为都依赖于被观察者中的数据状态。观察者中的主题要求能够注册和注销这些行为。这里的注册注销可以动态添加这两个行为到一个行为集体,然后在执行到状态改变时同时调用这些行为。现在尝试把两个行为能够添加到一
个列表集合中。
把行为添加到集合列表
两个行为MakeAlert和ShowMessage除方法名不同外,签名的其它都相同。所以可以考虑通过委托列表集合来实现。
定义一个委托并修改主题:
public class Heater
{
List<DelegateHeater> _list = new List<DelegateHeater>();
int _temperature;
public void BoilWater()
{
for (int i = 0; i < 101; i++)
{
if (i > 95)
{
_temperature = i;
Notify();
}
}
}
public void Notify()
{
foreach (DelegateHeater heater in _list)
heater(_temperature);
}
public void Registe(DelegateHeater dheater)
{
_list.Add(dheater);
}
public void RegisteCancel(DelegateHeater dheater)
{
_list.Remove(dheater);
}
}
使用加热器:
{
Heater heater = new Heater();
//警报
Alert aa=new Alert();
//显示
Display dd = new Display();
//注册两个行为
heater.Registe(new DelegateHeater(aa.MakeAlert));
heater.Registe(new DelegateHeater(dd.ShowMessage));
heater.BoilWater();
Console.WriteLine("************************************");
//显示器坏了,注销显示行为
heater.RegisteCancel(new DelegateHeater(dd.ShowMessage));
heater.BoilWater();
}
这样可以随意注册和注销加热器的行为。
现在实现了观察者模式,通过委托列表集合来保存注册的行为。委托类型可以把多个方法绑定到一个委托。在使用时,需要定
义委托类型实例,可以考虑把委托类型定义到主题(水加热器内部)并进行封装。
{
public delegate void DelegateHeater(int iTemperature);
public DelegateHeater _selfHeater;
int _temperature;
public void BoilWater()
{
for (int i = 0; i < 101; i++)
{
if (i > 95)
{
_temperature = i;
_selfHeater(_temperature);
}
}
}
}
去掉注册和注销的行为,通过添加到或删除从委托保存行为对象,然后通过调用委托,执行已经注册的行为。
{
Heater heater = new Heater();
//警报
Alert aa=new Alert();
//显示
Display dd = new Display();
//注册两个行为
heater._selfHeater += aa.MakeAlert;
heater._selfHeater += dd.ShowMessage;
heater.BoilWater();
Console.WriteLine("************************************");
//显示器坏了,注销显示行为
heater._selfHeater -= dd.ShowMessage;
heater.BoilWater();
}
访问类中的字段,通过采用属性方式访问。通过属性对域进行封装。它主要体现面向对象的封装特性,且可以提高安全性,和
数据的完整性。例如可以通过设置读和写属性来控制域的读写属性。但平时多从封装性方面来使用,在3.x及4.0版本中,出现了
自动属性。同样对于委托来说,可以通过事件类型来封装。
通过事件来封装委托实现水加热器。
{
public delegate void BoilWaterHandler(int iTemperature);
public event BoilWaterHandler BoilEvent;
int _temperature;
public void BoilWater()
{
for (int i = 0; i < 101; i++)
{
if (i > 95)
{
_temperature = i;
BoilEvent(_temperature);
}
}
}
}
调用时,和上一段实现没有区别:
{
Heater heater = new Heater();
//警报
Alert aa=new Alert();
//显示
Display dd = new Display();
//注册两个行为
heater.BoilEvent += aa.MakeAlert;
heater.BoilEvent += dd.ShowMessage;
heater.BoilWater();
Console.WriteLine("************************************");
//显示器坏了,注销显示行为
heater.BoilEvent -= dd.ShowMessage;
heater.BoilWater();
}
在加热器加热时,也就是在执行BoilWater时,如果温度到达95度以上,就会调用注册到BoilEvent事件的行为,它通过委托实现。但如果还没有注册行为,在BoilWater执行时,到达条件调用两个注册行为,会引发异常,因为现在BoilEvent未注册行为,所以,还应该在Heater类中BoilWater中添加对事件为空的判断。
BoilEvent(_temperature);
现在通过委托且用事件封装委托实现了观察者。在c#中,事件的实现模式就是观察者模式。下面通过系统的自定义事件实现这个水加热器。
先看下常用的page_load事件
可以知道,这个事件的委托类型委托的方法带两个参数: sender参数就是主题对象。而e为主题对象多个依赖的要获得的信息数
据,例如在热水器中,其中的温度就是这个e。
{
public delegate void BoilWaterEventHandler(object sender, BoildEventArgs e);
public event BoilWaterEventHandler Boiled;
int _temperature;
public void BoilWater()
{
for (int i = 0; i < 101; i++)
{
if (i > 95)
{
_temperature = i;
if (Boiled != null)
{
BoildEventArgs args = new BoildEventArgs(_temperature);
Boiled(this, args);
}
}
}
}
}
public class BoildEventArgs : EventArgs
{
public BoildEventArgs(int iTemperature)
{
Temperature = iTemperature;
}
public int Temperature { get; set; }
}
public class Alert
{
public void MakeAlert(object sender, BoildEventArgs e)
{
Console.WriteLine("alarm:" + e.Temperature);
}
}
public class Display
{
public void ShowMessage(object sender, BoildEventArgs e)
{
Console.WriteLine("current temperature is:" + e.Temperature);
}
}
因为委托变了,所以还要调用被委托的行为的签名,所以ShowMessage方法和MakeAlert方法都做了相应的调整。
实现三:通过.net内置的观察者模式实现水加热器
在.net4.0中,添加了对观察者模式的支持。添加了两个接口:
IObservable<T>这个就是主题
IObserver<T>这个是观察者。
{
List<IObserver<int>> _list = new List<IObserver<int>>();
int _temperature;
public IDisposable Subscribe(IObserver<int> observer)
{
_list.Add(observer);
return observer as IDisposable;
}
public void BoilWater()
{
for (int i = 0; i < 101; i++)
{
if (i > 95)
{
_temperature = i;
Notify(_temperature);
}
}
}
public void Notify(int iTemperature)
{
foreach (IObserver<int> observer in _list)
observer.OnNext(iTemperature);
}
public IDisposable UnSubscribe(IObserver<int> observer)
{
_list.Remove(observer);
return observer as IDisposable;
}
}
public class Alert:IObserver<int>
{
public void OnCompleted()
{
throw new NotImplementedException();
}
public void OnError(Exception error)
{
throw new NotImplementedException();
}
public void OnNext(int value)
{
Console.WriteLine("alarm:" + value);
}
}
public class Display : IObserver<int>
{
public void OnCompleted()
{
throw new NotImplementedException();
}
public void OnError(Exception error)
{
throw new NotImplementedException();
}
public void OnNext(int value)
{
Console.WriteLine("current temperature is:" + value);
}
}
public void Test_7_observer()
{
Heater heater = new Heater();
Alert aa = new Alert();
Display dd = new Display();
heater.Subscribe(aa);
heater.Subscribe(dd);
heater.BoilWater();
Console.WriteLine("************************************");
heater.UnSubscribe(dd);
heater.BoilWater();
}
Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达到松耦合。委托就是一种观察者模式的应
用,应用的前提是观察者(被委托的方法)具有相同的接口 ,或说具有相同的高层抽象。在这里被委托的方法,方法名可以
不同,但方法签名的其他部分相同。
