委托、事件与Observer设计模式
简记了两篇关于delegate 的内容,接着再来。
Observer设计模式中实际上只包含了两类对象,一个是Subject(主题),一个是Observer(观察者)。它们之间的角色是:
Subject:主题(被监视对象),它往往包含着Observer所感兴趣的内容。
Observer:观察者,它观察Subject。当Subject中的某件事发生的时候(通常是它所感兴趣的内容改变的时候),会被自动告知,而Observer则会采取相应的行动(通常为更新自身状态或者显示输出)。
学习一个例子:有一个热水器,通电后水温超过95度时
- 发出语音提示水的温度
- 液晶屏显示水温
热水器 Heater
水温 temperature
加热方法 BoilWater()
语音提示 MakeAlert()
显示水温 ShowMsg()
程序:Heater
using System;
using System.Collections.Generic;
using System.Text;
namespace Observer
{
class Heater
{
// 水温
private int temperature;
public void BoilWater()
{
for (int i = 0; i <= 100; i++)
{
temperature = i;
if (temperature >= 95)
{
MakeAlert(temperature);
ShowMsg(temperature);
}
}
}
// 语音提示
private void MakeAlert(int paraTemper)
{
Console.WriteLine("Alert: 现在水的温度是{0}", paraTemper);
}
// 显示水水温
private void ShowMsg(int paraTemper)
{
Console.WriteLine("Display: 现在水的温度是{0}", paraTemper);
}
static void Main(string[] args)
{
Heater ht = new Heater();
ht.BoilWater();
}
}
}
Alert: 现在水的温度是95
Display: 现在水的温度是95
Alert: 现在水的温度是96
Display: 现在水的温度是96
Alert: 现在水的温度是97
Display: 现在水的温度是97
Alert: 现在水的温度是98
Display: 现在水的温度是98
Alert: 现在水的温度是99
Display: 现在水的温度是99
Alert: 现在水的温度是100
Display: 现在水的温度是100
Press any key to continue . . .
Observer设计模式:
面向对象设计,高内聚低耦合,如果现有Heater分成三部分,Heater、 Alarm、 Display
各自负责各自的功能。如下:
using System;
using System.Collections.Generic;
using System.Text;
namespace Observer
{
class Heater
{
// 水温
private int temperature;
public void BoilWater()
{
for (int i = 0; i <= 100; i++)
{
temperature = i;
if (temperature >= 95)
{
//MakeAlert(temperature);
//ShowMsg(temperature);
}
}
}
//// 语音提示
//private void MakeAlert(int paraTemper)
//{
// Console.WriteLine("Alert: 现在水的温度是{0}", paraTemper);
//}
//// 显示水水温
//private void ShowMsg(int paraTemper)
//{
// Console.WriteLine("Display: 现在水的温度是{0}", paraTemper);
//}
static void Main(string[] args)
{
Heater ht = new Heater();
ht.BoilWater();
}
}
class Alarm
{
// 语音提示
private void MakeAlert(int paraTemper)
{
Console.WriteLine("Alert: 现在水的温度是{0}", paraTemper);
}
}
class Display
{
// 显示水水温
private void ShowMsg(int paraTemper)
{
Console.WriteLine("Display: 现在水的温度是{0}", paraTemper);
}
}
}
Observer设计模式
(1)Subject: 监视对象,它往往包含着其它对象所感兴趣的内容。在本范例中,热水器就是一个监视对象,它包含的其它对象所感兴趣的内容,就是temprature 字段,当这个字段的值快到100时,会不断把数据发给监视它的对象。
(2)Observer: 监视者,它监视Subject,当Subject中的某件事发生的时候,会告知Observer,而Observer则会采取相应的行动。在本范例中,Observer有警报器和显示器,它们采取的行动分别是发出警报和显示水温。
在本例中,事情发生的顺序应该是这样的:
(1)警报器和显示器告诉热水器,它对它的温度比较感兴趣(注册)。
(2)热水器知道后保留对警报器和显示器的引用。
(3)热水器进行烧水这一动作,当水温超过95度时,通过对警报器和显示器的引用,自动调用警报器的MakeAlert()方法、显示器的ShowMsg()方法。类似这样的例子是很多的,GOF对它进行了抽象,称为Observer设计模式:Observer设计模式是为了定义对象间的一种一对多的依赖关系,以便于当一个对象的状态改变时,其它依赖于它的对象会被自动告知并更新。Observer模式是一种松耦合的设计模式。
使用委托和事件再做:
using System;
using System.Collections.Generic;
using System.Text;
namespace Observer
{
class Heater
{
// 水温
private int temperature;
// Delegate
public delegate void BoilWaterHandler(int param);
// Event
public event BoilWaterHandler BoilEvent;
public void BoilWater()
{
for (int i = 0; i <= 100; i++)
{
temperature = i;
if (temperature >= 95)
{
if (BoilEvent != null)
{
BoilEvent(temperature);
}
}
}
}
}
class Alarm
{
// 语音提示
public void MakeAlert(int paraTemper)
{
Console.WriteLine("Alert: 现在水的温度是{0}", paraTemper);
}
}
class Display
{
// 显示水水温
public void ShowMsg(int paraTemper)
{
Console.WriteLine("Display: 现在水的温度是{0}", paraTemper);
}
}
class MyObserver
{
static void Main(string[] args)
{
Heater ht = new Heater();
Display dp = new Display();
ht.BoilEvent += (new Alarm()).MakeAlert;
ht.BoilEvent += dp.ShowMsg;
ht.BoilWater();
}
}
}
Alert: 现在水的温度是95
Display: 现在水的温度是95
Alert: 现在水的温度是96
Display: 现在水的温度是96
Alert: 现在水的温度是97
Display: 现在水的温度是97
Alert: 现在水的温度是98
Display: 现在水的温度是98
Alert: 现在水的温度是99
Display: 现在水的温度是99
Alert: 现在水的温度是100
Display: 现在水的温度是100
Press any key to continue . . .
Dot Net Framework 中的委托与事件
.Net Framework的编码规范:
(1)委托类型的名称都应该以EventHandler结束。
(2)委托的原型定义:有一个void返回值,并接受两个输入参数:一个Object类型,一个EventArgs类型(或继承自EventArgs)。
(3)事件的命名为:委托去掉EventHandler之后剩余的部分。
(4)继承自EventArgs的类型应该以EventArgs结尾。
再做一下说明:
(1)委托声明原型中的Object类型的参数代表了Subject,也就是监视对象,在本例中是Heater(热水器)。回调函数(比如 Alarm的MakeAlert)可以通过它访问触发事件的对象(Heater)。
(2)EventArgs对象包含了Observer所感兴趣的数据,在本例中是temperature。
上面这些其实不仅仅是为了编码规范而已,这样也使得程序有更大的灵活性。比如说,我们不光想获得热水器的温度,还想在Observer端(警报器或者显示器)方法中获得它的生产日期、型号、价格,那么委托和方法的声明都会变得麻烦,而如果我们将热水器的引用传给警报器方法,就可以在方法中直接访问热水器了。
using System;
using System.Collections.Generic;
using System.Text;
namespace ObserverFrameWork
{
class Heater
{
// 水温
private int temperature;
// Heater Type
public string type = "China - 888";
// Heater Area
public string area = "DL-LN";
// Delegate
public delegate void BoilWaterHandler(object sender, BoilWaterArgs e);
// Event
public event BoilWaterHandler BoilEvent;
// Declare BoilWaterArgs
public class BoilWaterArgs : EventArgs
{
public readonly int readTemperature;
public BoilWaterArgs(int temperature)
{
this.readTemperature = temperature;
}
}
protected virtual void BuildBoilEvent(BoilWaterArgs e)
{
if (BoilEvent != null)
{
BoilEvent(this, e);
}
}
public void BoilWater()
{
for (int i = 0; i <= 100; i++)
{
temperature = i;
if (temperature >= 95)
{
BoilWaterArgs e = new BoilWaterArgs(temperature);
this.BuildBoilEvent(e);
}
}
}
}
class Alarm
{
// 语音提示
public void MakeAlert(object sender, Heater.BoilWaterArgs e)
{
Heater ht = (Heater)sender;
Console.WriteLine("Heater Informaction:{0} ---{1} Alert: 现在水的温度是{2}", ht.area, ht.type, e.readTemperature);
}
}
class Display
{
// 显示水水温
public void ShowMsg(object sender, Heater.BoilWaterArgs e)
{
Heater ht = (Heater)sender;
Console.WriteLine("Heater Informaction:{0} ---{1} Display: 现在水的温度是{2}", ht.area, ht.type, e.readTemperature);
}
}
class Program
{
static void Main(string[] args)
{
Heater ht = new Heater();
Display dp = new Display();
ht.BoilEvent += (new Alarm()).MakeAlert;
ht.BoilEvent += dp.ShowMsg;
ht.BoilWater();
}
}
}