委托、事件与Observer设计模式
- 一、委托 delegate 的用法
- 1、将方法作为另一个方法的参数
- 2、将方法绑定到委托
- 二、事件 Event
- 三、Observer观察者模式
一、委托 delegate 的用法
1、将方法作为另一个方法的参数
委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句和枚举定义,同时使得程序具有更好的可扩展性。
没看懂?没关系,看代码一目了然!
定义及使用方式案例:
public class ShowControl : MonoBehaviour
{
public delegate void DelegateTool(string text);
void Start()
{
ShowEvent showEvent = new ShowEvent();
//调用
ShowText(" AAA", showEvent.ShowA);
ShowText(" BBB", showEvent.ShowB);
}
public void ShowText(string text, DelegateTool show)
{
show(text);
}
}
public class ShowEvent : MonoBehaviour {
public void ShowA(string text)
{
Debug.Log("ShowA" + text);
}
public void ShowB(string text)
{
Debug.Log("ShowB" + text);
}
}
运行结果:
2、将方法绑定到委托
使用委托可以将多个方法绑定到同一个委托变量,从而实现依次调用所有绑定的方法。
现在改写上面的Start方法:
void Start()
{
ShowEvent showEvent = new ShowEvent();
DelegateTool delegateTool;
delegateTool = showEvent.ShowA;
delegateTool += showEvent.ShowB;
//调用
ShowText(" 哒哒哒", delegateTool);
}
运行结果:
注意这里,第一次用的“=”,是赋值的语法;第二次,用的是“+=”,是绑定的语法。如果第一次就使用“+=”,将出现“使用了未赋值的局部变量”的编译错误。
实际上,我们也可以绕过ShowText方法,通过委托来直接调用ShowA和ShowB,运行结果和上面一样:
void Start()
{
ShowEvent showEvent = new ShowEvent();
DelegateTool delegateTool;
delegateTool = showEvent.ShowA;
delegateTool += showEvent.ShowB;
//调用
//ShowText(" 哒哒哒", delegateTool);
delegateTool(" 哒哒哒");
}
也可以再简化一下,效果一样:
void Start()
{
ShowEvent showEvent = new ShowEvent();
//DelegateTool delegateTool;
//delegateTool = showEvent.ShowA;
//delegateTool += showEvent.ShowB;
DelegateTool delegateTool = new DelegateTool(showEvent.ShowA);
delegateTool += showEvent.ShowB;
//调用
ShowText(" 哒哒哒", delegateTool);
//delegateTool(" 哒哒哒");
}
既然给委托可以绑定一个方法,那么也应该有办法取消对方法的绑定,很容易想到,这个语法是“-=”:
void Start()
{
ShowEvent showEvent = new ShowEvent();
DelegateTool delegateTool;
delegateTool = showEvent.ShowA;
delegateTool += showEvent.ShowB;
ShowText(" 哒哒哒", delegateTool);
//取消对ShowB的绑定
delegateTool -= showEvent.ShowB;
ShowText(" -----", delegateTool);
}
运行结果:
二、事件 Event
事件是对委托的封装,与之前委托变量DelegateTool的声明唯一的区别是多了一个event关键字。
public event DelegateTool delegateTool;
如果一个类里,你把一个委托声明为 public 了,那么外部就可以随意改变委托变量的值,包括清空委托变量等,这样的话就违背了面向对象思想的封装特性;但如果声明为 private ,那就失去了委托的意义(在外部不能给委托添加函数引用)。此时就需要事件了。
可以把事件看成是委托的实例,事件是对委托的封装,就像类的属性成员一样,事件封装了委托,这样,就可以把委托定义为 private 类型,在外部就可以通过与委托对应的事件来访问委托了,而事件受到限制符“+=”“-=”的影响,不会破坏封装的特性。
事件的本质就是委托。委托类型用 delegate 修饰,事件类型用 event 修饰。
另外:委托与事件的关系可以形象的用 字段与属性来进行类比,事件确实可以自定义add()、remove()方法,属性可以自定义get、set 。
上面的例子不足以介绍Event,跟着下面的Observer监视模式一起学习。
三、Observer观察者模式
Observer设计模式中主要包括如下两类对象:
1.Subject:监视对象,它往往包含着其他对象所感兴趣的内容。
2.Observer:监视者,它监视Subject,当Subject中的某件事发生的时候,会告知Observer,而Observer则会采取相应的行动。
假设我们有个高档的热水器,我们给它通上电,当水温超过95度的时候:
1、扬声器会开始发出语音,告诉你水的温度;
2、液晶屏也会改变水温的显示,来提示水已经快烧开了。
3、热水器由三部分组成:热水器、警报器、显示器
现在我们需要写个程序来模拟这个烧水的过程,我们将定义一个类来代表热水器,我们管它叫:Heater,它有代表水温的字段,叫做temperature;当然,还有必不可少的给水加热方法BoilWater(),一个发出语音警报的方法MakeAlert(),一个显示水温的方法,ShowMsg()。
namespace Delegate {
// 热水器
public class Heater {
private int temperature;
public delegate void BoilHandler(int param); //声明委托
public event BoilHandler BoilEvent; //声明事件
// 烧水
public void BoilWater() {
for (int i = 0; i <= 100; i++) {
temperature = i;
if (temperature > 95) {
if (BoilEvent != null) { //如果有对象注册
BoilEvent(temperature); //调用所有注册对象的方法
}
}
}
}
}
// 警报器
public class Alarm {
public void MakeAlert(int param) {
Console.WriteLine("Alarm:嘀嘀嘀,水已经 {0} 度了:", param);
}
}
// 显示器
public class Display {
public static void ShowMsg(int param) { //静态方法
Console.WriteLine("Display:水快烧开了,当前温度:{0}度。", param);
}
}
class Program {
static void Main() {
Heater heater = new Heater();
Alarm alarm = new Alarm();
heater.BoilEvent += alarm.MakeAlert; //注册方法
heater.BoilEvent -= alarm.MakeAlert; //取消方法绑定需要用指定的实例化对象
heater.BoilEvent += (new Alarm()).MakeAlert; //给匿名对象注册方法
heater.BoilEvent -= (new Alarm()).MakeAlert; //未用指定的实例化对象,没有效果
heater.BoilEvent += Display.ShowMsg; //注册静态方法
heater.BoilWater(); //烧水,会自动调用注册过对象的方法
}
}
}
输出为:
Alarm:嘀嘀嘀,水已经 96 度了:
Display:水快烧开了,当前温度:96度。