一、定义
观察者模式又称发布-订阅(Publish/Subscribe)模式,这个模式有两种角色被观察者和观察者,多个观察者可以订阅某一个被观察者。被观察者通过委托或事件机制,在执行发布方法后调用观察者的某些方法。
二、委托法实现
委托是一种数据类型使用delegate关键字定义。可以认为委托是函数指针,将方法名赋值给委托,委托就能记录下方法的地址,执行委托能够间接地调用方法。
举例:博主(blogger)具有发布文章的方法;粉丝(fans)具有订阅博主、阅读博主文章的方法。现实现博主发布文章后,该博主的粉丝立即阅读文章。以下使用C#代码实现观察者模式,博主是被观察者,博主的两个粉丝是观察者。
namespace Observer
{
internal class Program
{
//定义一个委托,委托名为DelegateInBlogger,该委托能接收 返回值为void,参数为string的方法
public delegate void DelegateInBlogger(string articleName);
public class Blogger
{
//博主的委托属性为静态的,保证后续多个粉丝访问的是同一个委托
public static DelegateInBlogger delegateInBlogger = null;
public void publish(string articleName)
{
Console.WriteLine("博主发表了"+articleName);
//执行委托多播,以articleName为参数分别调用粉丝1和粉丝2的Read方法
delegateInBlogger(articleName);
}
}
public class Fans1
{
string name = "粉丝1";
//粉丝1订阅博主
public void Subscript()
{
//为博主的静态委托属性赋值,将方法名Read赋值给委托
Blogger.delegateInBlogger = Read;
}
public void Read(string articleName) {
Console.WriteLine(name+"观看了"+ articleName);
}
}
public class Fans2
{
string name = "粉丝2";
//粉丝2订阅博主
public void Subscript()
{
//往博主的静态委托属性添加方法名
Blogger.delegateInBlogger += Read;
}
public void Read(string articleName)
{
Console.WriteLine(name + "观看了" + articleName);
}
}
static void Main(string[] args)
{
Fans1 fans1 = new Fans1();
fans1.Subscript();
Fans2 fans2 = new Fans2();
fans2.Subscript();
new Blogger().publish("作品");
}
}
}
输出结果如下:
三、委托的问题
粉丝1订阅博主的代码如下:委托的初始值是null,所以需要直接为委托赋值。
public void Subscript(){
Blogger.delegateInBlogger = Read;
}
粉丝2订阅博主的代码如下:粉丝1订阅后委托值不再是null,直接使用 "=" 运算对委托赋值会导致原有的委托方法被覆盖;使用 "+=" 运算表示委托合并,不会覆盖原有的委托方法。
public void Subscript(){
Blogger.delegateInBlogger += Read;
}
使用委托实现观察者模式时,由于委托赋初始值和委托合并写法不同,因此需要区分开写。
四、事件法实现
事件是对委托的封装。事件的优点在于能防止外部直接赋值(事件类型不能使用 = 进行赋值),同时提供了添加(+=)和移除(-=)两个方法进行委托合并。使用事件实现观察者模式,初始赋值和后续合并写法相同,代码更加简洁。
namespace Observer_Event
{
internal class Program
{
public delegate void DelegateInBlogger(string articleName);
public class Blogger
{
//将委托定义为事件
public static event DelegateInBlogger delegateInBlogger = null;
public void publish(string articleName)
{
Console.WriteLine("博主发表了" + articleName);
delegateInBlogger(articleName);
}
}
public class Fans
{
string Name { get; set; }
public Fans(string name) {
this.Name = name;
}
public void Subscript()
{
//不需要考虑赋初始值问题
Blogger.delegateInBlogger += Read;
}
public void Read(string articleName)
{
Console.WriteLine(Name + "观看了" + articleName);
}
}
static void Main(string[] args)
{
Fans fans1 = new Fans("粉丝1");
fans1.Subscript();
Fans fans2 = new Fans("粉丝2");
fans2.Subscript();
new Blogger().publish("作品");
}
}
}