C#中观察者模式(Observer Pattern)

观察者模式

观察者模式(Observer Pattern)是设计模式中比较常见的一种,有时候也称为发布/订阅者模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个目标主题对象,当主题对象状态发生变化时,会通知所有的观察者对象,使其自动作出相应的响应行为。

在现实生活中处处可见观察者模式,比如说微信中的订阅号,微博关注好友,等等。当订阅号有什么推送,所有关注此订阅号的用户都将收到推送信息。

观察者模式组成

从整体来讲,观察者模式中会存在两种角色,观察者角色,和被观察角色(即主体角色)。
观察者模式中具有的角色:

  1. 抽象主题(Subject):抽象主题把所有观察者对象的引用保存在一个聚集里,每个主题都可以有N多个观察者。抽象主题提供一个接口或抽象类,可以增加和删除观察者对象。
  2. 具体主题(ConcreteSubject):实现抽象主题接口,将有关状态存入具体观察者对象,在具体主题内部状态发生改变时,通知所有登记过的观察者。
  3. 抽象观察者(Observer):为所有具体观察者定义一个接口,在主题通知时更新自己。一般由抽象类或接口实现。
  4. 具体观察者(ConcreteObserver):实现抽象观察者所要求的接口,是自身状态和主题状态协调。

具体过程可以参考观察者模式结构图:

观察者模式优缺点

优点:
  1. 广播通信。
    观察者模式支持广播通信,被观察者会向所有注册过的观察者发出通知。
  2. 聚耦合。
    观察者模式在观察者和被观察者间建立了一个抽象的耦合。被观察者并不知道任何一个具体的观察者,只是保存抽象观察者的列表,每一个具体观察者都符合一个抽象观察者的接口。
  3. 表示层和数据逻辑层分离。
    观察者模式实现了表示层和数据逻辑层的分离,并定义了稳定的更新消息传递机制,抽象了更新接口,使其可以有各种不同的表示层,即观察者。
缺点:
  1. 时间复杂度。
    如果一个被观察对象有很多直接和间接的观察者的话,通知到所有的观察者将会需要很长时间。
  2. 内联不足。
    虽然观察者模式可以随时将主题对象的变化通知给订阅者,但是观察者无法得知所订阅的主题对象怎样发生变化。
  3. 循环调用。
    若被观察的主题对象之间有循环依赖的话,被观察者之间会进行循环调用,导致系统崩溃,需要特殊主义。

观察者模式实现

知道了整个工作原理,现在我们来按照上面的流程实现一个观察者模式。模仿微信公众号推送文章过程。
整体框架如图所示,我们一共创建了四个类和一个接口。

首先,定义一个抽象主题类,即公众号抽象类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
 #region 公众号抽象类
public abstract class OfficalAccounts {
//保存观察者列表
private List<IObserver> observers = new List<IObserver>();

//公众号名称
public string OfficalAccountsName { get; set; }
//发送文章标题
public string OfficalAccountsTitle { get; set; }
//发送的文章信息
public string OfficalAccountsInfo { get; set; }

//公众号构造函数
public OfficalAccounts(string name, string title, string info) {
OfficalAccountsName = name;
OfficalAccountsTitle = title;
OfficalAccountsInfo = info;
}

// 增加,删除订阅者
public void AddObserver(IObserver observer) {
if (observers.Contains(observer)) {
return;
}
observers.Add(observer);
}
public void DeleteObserver(IObserver observer) {
if (observers.Contains(observer)) {
observers.Remove(observer);
}
}

// 遍历通知每一个订阅者
public void PublishContext() {
foreach (IObserver o in observers) {
if (o != null) {
o.Recieve(this);
}

}
}
}
#endregion

具体公众号类

1
2
3
4
5
6
7
8
#region 具体公众号类
public class MeituanOfficalAccout : OfficalAccounts
{
public MeituanOfficalAccout(string name, string title, string info) : base(name, title, info)
{
}
}
#endregion

订阅者接口

1
2
3
4
5
 #region  订阅者接口
public interface IObserver {
void Recieve(OfficalAccounts offical);
}
#endregion

