[原创]IssueVision 学习笔记(三)-----设计模式之OBSERVER(观察者)模式

这次我们来看看 IssueVision 中的对设计模式的应用, IssueVision 主要使用了 OBSERVER (观察者)模式和 COMMAND (命令)模式.今天就来看看 OBSERVER (观察者)模式在 IssueVision 中的应用,它在 IssueVision 中扮演着重要角色.

"四人帮"GoF是这样定义 ** OBSERVER ** (观察者)模式的------ 定义对象间的一种一对多的关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知,并被自动更新 .
从定义可以看出, ** OBSERVER ** (观察者)模式逻辑上需要两组对象来实现.首先它必需要有 发布者(Publish) ,也可称为被观察的 目标(Subject) (习惯上都称它为 目标Subject ,后面我们都称它作 目标Subject ),另外就是 订阅者(Subscribe) ,习惯上称为 观察者(Observer) .一个目标对象对应多个观察者对象,目标对象发生变化时,所有在目标对象中注册的观察者对象会得到通知,自动更新自己.

在应用程序开发过程中,往往都要求用户界面和业务逻辑分离,划定清晰的界限.因为应用程序要求能快速的更改用户界面并且不能对应用程序其他部分产生连带影响,而且业务逻辑也会发生变化并要求这一切变化与用户界面无关. 观察者(Observer) 就是解决此问题最常用的设计模式,它非常有助于在系统中各个对象之间划分清晰的界限.

下图最好的展示了这种形式,观者者(observer)们(表格对象,柱状图对象和饼状图对象)都依赖数据对象 Subject ,所以数据对象Subject的所有改变都会通知它们.但它们互相之间并不知道对方的存在,表格对象不知道其他表格对象或者其他柱状图对象的存在.对于数据对象 Subject 可以有任何多的观察者,这些观察者都能在Subject对象发生改变时得到通知,使自己的状态与Subject同步:

好吧,概念就说这么多吧,我们来看一下 ** OBSERVER ** (观察者)模式的实现.通用的 ** OBSERVER ** (观察者)模式的实现我在这就不说了,可以参考GoF的设计模式和Java与模式等书.我们就来专注于. NET框架 中 ** OBSERVER ** (观察者)模式的实现.

. NET框架 引入了 ** 委托 ** 和 ** 事件 ** ,它们提供了更新,功能更强大的方法来实现 ** OBSERVER ** (观察者)模式.(关于 ** 委托 ** 和 ** 事件 ** 的更多内容请参考相关文档).如果你不熟悉 ** 委托 ** 和 ** 事件 ** ,实现 ** OBSERVER ** (观察者)模式则需要作很多工作(像在Java中实现那样).下面我就以 IssueVision 中的实现来简单讲述一下.

IssueVisionPatterns 文件夹下有两个文件 ISubject.csIObserver.cs .它们分别定义了目标(Subject)对象和观者者(Observer)对象的接口,代码如下:

ISubject.cs

namespace IssueVision
{
// ISubject is a simple marker interface that supports the implementation
// of the Observer pattern in IssueVision.
public interface ISubject
{
}
}

IObserver.cs

namespace IssueVision
{
// IObserver is a simple interface that supports the implementation of the
// Observer pattern in IssueVision.
public interface IObserver
{
ISubject Subject
{
set;
}
}
}

大家可能发现,这两个接口几乎为空,什么都没有定义,那这两个接口的作用是什么呢?其实定义这两个接口的作用主要为编码的"规范化",只要类实现了这两个接口任何一个,那么就代表此类就实现了 ** OBSERVER ** (观察者)模式,并且很明显的知道谁是 Subject ,谁是 Observer .

IssueVision 中的 IssueSubject 组件就实现ISubject接口,它是 IssueVision 中实现 ** OBSERVER ** (观察者)模式最主要的部分,也是 IssueVision 中最最复杂的一个类.我们慢慢一点一点来分析它(当然之分析 ** OBSERVER ** (观察者)模式相关部分):

IssueSubject.cs

public class IssueSubject : Component, ISubject

首先定义类 IssueSubject 继承自Component和ISubject,从这里和类名可以看出,此类是作为 ** OBSERVER ** (观察者)模式中的 目标(Subject) 实现的. 目标(Subject) 对象要求实现对 观察者(Observer)注册(Register)通知 ,Component类就是用来实现 注册(Register) 的.大多数 目标(Subject) 对象的实现并不是并非将 观察者(Observer) 的引用直接存储在自己的实例变量中,而是将此人物委托给一个单独的对象( 通常为一个容器 )Component类就是这样一个容器,下面代码显示了如何用Component类容器来存储对 观察者(Observer) 对象的引用 **注册(Register)

