这次我们来看看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中的实现来简单讲述一下.
在issuevision中patterns文件夹下有两个文件isubject.cs和iobserver.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.