具体订阅者类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#region 具体订阅者类
public class observer : IObserver {
//订阅者姓名
private string _name;
public string UserName {
get { return _name; }
set { _name = value; }
}
public observer(string name) {
this._name = name;
}
//订阅接受推送函数
public void Recieve(OfficalAccounts offical) {
Console.WriteLine("User\"{0}\"have recieved a post from\"{1}\",the title is :\"{2}\",Content:\"{3}\"",
UserName,offical.OfficalAccountsName,offical.OfficalAccountsTitle,offical.OfficalAccountsInfo);

}
}
#endregion

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#region 测试类
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Start posting...");
Console.WriteLine("----------------------------------------------");
//创建一个推送
OfficalAccounts meituan = new MeituanOfficalAccout("meituan", "Sale", "ALL Food 50% Sale!!!!!!");
//创建用户
observer user1 = new observer("baby");
observer user2 = new observer("yang");
observer user3 = new observer("kim");
//使用户关注公众号
meituan.AddObserver(user1);
meituan.AddObserver(user2);
meituan.AddObserver(user3);
//美团发送推送
meituan.PublishContext();
Console.WriteLine("/////////////////////////////////////////");
Console.WriteLine("baby cancled follow about meituan.");
Console.WriteLine("-----------------------------------------");
//用户baby取消了对美团公众号的订阅
meituan.DeleteObserver(user1);
//美团推送
meituan.PublishContext();

}
}
#endregion

最终运行结果如图

用委托来实现观察者模式

之前我们已经讲过委托在C#中的重要作用,现在我们来用委托来实现上面的观察者模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
namespace observerDelegate
{

//定义委托代替订阅者接口类
public delegate void NotifyEventHandler(object sender);

#region 公众号抽象类
public class OfficalAccounts
{
public NotifyEventHandler notifyEvent;
//公众号名称
public string OfficalAccountsName { get; set; }
//发送文章标题
public string OfficalAccountsTitle { get; set; }
//发送的文章信息
public string OfficalAccountsInfo { get; set; }

//公众号构造函数
public OfficalAccounts(string name, string title, string info)
{
OfficalAccountsName = name;
OfficalAccountsTitle = title;
OfficalAccountsInfo = info;
}

// 增加,删除订阅者
public void AddObserver(NotifyEventHandler observer)
{
notifyEvent += observer;
}
public void DeleteObserver(NotifyEventHandler observer)
{
notifyEvent -= observer;
}

// 遍历通知每一个订阅者
public void PublishContext()
{
if (notifyEvent!=null) {
notifyEvent(this);
}
}
}
#endregion

具体公众号类

1
2
3
4
5
6
7
8
9
#region 具体公众号类
public class MeituanOfficalAccout : OfficalAccounts
{
public MeituanOfficalAccout(string name, string title, string info) : base(name, title, info)
{
}

}
#endregion

具体订阅者类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#region 具体订阅者类
public class observer
{
//订阅者姓名
private string _name;
public string UserName
{
get { return _name; }
set { _name = value; }
}
public observer(string name)
{
this._name = name;
}
//订阅者接收推送
public void Recieve(object obj)
{
OfficalAccounts offical = obj as OfficalAccounts;
Console.WriteLine("User\"{0}\"have recieved a post from\"{1}\",the title is :\"{2}\",Content:\"{3}\"",
UserName, offical.OfficalAccountsName, offical.OfficalAccountsTitle, offical.OfficalAccountsInfo);

}
}
#endregion

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#region 测试类
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Start posting...");
Console.WriteLine("----------------------------------------------");
//创建一个推送
OfficalAccounts meituan = new MeituanOfficalAccout("meituan", "Sale", "ALL Food 50% Sale!!!!!!");
//创建用户
observer user1 = new observer("baby");
observer user2 = new observer("yang");
observer user3 = new observer("kim");

//使用户关注公众号
meituan.AddObserver(new NotifyEventHandler(user1.Recieve));
meituan.AddObserver(new NotifyEventHandler(user2.Recieve));
meituan.AddObserver(new NotifyEventHandler(user3.Recieve));
//美团发送推送
meituan.PublishContext();
Console.WriteLine("/////////////////////////////////////////");
Console.WriteLine("baby cancled follow about meituan.");
Console.WriteLine("-----------------------------------------");
//用户baby取消了对美团公众号的订阅
meituan.DeleteObserver(new NotifyEventHandler(user1.Recieve));
//美团推送
meituan.PublishContext();

}
}
#endregion
}

以上。


[1]:参考 http://www.vkadoo.cn/3E618377A5ECBF5B31C32794D00E464E.AHtml
[2]:参考 http://www.newbieol.com/information/1766.html
[3]: 参考 http://www.cnblogs.com/zhili/p/ObserverPattern.html

如果觉得对您有帮助,就扫我交个朋友吧!