** public IssueSubject (IContainer Container) : this()
{
Container.Add(this);
}

我们来看一下它是如何完成 注册(Register) 的,首先我们找到一个 观察者(Observer) 对象的实现,Controls/IssueTreeView 用户控件就是一个 观察者(Observer) 对象.它实现了IObserver接口.

Controls/IssueTreeView.cs

namespace IssueVision
{
// The IssueTreeView user control implements the view selection ui for IssueVision
public class IssueTreeView : UserControl, IObserver
{

.....
private TreeView trvViews;
private ImageList images;

private IssueSubject m_subject = null;
private const string m_fontname = "Tahoma";

private IContainer components;

public virtual ISubject Subject //ISubject接口的方法
{
set
{
m_subject = (IssueSubject)value;
.....
}
}

Pane/StaffPane用户控件引用了此 IssueTreeView ,它同样也是 观察者(Observer) 对象.

Pane/StaffPane.cs

namespace IssueVision
{
public class StaffPane : UserControl, IObserver
{

....
private IssueTreeView itvViews;

private IContainer components = null;

// IObserver.Subject
public ISubject Subject
{
set
{
itvViews.Subject = (IssueSubject)value;
}
}

在MainForm.cs中调用此用户控件代码如下:

private IssueSubject m_issueSubject = null;

.....
m_issueSubject = new IssueSubject (this.components); //调用IssueSubject的构造函数

paneStaff.Subject = m_issueSubject;

通过调用IssueSubject的构造函数,是此 观察者(Observer) 对象注册到了 目标(Subject) 对象中.(通过容器的 Container.Add() )

这样就完成了 ** OBSERVER ** (观察者)模式的第一步, 观察者(Observer) 对象的注册 .下面我们来看第二步, 目标(Subject) 对象 如何通知 观察者(Observer) 对象

这就需要使用到 ** 委托 ** 和 ** 事件 ** 了.在回来看 **IssueSubject
**
IssueSubject.cs

public class IssueSubject : Component, ISubject
{

#region Delagate and Event Declarations
.......

public ** delegate ** void ConflictDataChangedEventHandler(object sender, EventArgs e);
public ** delegate ** void LookupDataChangedEventHandler(object sender, EventArgs e);

// ConflictDataChanged changes when a conflict is resolved, or new conflicts are
// detected.
public virtual ** event ** ConflictDataChangedEventHandler ConflictDataChanged;

// LookupDataChanged is raised when lookup data is downloaded from the server
public virtual ** event ** LookupDataChangedEventHandler LookupDataChanged ;

......

IssueSubject 中申明 ** 委托 ** 和 ** 事件 ** , 观察者(Observer) 对象登记这些 事件 ,那么当 IssueSubject 改变后,激活一个 事件 ,那么所有的 观察者(Observer) 对象都能得到这个改变的通知,从而激活相应的处理.

再看Controls/IssueTreeView.cs

namespace IssueVision
{
// The IssueTreeView user control implements the view selection ui for IssueVision
public class IssueTreeView : UserControl, IObserver
{

.....
private TreeView trvViews;

private IssueSubject m_subject = null;

private IContainer components;

public virtual ISubject Subject //ISubject接口的方法
{
set
{
m_subject = (IssueSubject)value;

//登记 IssueSubject 的事件,并交给相关方法处理事件
m_subject.LookupDataChanged += new IssueSubject.LookupDataChangedEventHandler(this.Subject_LookupDataChanged);
m_subject.ConflictDataChanged += new IssueSubject.ConflictDataChangedEventHandler(this.Subject_ConflictDataChanged);
}
}

最后,在 IssueSubject 中激活这些事件 .

IssueSubject.cs

private void LoadIssueData()
{
.......
m_dataSet.DataSetName = "IssueSubject";

if (LookupDataChanged != null)
{
LookupDataChanged (this, EventArgs.Empty);
}
}

通过这么简单的几个步骤,就实现了 ** OBSERVER ** (观察者)模式, .NET框架 提供的 ** 委托 ** 和 ** 事件 ** 机制很大的简化了模式的实现.

CopyRight © YellowWee 2004. All Right Reserved.

Published At
Categories with Web编程
Tagged with
comments powered by Disqus