注解:观察者模式构造出对象之间一对多的代懒关系。
要点:抽取容易发生变化的部分把这些可能会随着日后需求改变的"一组行为"抽取出来,变成"一族算法"。
总结:1.多用包含,少用继承(解除继承所代来的单一性) 。
2.抽取可能会发生变化的部分,既使在项目初期所能预料到的变化很少。
3.面向父类或接口编程。
项目结构如下图:
为了清晰思路,特做了个小图
就好像报社(主题)天天再印报纸,所有的用户(观察者)每天都能收到报社送来的最新的报纸。如果用户(观察者)不想订阅报纸了,那么只要告诉报社不要给我报纸就可以了。
程序的设计理念为 主题控制观察者的接口,通知观察者是否有新的数据需要更新。观察者控制主题的接口随时告诉主题,是否增加了新的观察者。以便主题在一下次数据更新时能知道又来了新的观察者,并为其发送数据。
代码说话:
主题接口 1 using System.Collections.Generic;
2 using 观察者模式.Observer;
3
4 namespace 观察者模式.Subject
5 {
6 /// <summary>
7 /// 主题
8 /// </summary>
9 public interface ISubject
10 {
11 /// <summary>
12 /// 多个观察者
13 /// </summary>
14 List<IObserver> Observers { get; set; }
15
16 /// <summary>
17 /// 向主题注册观察者
18 /// </summary>
19 /// <param name="observer"></param>
20 void RegisterObserver(IObserver observer);
21
22 /// <summary>
23 /// 向主题删除观察者
24 /// </summary>
25 /// <param name="observer"></param>
26 void RemoveObserver(IObserver observer);
27
28 /// <summary>
29 /// 通知观察者
30 /// </summary>
31 void NotifyObserver();
32 }
33 }
34
2 using 观察者模式.Observer;
3
4 namespace 观察者模式.Subject
5 {
6 /// <summary>
7 /// 主题
8 /// </summary>
9 public interface ISubject
10 {
11 /// <summary>
12 /// 多个观察者
13 /// </summary>
14 List<IObserver> Observers { get; set; }
15
16 /// <summary>
17 /// 向主题注册观察者
18 /// </summary>
19 /// <param name="observer"></param>
20 void RegisterObserver(IObserver observer);
21
22 /// <summary>
23 /// 向主题删除观察者
24 /// </summary>
25 /// <param name="observer"></param>
26 void RemoveObserver(IObserver observer);
27
28 /// <summary>
29 /// 通知观察者
30 /// </summary>
31 void NotifyObserver();
32 }
33 }
34
观察者接口
1 using 观察者模式.Subject;
2
3 namespace 观察者模式.Observer
4 {
5 /// <summary>
6 /// 观察者
7 /// </summary>
8 public interface IObserver
9 {
10 /// <summary>
11 /// 主题
12 /// </summary>
13 ISubject Subject { get; set; }
14
15 /// <summary>
16 /// 更新观察者数据
17 /// </summary>
18 /// <param name="pressure">气压</param>
19 /// <param name="temp">温度</param>
20 /// <param name="direction">风向</param>
21 void UpdateData(float pressure, float temp, string direction);
22 }
23 }
24
2
3 namespace 观察者模式.Observer
4 {
5 /// <summary>
6 /// 观察者
7 /// </summary>
8 public interface IObserver
9 {
10 /// <summary>
11 /// 主题
12 /// </summary>
13 ISubject Subject { get; set; }
14
15 /// <summary>
16 /// 更新观察者数据
17 /// </summary>
18 /// <param name="pressure">气压</param>
19 /// <param name="temp">温度</param>
20 /// <param name="direction">风向</param>
21 void UpdateData(float pressure, float temp, string direction);
22 }
23 }
24
显示数据接口
1 namespace 观察者模式.Observer
2 {
3 /// <summary>
4 /// 显示数据
5 /// </summary>
6 public interface IDisplayElement
7 {
8 void DisplayData(string displayData);
9 }
10 }
11
2 {
3 /// <summary>
4 /// 显示数据
5 /// </summary>
6 public interface IDisplayElement
7 {
8 void DisplayData(string displayData);
9 }
10 }
11
具体的主题
1 using System.Collections.Generic;
2 using 观察者模式.Observer;
3
4 namespace 观察者模式.Subject
5 {
6 /// <summary>
7 /// 此类为具体的主题
8 /// 假设此类是从气向局提取数据
9 /// </summary>
10 public class WeaterData : ISubject
11 {
12 public WeaterData()
13 {
14 Observers = new List<IObserver>();
15 }
16
17 public List<IObserver> Observers { get; set; }
18
19 #region 属性
20 /// <summary>
21 /// 气压
22 /// </summary>
23 public float Pressure { get; set; }
24
25 /// <summary>
26 /// 温度
27 /// </summary>
28 public float Temp { get; set; }
29
30 /// <summary>
31 /// 风向
32 /// </summary>
33 public string Direction { get; set; }
34 #endregion
35
36 /// <summary>
37 /// 向主题注册观察者
38 /// </summary>
39 /// <param name="observer"></param>
40 public void RegisterObserver(IObserver observer)
41 {
42 Observers.Add(observer);
43 }
44
45 /// <summary>
46 /// 向主题删除观察者
47 /// </summary>
48 /// <param name="observer"></param>
49 public void RemoveObserver(IObserver observer)
50 {
51 var index = Observers.IndexOf(observer);
52 Observers.RemoveAt(index);
53 }
54
55 /// <summary>
56 /// 通知观察者
57 /// </summary>
58 public void NotifyObserver()
59 {
60 for (int i = 0, j = Observers.Count; i < j; i++)
61 {
62 Observers[i].UpdateData(Pressure, Temp, Direction);
63 }
64 }
65
66 /// <summary>
67 /// 当天气改变的时候
68 /// 会去通知每一个观察者
69 /// </summary>
70 /// <param name="pressure"></param>
71 /// <param name="temp"></param>
72 /// <param name="direction"></param>
73 public void WeaterChanged(float pressure, float temp, string direction)
74 {
75 this.Pressure = pressure;
76 this.Temp = temp;
77 this.Direction = direction;
78 NotifyObserver();
79 }
80 }
81 }
82
2 using 观察者模式.Observer;
3
4 namespace 观察者模式.Subject
5 {
6 /// <summary>
7 /// 此类为具体的主题
8 /// 假设此类是从气向局提取数据
9 /// </summary>
10 public class WeaterData : ISubject
11 {
12 public WeaterData()
13 {
14 Observers = new List<IObserver>();
15 }
16
17 public List<IObserver> Observers { get; set; }
18
19 #region 属性
20 /// <summary>
21 /// 气压
22 /// </summary>
23 public float Pressure { get; set; }
24
25 /// <summary>
26 /// 温度
27 /// </summary>
28 public float Temp { get; set; }
29
30 /// <summary>
31 /// 风向
32 /// </summary>
33 public string Direction { get; set; }
34 #endregion
35
36 /// <summary>
37 /// 向主题注册观察者
38 /// </summary>
39 /// <param name="observer"></param>
40 public void RegisterObserver(IObserver observer)
41 {
42 Observers.Add(observer);
43 }
44
45 /// <summary>
46 /// 向主题删除观察者
47 /// </summary>
48 /// <param name="observer"></param>
49 public void RemoveObserver(IObserver observer)
50 {
51 var index = Observers.IndexOf(observer);
52 Observers.RemoveAt(index);
53 }
54
55 /// <summary>
56 /// 通知观察者
57 /// </summary>
58 public void NotifyObserver()
59 {
60 for (int i = 0, j = Observers.Count; i < j; i++)
61 {
62 Observers[i].UpdateData(Pressure, Temp, Direction);
63 }
64 }
65
66 /// <summary>
67 /// 当天气改变的时候
68 /// 会去通知每一个观察者
69 /// </summary>
70 /// <param name="pressure"></param>
71 /// <param name="temp"></param>
72 /// <param name="direction"></param>
73 public void WeaterChanged(float pressure, float temp, string direction)
74 {
75 this.Pressure = pressure;
76 this.Temp = temp;
77 this.Direction = direction;
78 NotifyObserver();
79 }
80 }
81 }
82
观察者一
1 using 观察者模式.Subject;
2
3 namespace 观察者模式.Observer
4 {
5 /// <summary>
6 /// 中国天气
7 /// 中国观察者
8 /// </summary>
9 public class ChinaWeater : IObserver, IDisplayElement
10 {
11 public ChinaWeater(ISubject subject)
12 {
13 this.Subject = subject;
14 this.Subject.RegisterObserver(this);//告诉主题我来了
15 }
16
17 /// <summary>
18 /// 这个属性似乎没什么意义
19 /// 但是我们在删除观察者时可能会用到它的引用哦。
20 /// </summary>
21 public ISubject Subject { get; set; }
22
23 /// <summary>
24 /// 更新数据
25 /// </summary>
26 /// <param name="pressure"></param>
27 /// <param name="temp"></param>
28 /// <param name="direction"></param>
29 public void UpdateData(float pressure, float temp, string direction)
30 {
31 var displayData = string.Format("中国天气\r\n气压:{0}\r\n温度:{1}\r\n风向:{2}", pressure, temp, direction);
32 DisplayData(displayData);
33 }
34
35 /// <summary>
36 /// 显示数据
37 /// </summary>
38 /// <returns></returns>
39 public void DisplayData(string displayData)
40 {
41 System.Windows.Forms.MessageBox.Show(displayData);
42 }
43 }
44 }
45
2
3 namespace 观察者模式.Observer
4 {
5 /// <summary>
6 /// 中国天气
7 /// 中国观察者
8 /// </summary>
9 public class ChinaWeater : IObserver, IDisplayElement
10 {
11 public ChinaWeater(ISubject subject)
12 {
13 this.Subject = subject;
14 this.Subject.RegisterObserver(this);//告诉主题我来了
15 }
16
17 /// <summary>
18 /// 这个属性似乎没什么意义
19 /// 但是我们在删除观察者时可能会用到它的引用哦。
20 /// </summary>
21 public ISubject Subject { get; set; }
22
23 /// <summary>
24 /// 更新数据
25 /// </summary>
26 /// <param name="pressure"></param>
27 /// <param name="temp"></param>
28 /// <param name="direction"></param>
29 public void UpdateData(float pressure, float temp, string direction)
30 {
31 var displayData = string.Format("中国天气\r\n气压:{0}\r\n温度:{1}\r\n风向:{2}", pressure, temp, direction);
32 DisplayData(displayData);
33 }
34
35 /// <summary>
36 /// 显示数据
37 /// </summary>
38 /// <returns></returns>
39 public void DisplayData(string displayData)
40 {
41 System.Windows.Forms.MessageBox.Show(displayData);
42 }
43 }
44 }
45
观察者2
1 using 观察者模式.Subject;
2
3 namespace 观察者模式.Observer
4 {
5 /// <summary>
6 /// 美国天气
7 /// </summary>
8 public class USAWeater : IObserver, IDisplayElement
9 {
10 public USAWeater(ISubject subject)
11 {
12 this.Subject = subject;
13 this.Subject.RegisterObserver(this); //告诉主题我来了
14 }
15
16 /// <summary>
17 /// 这个属性似乎没什么意义
18 /// 但是我们在删除观察者时可能会用到它的引用哦。
19 /// </summary>
20 public ISubject Subject { get; set; }
21
22 /// <summary>
23 /// 更新数据
24 /// </summary>
25 /// <param name="pressure"></param>
26 /// <param name="temp"></param>
27 /// <param name="direction"></param>
28 public void UpdateData(float pressure, float temp, string direction)
29 {
30 var displayData = string.Format("美国天气\r\n气压:{0}\r\n温度:{1}\r\n风向:{2}\r\n温度和气压总计:{3}", pressure, temp, direction, (pressure + temp));
31 DisplayData(displayData);
32 }
33
34 /// <summary>
35 /// 显示数据
36 /// </summary>
37 /// <returns></returns>
38 public void DisplayData(string displayData)
39 {
40 System.Windows.Forms.MessageBox.Show(displayData);
41 }
42 }
43 }
44
2
3 namespace 观察者模式.Observer
4 {
5 /// <summary>
6 /// 美国天气
7 /// </summary>
8 public class USAWeater : IObserver, IDisplayElement
9 {
10 public USAWeater(ISubject subject)
11 {
12 this.Subject = subject;
13 this.Subject.RegisterObserver(this); //告诉主题我来了
14 }
15
16 /// <summary>
17 /// 这个属性似乎没什么意义
18 /// 但是我们在删除观察者时可能会用到它的引用哦。
19 /// </summary>
20 public ISubject Subject { get; set; }
21
22 /// <summary>
23 /// 更新数据
24 /// </summary>
25 /// <param name="pressure"></param>
26 /// <param name="temp"></param>
27 /// <param name="direction"></param>
28 public void UpdateData(float pressure, float temp, string direction)
29 {
30 var displayData = string.Format("美国天气\r\n气压:{0}\r\n温度:{1}\r\n风向:{2}\r\n温度和气压总计:{3}", pressure, temp, direction, (pressure + temp));
31 DisplayData(displayData);
32 }
33
34 /// <summary>
35 /// 显示数据
36 /// </summary>
37 /// <returns></returns>
38 public void DisplayData(string displayData)
39 {
40 System.Windows.Forms.MessageBox.Show(displayData);
41 }
42 }
43 }
44
观察者3
1 using System;
2 using 观察者模式.Subject;
3
4 namespace 观察者模式.Observer
5 {
6 /// <summary>
7 /// 德国天气
8 /// 德国观察者
9 /// </summary>
10 public class GermanyWeater : IObserver, IDisplayElement
11 {
12 public GermanyWeater(ISubject subject)
13 {
14 this.Subject = subject;
15 this.Subject.RegisterObserver(this); //告诉主题我来了
16 }
17
18 /// <summary>
19 /// 这个属性似乎没什么意义
20 /// 但是我们在删除观察者时可能会用到它的引用哦。
21 /// </summary>
22 public ISubject Subject { get; set; }
23
24 /// <summary>
25 /// 更新数据
26 /// </summary>
27 /// <param name="pressure"></param>
28 /// <param name="temp"></param>
29 /// <param name="direction"></param>
30 public void UpdateData(float pressure, float temp, string direction)
31 {
32 var displayData = string.Format("德国天气\r\n气压:{0}\r\n温度:{1}\r\n风向:{2}\r\n气温最大值{3}", pressure, temp, direction, Math.Abs(pressure));
33 DisplayData(displayData);
34 }
35
36 /// <summary>
37 /// 显示数据
38 /// </summary>
39 /// <returns></returns>
40 public void DisplayData(string displayData)
41 {
42 System.Windows.Forms.MessageBox.Show(displayData);
43 }
44 }
45 }
46
2 using 观察者模式.Subject;
3
4 namespace 观察者模式.Observer
5 {
6 /// <summary>
7 /// 德国天气
8 /// 德国观察者
9 /// </summary>
10 public class GermanyWeater : IObserver, IDisplayElement
11 {
12 public GermanyWeater(ISubject subject)
13 {
14 this.Subject = subject;
15 this.Subject.RegisterObserver(this); //告诉主题我来了
16 }
17
18 /// <summary>
19 /// 这个属性似乎没什么意义
20 /// 但是我们在删除观察者时可能会用到它的引用哦。
21 /// </summary>
22 public ISubject Subject { get; set; }
23
24 /// <summary>
25 /// 更新数据
26 /// </summary>
27 /// <param name="pressure"></param>
28 /// <param name="temp"></param>
29 /// <param name="direction"></param>
30 public void UpdateData(float pressure, float temp, string direction)
31 {
32 var displayData = string.Format("德国天气\r\n气压:{0}\r\n温度:{1}\r\n风向:{2}\r\n气温最大值{3}", pressure, temp, direction, Math.Abs(pressure));
33 DisplayData(displayData);
34 }
35
36 /// <summary>
37 /// 显示数据
38 /// </summary>
39 /// <returns></returns>
40 public void DisplayData(string displayData)
41 {
42 System.Windows.Forms.MessageBox.Show(displayData);
43 }
44 }
45 }
46
一个测试的小应用程序
UML图如下: