观察者模式
概述
在设计一款软件时,我们经常需要这样的功能,当某个状态发生变化,其它控件也相应的更具这个状态调整自己的属性,我们需要为这些对象建立一种“通知依赖关系”。如果依赖关系过于的紧密,那么当代码修改时,不能抵御变化,会牵涉到大量的修改。使用面向对象技术,可以使依赖关系弱化,形成稳定的依赖关系。从而实现软件体系结构的松耦合。
定义
GOF 给观察者模式是这么定义的:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
结构图
生活中的例子
观察者定义了对象间一对多的关系,当一个对象的状态变化时,所有依赖它的对象都得到通知并且自动地更新。现在又拿经典案例热水器烧水作为例子。现在假设热水器由三部分组成:热水器、警报器、显示器。报警器和显示器告诉热水器,对温度感兴趣。然后热水器烧水时,当温度达到一定值后,就通知报警器和显示器,报警器鸣叫,显示器显示警告信息。 类似这样的例子是很多的,GOF对它进行了抽象,称为Observer设计模式。
Observer模式分析
下面通过一个例子来说明Obversor模式。监控家里的花园,如果有贼进来,就将拍照,并发送短信到主人手机。
实现方法(本人对C#熟悉,故这里只用C#来实现Observer模式):
Cam.cs
oberservers用来存放所有的观察者,通过方法Add和Remove来添加和删除观察者。
warning和picture用来模拟警告信息和贼的照片。
update方法将会依次调用观察者自己的SendData方法,并将本实例的warning和picture的值传递过去。
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace uml
7 {
8 public abstract class Cam
9 {
10 private List<IObserver> observers = new List<IObserver>();
11
12 private String warning;
13
14 private String picture;
15
16 public Cam(String warning, String picture)
17 {
18 this.warning = warning;
19
20 this.picture = picture;
21 }
22
23 public void Update()
24 {
25 foreach (IObserver o in observers)
26 {
27 o.SendData(this);
28 }
29 }
30
31 public void AddObserver(IObserver observer)
32 {
33 observers.Add(observer);
34 }
35
36 public void RemoveObserver(IObserver observer)
37 {
38 observers.Remove(observer);
39 }
40
41 public String Warning
42 {
43 get { return warning; }
44 set { warning = value; }
45 }
46
47 public String Picture
48 {
49 get { return picture; }
50 set { picture = value; }
51 }
52 }
53 }
MyCam.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace uml
7 {
8 public class MyCam : Cam
9 {
10 public MyCam(String warning, String picture)
11 : base(warning, picture)
12 { }
13 }
14 }
IObserver.cs
将Observer抽象出来,可以避免Cam对具体哪个观察者的依赖。Cam类中只对Iobserver依赖。
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace uml
7 {
8 public interface IObserver
9 {
10 void SendData(Cam cam);
11 }
12 }
Host.cs
观察者具体实例。其中有两个字段。_name是主人的名字。SendData方法用来显示具体的警告信息和贼的照片。
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace uml
7 {
8 public class Host : IObserver
9 {
10 private string _name;
11
12 public Host(string name)
13 {
14 this._name = name;
15 }
16
17 public void SendData(Cam cam)
18 {
19 Console.WriteLine("Host:{0},Notified{1},Picture: {2}",
20 _name, cam.Warning,cam.Picture);
21 }
22 }
23 }
HostMobile.cs
观察者实例。实现IOberver接口。这样,可以让Cam类不直接和观察者具体实例发生联系。避免依赖紧密。
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace uml
7 {
8 public class HostMobile : IObserver
9 {
10 private string _name;
11
12 public HostMobile(string name)
13 {
14 this._name = name;
15 }
16
17 public void SendData(Cam cam)
18 {
19 Console.WriteLine("MSG:Host:{0},Notified{1},Picture:{2}",
20 _name,cam.Warning,cam.Picture);
21 }
22 }
23 }
客户端程序代码如下:
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 Cam mc = new MyCam("Thief!!Be Careful!!","He is so ugly!!");
6
7 mc.AddObserver(new Host("Husband"));
8
9 mc.AddObserver(new HostMobile("Husband"));
10
11 mc.Update();
12
13 Console.ReadLine();
14 }
15 }
运行结果如下:
Host:Husband,Notified Thief!!Be Careful!!,Picture: He is so ugly!!
MSG:Host:Husband,Notified Thief!!Be Careful!!,Picture: He is so ugly!!
分析:
静态uml图:
在Cam类中,observers用来存放所有的观察者,IOberser充当了观察者的角色,而Cam则扮演了主题对象角色,在任何时候,只要调用了Cam的Update()方法,它就会通知它的所有观察者对象。同时可以看到,通过Observer模式,取消了直接依赖,变为间接依赖,这样大大提供了系统的可维护性和可扩展性。
在生活中,我们的摄像头不可能只有一个,所以在这里对Cam进行了抽象,这样,Host和HostMobile都不依赖于具体的某个Camera,然后也可以看到,MyCam也不依赖于具体的具体的Host,而是依赖于IOberser。
总结
观察者模式所做的工作其实就是解除耦合,让耦合的双方都依赖于抽象,从而使得各自的变化不会影响到另一边。
附:对UML与设计模式课程的意见和建议
1)部分学生不热爱学习,对课程不感兴趣,或者不知到怎么学习这么课程。老师可以在这么课还没开始上的时候,给我们一个详细的学习规划,如何学习好这么课程。
2)课堂气氛还不够活跃,希望老师可以采取各种方法活跃课堂气氛。
参考文献
.net设计模式19 观察者模式(Observer Pattern)http://www.cnblogs.com/terrylee/archive/2006/10/23/observer_pattern.html
大话设计模式 清华大学出版社 程杰
徐佳良
2011.6.7