服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - C# 设计模式系列教程-观察者模式

C# 设计模式系列教程-观察者模式

2021-11-23 15:00Wang Juqiang C#

将一个系统分割成一个一些类相互协作的类有一个不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。观察者就是解决这类的耦合关系的。

1. 概述

  有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

2. 解决的问题

  将一个系统分割成一个一些类相互协作的类有一个不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。观察者就是解决这类的耦合关系的。

3. 模式中的角色

  3.1 抽象主题(subject):它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。

  3.2 具体主题(concretesubject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。

  3.3 抽象观察者(observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。

  3.4 具体观察者(concreteobserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。

4. 模式解读

  4.1 观察者模式的类图  

C# 设计模式系列教程-观察者模式

  4.2 观察者模式的代码

?
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/// <summary>
/// 抽象主题类
/// </summary>
public abstract class subject
{
private ilist<observer> observers = new list<observer>();
 
/// <summary>
/// 增加观察者
/// </summary>
/// <param name="observer"></param>
public void attach(observer observer)
{
 observers.add(observer);
}
 
/// <summary>
/// 移除观察者
/// </summary>
/// <param name="observer"></param>
public void detach(observer observer)
{
 observers.remove(observer);
}
 
/// <summary>
/// 向观察者(们)发出通知
/// </summary>
public void notify()
{
 foreach (observer o in observers)
 {
 o.update();
 }
}
}
 
/// <summary>
/// 抽象观察者类,为所有具体观察者定义一个接口,在得到通知时更新自己
/// </summary>
public abstract class observer
{
public abstract void update();
}
 
/// <summary>
/// 具体观察者或具体通知者,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。
/// </summary>
public class concretesubject : subject
{
private string subjectstate;
 
/// <summary>
/// 具体观察者的状态
/// </summary>
public string subjectstate
{
 get { return subjectstate; }
 set { subjectstate = value; }
}
}
 
/// <summary>
/// 具体观察者,实现抽象观察者角色所要求的更新接口,已是本身状态与主题状态相协调
/// </summary>
public class concreteobserver : observer
{
private string observerstate;
private string name;
private concretesubject subject;
 
/// <summary>
/// 具体观察者用一个具体主题来实现
/// </summary>
public concretesubject subject
{
 get { return subject; }
 set { subject = value; }
}
 
public concreteobserver(concretesubject subject, string name)
{
 this.subject = subject;
 this.name = name;
}
 
/// <summary>
/// 实现抽象观察者中的更新操作
/// </summary>
public override void update()
{
 observerstate = subject.subjectstate;
 console.writeline("the observer's state of {0} is {1}", name, observerstate);
}
}

  4.3 客户端代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class program
{
static void main(string[] args)
{
 // 具体主题角色通常用具体自来来实现
 concretesubject subject = new concretesubject();
 
 subject.attach(new concreteobserver(subject, "observer a"));
 subject.attach(new concreteobserver(subject, "observer b"));
 subject.attach(new concreteobserver(subject, "observer c"));
 
 subject.subjectstate = "ready";
 subject.notify();
 
 console.read();
}
}


  运行结果

C# 设计模式系列教程-观察者模式

5. 模式总结

  5.1 优点

    5.1.1 观察者模式解除了主题和具体观察者的耦合,让耦合的双方都依赖于抽象,而不是依赖具体。从而使得各自的变化都不会影响另一边的变化。

  5.2 缺点

    5.2.1 依赖关系并未完全解除,抽象通知者依旧依赖抽象的观察者。

  5.3 适用场景

    5.3.1 当一个对象的改变需要给变其它对象时,而且它不知道具体有多少个对象有待改变时。

    5.3.2 一个抽象某型有两个方面,当其中一个方面依赖于另一个方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。

 

6. 模式引申,应用c#中的事件委托来彻底解除通知者和观察者之间的耦合。

   6.1 关于委托的定义:委托是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法有相同的行为。委托方法可以像其它任何方法一样,具有参数和返回值。委托可以看作是对函数(方法)的的抽象,是函数的“类”,委托的实例代表一个(或多个)具体的函数,它可以是多播的。

   6.2 关于事件:事件基于委托,为委托提供了一种发布/订阅机制。事件的订阅与取消与我们刚才讲的观察者模式中的订阅与取消类似,只是表现形式有所不同。在观察者模式中,订阅使用方法attach()来进行;在事件的订阅中使用“+=”。类似地,取消订阅在观察者模式中用dettach(),而事件的取消用“-=”。

 

7. 下面例子分别用观察者模式,事件机制来实现

  7.1 实例描述:客户支付了订单款项,这时财务需要开具发票,出纳需要记账,配送员需要配货。

  7.2 观察者模式的实现

    7.2.1 类图

C# 设计模式系列教程-观察者模式

    7.2.2 代码实现

?
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/// <summary>
/// 抽象观察者
/// </summary>
public interface isubject
{
void notify();
}
 
/// <summary>
/// 工作岗位,作为这里的观察者的抽象
/// </summary>
public abstract class jobstation
{
public abstract void update();
}
 
/// <summary>
/// 具体主题,这里是客户
/// </summary>
public class customer : isubject
{
private string customerstate;
 
private ilist<jobstation> observers = new list<jobstation>();
 
/// <summary>
/// 增加观察者
/// </summary>
/// <param name="observer"></param>
public void attach(jobstation observer)
{
 this.observers.add(observer);
}
 
/// <summary>
/// 移除观察者
/// </summary>
/// <param name="observer"></param>
public void detach(jobstation observer)
{
 this.observers.remove(observer);
}
 
/// <summary>
/// 客户状态
/// </summary>
public string customerstate
{
 get { return customerstate; }
 set { customerstate = value; }
}
 
public void notify()
{
 foreach (jobstation o in observers)
 {
 o.update();
 }
}
}
 
/// <summary>
/// 会计
/// </summary>
public class accountant : jobstation
{
private string accountantstate;
private customer customer;
 
public accountant(customer customer)
{
 this.customer = customer;
}
 
/// <summary>
/// 更新状态
/// </summary>
public override void update()
{
 if (customer.customerstate == "已付款")
 {
 console.writeline("我是会计,我来开具发票。");
 accountantstate = "已开发票";
 }
}
}
 
/// <summary>
/// 出纳
/// </summary>
public class cashier : jobstation
{
private string cashierstate;
private customer customer;
 
public cashier(customer customer)
{
 this.customer = customer;
}
 
public override void update()
{
 if (customer.customerstate == "已付款")
 {
 console.writeline("我是出纳员,我给登记入账。");
 cashierstate = "已入账";
 }
}
}
 
/// <summary>
/// 配送员
/// </summary>
public class dilliveryman : jobstation
{
private string dillivierymanstate;
private customer customer;
 
public dilliveryman(customer customer)
{
 this.customer = customer;
}
 
public override void update()
{
 if (customer.customerstate == "已付款")
 {
 console.writeline("我是配送员,我来发货。");
 dillivierymanstate = "已发货";
 }
}
}

 

    7.2.3 客户端代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class program
{
static void main(string[] args)
{
 
 customer subject = new customer();
 
 subject.attach(new accountant(subject));
 subject.attach(new cashier(subject));
 subject.attach(new dilliveryman(subject));
 
 subject.customerstate = "已付款";
 subject.notify();
 
 console.read();
}
}

 


    运行结果:

    我是会计,我来开具发票。
    我是出纳员,我给登记入账。
    我是配送员,我来发货。

 

  7.3 事件实现

    7.3.1 类图

C# 设计模式系列教程-观察者模式

    通过类图来看,观察者和主题之间已经不存在任何依赖关系了。

    7.3.2 代码实现

?
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/// <summary>
/// 抽象主题
/// </summary>
public interface isubject
{
void notify();
}
 
/// <summary>
/// 声明委托
/// </summary>
public delegate void customereventhandler();
 
/// <summary>
/// 具体主题
/// </summary>
public class customer : isubject
{
private string customerstate;
 
// 声明一个委托事件,类型为 customereventhandler
public event customereventhandler update;
 
public void notify()
{
 if (update != null)
 {
 // 使用事件来通知给订阅者
 update();
 }
}
 
public string customerstate
{
 get { return customerstate; }
 set { customerstate = value; }
}
}
 
/// <summary>
/// 财务,已经不需要实现抽象的观察者类,并且不用引用具体的主题
/// </summary>
public class accountant
{
private string accountantstate;
 
public accountant()
{ }
 
/// <summary>
/// 开发票
/// </summary>
public void giveinvoice()
{
 console.writeline("我是会计,我来开具发票。");
 accountantstate = "已开发票";
}
}
 
/// <summary>
/// 出纳,已经不需要实现抽象的观察者类,并且不用引用具体的主题
/// </summary>
public class cashier
{
private string cashierstate;
 
public void recoded()
{
 console.writeline("我是出纳员,我给登记入账。");
 cashierstate = "已入账";
}
}
 
/// <summary>
/// 配送员,已经不需要实现抽象的观察者类,并且不用引用具体的主题
/// </summary>
public class dilliveryman
{
private string dillivierymanstate;
 
public void dilliver()
{
 console.writeline("我是配送员,我来发货。");
 dillivierymanstate = "已发货";
}
}

 

    7.3.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
class program
{
static void main(string[] args)
{
 
 customer subject = new customer();
 
 accountant accountant = new accountant();
 cashier cashier = new cashier();
 dilliveryman dilliveryman = new dilliveryman();
 
 // 注册事件
 subject.update += accountant.giveinvoice;
 subject.update += cashier.recoded;
 subject.update += dilliveryman.dilliver;
 
 /*
 * 以上写法也可以用下面代码来替换
 subject.update += new customereventhandler(accountant.giveinvoice);
 subject.update += new customereventhandler(cashier.recoded);
 subject.update += new customereventhandler(dilliveryman.dilliver);
 */
 
 subject.customerstate = "已付款";
 subject.notify();
 
 console.read();
}
}


    运行结果

    我是会计,我来开具发票。
    我是出纳员,我给登记入账。
    我是配送员,我来发货。

延伸 · 阅读

精彩推荐
  • C#C#设计模式之Strategy策略模式解决007大破密码危机问题示例

    C#设计模式之Strategy策略模式解决007大破密码危机问题示例

    这篇文章主要介绍了C#设计模式之Strategy策略模式解决007大破密码危机问题,简单描述了策略模式的定义并结合加密解密算法实例分析了C#策略模式的具体使用...

    GhostRider10972022-01-21
  • C#三十分钟快速掌握C# 6.0知识点

    三十分钟快速掌握C# 6.0知识点

    这篇文章主要介绍了C# 6.0的相关知识点,文中介绍的非常详细,通过这篇文字可以让大家在三十分钟内快速的掌握C# 6.0,需要的朋友可以参考借鉴,下面来...

    雨夜潇湘8272021-12-28
  • C#利用C#实现网络爬虫

    利用C#实现网络爬虫

    这篇文章主要介绍了利用C#实现网络爬虫,完整的介绍了C#实现网络爬虫详细过程,感兴趣的小伙伴们可以参考一下...

    C#教程网11852021-11-16
  • C#C#微信公众号与订阅号接口开发示例代码

    C#微信公众号与订阅号接口开发示例代码

    这篇文章主要介绍了C#微信公众号与订阅号接口开发示例代码,结合实例形式简单分析了C#针对微信接口的调用与处理技巧,需要的朋友可以参考下...

    smartsmile20127762021-11-25
  • C#如何使用C#将Tensorflow训练的.pb文件用在生产环境详解

    如何使用C#将Tensorflow训练的.pb文件用在生产环境详解

    这篇文章主要给大家介绍了关于如何使用C#将Tensorflow训练的.pb文件用在生产环境的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴...

    bbird201811792022-03-05
  • C#VS2012 程序打包部署图文详解

    VS2012 程序打包部署图文详解

    VS2012虽然没有集成打包工具,但它为我们提供了下载的端口,需要我们手动安装一个插件InstallShield。网上有很多第三方的打包工具,但为什么偏要使用微软...

    张信秀7712021-12-15
  • C#深入理解C#的数组

    深入理解C#的数组

    本篇文章主要介绍了C#的数组,数组是一种数据结构,详细的介绍了数组的声明和访问等,有兴趣的可以了解一下。...

    佳园9492021-12-10
  • C#SQLite在C#中的安装与操作技巧

    SQLite在C#中的安装与操作技巧

    SQLite,是一款轻型的数据库,用于本地的数据储存。其优点有很多,下面通过本文给大家介绍SQLite在C#中的安装与操作技巧,感兴趣的的朋友参考下吧...

    蓝曈魅11162022-01-20