认识观察者模式
(
设计模式
)
-、开题
设计模式就好比
倚
天屠龙记里的兵法书武穆遗书哦,看了设计模式就会惊呼,
软件设计中
你已经在不知不觉使用,或者碰到了但尚未解决的,或者至今还没意识到的问题,上边都有了经验总结
;
学习了想必事半功倍,具体还要在实践中不断尝试。
设计模式一书已经被存储到大家知识库里并且逐步重视起来基础上,
这次我们来认识和讨论一下观察者模式,
观察者模式:
这里不多说,空间留给大家,可以从观察者模式的背景、意图、适用范围、优点、缺点、最好结合例子谈谈。
二讨论
代
Charley Chen
跟贴
:
实现观察者模式有很多形式,比较直观的一种是使用一种
“
注册
——
通知
——
撤销注册
”
的形式。下面的三个图详细的描述了这样一种过程:
1
:观察者(
Observer
)将自己注册到被观察对象(
Subject
)中,被观察对象将观察者存放在一个容器(
Container
)里。
2
:被观察对象发生了某种变化(如图中的
AskPriceChanged
),从容器中得到所有注册过的观察者,将变化通知观察者。
此主题相关图片如下:
3
:观察者告诉被观察者要撤销观察,被观察者从容器中将观察者去除。
此主题相关图片如下:
观察者将自己注册到被观察者的容器中时,被观察者不应该过问观察者的具体类型,而是应该使用观察者的接口。这样的优点是:假定程序中还有别的观察者,那么只要这个观察者也是相同的接口实现即可。一个被观察者可以对应多个观察者,当被观察者发生变化的时候,他可以将消息一一通知给所有的观察者。基于接口,而不是具体的实现
——
这一点为程序提供了更大的灵活性。
(一)通过别名从不同角度来理解
发布
-
订阅(
Publish/Subscribe
)模式:状态变更的动作基本上是由被观察者发出,因此这个别名比观察者更加贴切
模型
-
视图(
Model/View
)模式:该模式重要的应用场合,也是最为适合的应用场合,常常作为示例
源
-
监听器(
Source/Listener
)模式:?
从属者(
Dependents
)模式:?
(二)
C#.NET
中可以通过
Delegate
与
Event
机制简化
Observer
模式
详见下,对于
.NET
还不是很熟,例子摘录自
C#
设计模式
(三)
Java
中内建了类
java.util.Observerable
和接口
java.util.Observer
,是对
Observer
的简单实现
详见下,链了一篇介绍
实际上在
C#
中实现
Observer
模式没有这么辛苦,
.NET
中提供了
Delegate
与
Event
机制,我们可以利用这种机制简化
Observer
模式。关于
Delegate
与
Event
的使用方法请参考相关文档。改进后的
Observer
模式实现如下:
// Observer pattern -- Structural example
using System;
//Delegate
delegate void UpdateDelegate();
//Subject
class Subject
{
public event UpdateDelegate UpdateHandler;
// Methods
public void Attach( UpdateDelegate ud )
{
UpdateHandler += ud;
}
public void Detach( UpdateDelegate ud )
{
UpdateHandler -= ud;
}
public void Notify()
{
if (UpdateHandler != null ) UpdateHandler();
}
}
//ConcreteSubject
class ConcreteSubject : Subject
{
// Fields
private string subjectState;
// Properties
public string SubjectState
{
get { return subjectState; }
set { subjectState = value; }
}
}
// "ConcreteObserver"
class ConcreteObserver
{
// Fields
private string name;
private string observerState;
private ConcreteSubject subject;
// Constructors
public ConcreteObserver( ConcreteSubject subject,
string name )
{
this .subject = subject;
this .name = name;
}
// Methods
public void Update()
{
observerState = subject.SubjectState;
Console.WriteLine( "Observer {0}'s new state is {1}",
name, observerState );
}
// Properties
public ConcreteSubject Subject
{
get { return subject; }
set { subject = value; }
}
}
// "ConcreteObserver"
class AnotherObserver
{
// Methods
public void Show()
{
Console.WriteLine("AnotherObserver got an Notification!");
}
}
public class Client
{
public static void Main( string [] args)
{
ConcreteSubject s = new ConcreteSubject();
ConcreteObserver o1 = new ConcreteObserver(s, "1");
ConcreteObserver o2 = new ConcreteObserver(s, "2");
AnotherObserver o3 = new AnotherObserver();
s.Attach( new UpdateDelegate(o1.Update));
s.Attach( new UpdateDelegate(o2.Update));
s.Attach( new UpdateDelegate(o3.Show));
s.SubjectState = "ABC";
s.Notify();
Console.WriteLine("--------------------------");
s.Detach( new UpdateDelegate(o1.Update));
s.SubjectState = "DEF";
s.Notify();
}
}
using System;
//Delegate
delegate void UpdateDelegate();
//Subject
class Subject
{
public event UpdateDelegate UpdateHandler;
// Methods
public void Attach( UpdateDelegate ud )
{
UpdateHandler += ud;
}
public void Detach( UpdateDelegate ud )
{
UpdateHandler -= ud;
}
public void Notify()
{
if (UpdateHandler != null ) UpdateHandler();
}
}
//ConcreteSubject
class ConcreteSubject : Subject
{
// Fields
private string subjectState;
// Properties
public string SubjectState
{
get { return subjectState; }
set { subjectState = value; }
}
}
// "ConcreteObserver"
class ConcreteObserver
{
// Fields
private string name;
private string observerState;
private ConcreteSubject subject;
// Constructors
public ConcreteObserver( ConcreteSubject subject,
string name )
{
this .subject = subject;
this .name = name;
}
// Methods
public void Update()
{
observerState = subject.SubjectState;
Console.WriteLine( "Observer {0}'s new state is {1}",
name, observerState );
}
// Properties
public ConcreteSubject Subject
{
get { return subject; }
set { subject = value; }
}
}
// "ConcreteObserver"
class AnotherObserver
{
// Methods
public void Show()
{
Console.WriteLine("AnotherObserver got an Notification!");
}
}
public class Client
{
public static void Main( string [] args)
{
ConcreteSubject s = new ConcreteSubject();
ConcreteObserver o1 = new ConcreteObserver(s, "1");
ConcreteObserver o2 = new ConcreteObserver(s, "2");
AnotherObserver o3 = new AnotherObserver();
s.Attach( new UpdateDelegate(o1.Update));
s.Attach( new UpdateDelegate(o2.Update));
s.Attach( new UpdateDelegate(o3.Show));
s.SubjectState = "ABC";
s.Notify();
Console.WriteLine("--------------------------");
s.Detach( new UpdateDelegate(o1.Update));
s.SubjectState = "DEF";
s.Notify();
}
}
其中,关键的代码如下:
delegate
void
UpdateDelegate();
定义一个
Delegate
,用来规范函数结构。不管是
ConcreteObserver
类的
Update
方法还是
AnotherObserver
类的
Show
方法都符合该
Delegate
。这不象用
Observer
接口来规范必须使用
Update
方法那么严格。只要符合
Delegate
所指定的方法结构的方法都可以在后面被事件所处理。
public
event
UpdateDelegate UpdateHandler;
定义一个事件,一旦触发,可以调用一组符合
UpdateDelegate
规范的方法。
public
void
Attach( UpdateDelegate ud )
{
UpdateHandler += ud;
}
{
UpdateHandler += ud;
}
订阅事件。只要是一个满足
UpdateDelegate
的方法,就可以进行订阅操作(如下所示)。
s.Attach(
new
UpdateDelegate(o1.Update));
s.Attach( new UpdateDelegate(o2.Update));
s.Attach( new UpdateDelegate(o3.Show));
s.Attach( new UpdateDelegate(o2.Update));
s.Attach( new UpdateDelegate(o3.Show));
在
Notify
方法中:
public
void
Notify()
{
if (UpdateHandler != null ) UpdateHandler();
}
{
if (UpdateHandler != null ) UpdateHandler();
}
只要
UpdateHandler != null
(表示有订阅者),就可以触发事件(
UpdateHandler()
),所有的订阅者便会接到通知。
关于事件的使用,有一点还是值得注意:
一般我们认为:全部的事件处理器都应该返回
void
并接受两个参数:第一个参数为一个对象,它代表产生事件的对象;第二个参数是从
System.EventArgs
中派生而来的类的对象。
EventArgs
是事件数据的基类,并代表了事件的细节。
摘自范亮的举例
:
ConcreteSubject s = new ConcreteSubject();
ConcreteObserver o1 = new ConcreteObserver(s, "1");
ConcreteObserver o2 = new ConcreteObserver(s, "2");
AnotherObserver o3 = new AnotherObserver();
s.Attach(new UpdateDelegate(o1.Update));
s.Attach(new UpdateDelegate(o2.Update));
s.Attach(new UpdateDelegate(o3.Show));
ConcreteObserver o1 = new ConcreteObserver(s, "1");
ConcreteObserver o2 = new ConcreteObserver(s, "2");
AnotherObserver o3 = new AnotherObserver();
s.Attach(new UpdateDelegate(o1.Update));
s.Attach(new UpdateDelegate(o2.Update));
s.Attach(new UpdateDelegate(o3.Show));
如果
o1,o2 ,o3
有统一的接口去响应主题的
UpdateHandler
事件
,
那就可以去实现
(
继承
)
同一接口
IObserver,
编程会变的更加简练
,
当然这是锦上天花的事
,
例子已经能够说明观察者模式的思想
.
下面我就随便谈谈 观察者模式 的 背景、意图、适用范围、优点、缺点 ...
下面我就随便谈谈 观察者模式 的 背景、意图、适用范围、优点、缺点 ...
先谈背景
:
还是举个列子
A 类
Class A
{
function funA()
{
}
}
B 类
Class B
{
function funB()
{
}
}
C 类
Class C
{
function funC()
{
A.funA();
B.funB();
}
}
还是举个列子
A 类
Class A
{
function funA()
{
}
}
B 类
Class B
{
function funB()
{
}
}
C 类
Class C
{
function funC()
{
A.funA();
B.funB();
}
}
如果观察者模式可以总结成下面的应用
:
定义对象间的一种一对多的依赖关系 , 当一个对象的状态发生改变时 , 所有依赖于它的对象都得到通知并被自动更新。 ( 摘自新版设计模式手册 [C#].pdf)
定义对象间的一种一对多的依赖关系 , 当一个对象的状态发生改变时 , 所有依赖于它的对象都得到通知并被自动更新。 ( 摘自新版设计模式手册 [C#].pdf)
那上边的例子是否看成观察者模式的最原始的一种呢
?
优点:
是实现了表示层和数据逻辑层的分离,并定义了稳定的更新消息传递机制,类别清晰,并抽象了更新接口,使得可以有各种各样不同的表示层
缺点:
如果
1
对
N
的
N
很大时,花销较大。
三:总结
观察者模式的讨论先告一段落 , 主要还要在实践中慢慢体会.
1. 在分层设计中,同层类的交互,就是横向调用比如在 Bussiness 层,如果有当一个对象的状态发生改变时 , 所有依赖于它的对象都得到通知并被自动更新的,我们可以尝试观察者模式,从而降低类之间的耦合性,我
写的例子是一种强耦合的,一定程度上讲破坏了类的高内聚低耦合及封闭原则.
2.C# 编程中 Delegate-Event 就是很好的观察者模式原形,我们要多用它.
以下摘自 C# 设计模式一书,讲的蛮好:
观察者模式的效果有以下几个优点:
观察者模式的讨论先告一段落 , 主要还要在实践中慢慢体会.
1. 在分层设计中,同层类的交互,就是横向调用比如在 Bussiness 层,如果有当一个对象的状态发生改变时 , 所有依赖于它的对象都得到通知并被自动更新的,我们可以尝试观察者模式,从而降低类之间的耦合性,我
写的例子是一种强耦合的,一定程度上讲破坏了类的高内聚低耦合及封闭原则.
2.C# 编程中 Delegate-Event 就是很好的观察者模式原形,我们要多用它.
以下摘自 C# 设计模式一书,讲的蛮好:
观察者模式的效果有以下几个优点:
(
1
)观察者模式在被观察者和观察者之间建立一个抽象的耦合。被观察者角色所知道的只是一个具体现察者聚集,每一个具体现察者都符合一个抽象观察者的接口。被观察者并不认识任何一个具体观察者,它只知道它们都有一个共同的接口。由于被观察者和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。
(
2
)观察者模式支持广播通信。被观察者会向所有的登记过的观察者发出通知。
观察者模式有下面的一些缺点:
(
1
)如果一个被观察者对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
(
2
)
如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察考模式时要特别注意这一点。
(
3
)如果对观察者的通知是通过另外的线程进行异步投递的话,系统必须保证投递是以自恰的方式进行的。
(
4
)虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎么发生变化的。