Observer模式是我们应用得非常多的一个模式,因为它定义了单数据源多视图结构的消息传递基本机制和对象设计,所以在几乎所有的将表示层与数据逻辑层分开的多层应用软件上都能套用上Observer模式。本文将分析C#语言来实现该模式的代码,并且针对该模式存在的一些缺点,利用C#的一个新特性Delegate来尝试对该模式进行了一些改进。
为了让更多的人能够看明白本文,所以在此之前,我们先来了解一下Observer模式的基本概念。
模式名称:Observer
结构图:
意图:
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。
适用性:
当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。
应用实例:
在编写多层应用程序时,我们通常将表示层和数据逻辑层分隔开,比如很常见的View/Document结构,这种设计方式的好处带来的结果通常是使用多视图同时表示单一数据源,比如一个Web网站可以方便的同时拥有针对电脑的Html页面和针对手机的WAP页面。使用这种结构时,为了保持数据显示的一致性,必须要求数据源在数据发生改变时能及时的逐一通知每一个和它绑定的表示层进行更新。但是问题在于数据层本身并不知道到底有多少个不同的表示层正在反映着它的数据内容。因此需要设计一套有效的机制来完成这个目标。
模式实现:
我们先看看来自《设计模式迷你手册》的常规的C#实现代码。
Subject(抽象目标):
目标知道它的观察者。可以有任意多个观察者观察同一个目标。
//实现代码
class Subject
{
//由于不知道有多少个观察者,所以建立了一个观察者链表
private ArrayList list = new ArrayList();
private string strImportantSubjectData = "Initial";
public string ImportantSubjectData
{
get
{
return strImportantSubjectData;
}
set
{
strImportantSubjectData = value;
}
}
public void Attach(Observer o)
{
list.Add(o);
o.ObservedSubject = this;
}
public void Detach(Observer o)
{
}
public void Notify()
{
//在数据发生改变后遍历列表通知观察者
foreach (Observer o in list)
{
o.Update();
}
}
}
Observer(抽象观察者):
为那些在目标发生改变时需要获得通知的对象定义一个更新接口。
abstract class Observer
{
//内置一个需要观察的对象
protected Subject s;
public Subject ObservedSubject
{
get
{
return s;
}
set
{
s = value;
}
}
abstract public void Update();
}
ConcreteSubject(实体目标,在这里相当于数据逻辑层):
将有关状态存入各ConcreteSubject对象。
当它的状态发生改变时,向它的各个观察者发出通知。
//在这里基本上什么都没有做,数据的获取可以放到GetState()里面
class ConcreteSubject : Subject
{
public void GetState()
{
}
public void SetState()
{
}
}
ConcreteObserver(实体观察者,在这里就相当于表示层):
维护一个指向ConcreteSubject的引用。
储存有关状态,这些状态应与目标的状态保持一致。
实现Observer的更新接口以使自身状态与目标状态保持一致。
class ConcreteObserver : Observer
{
private string observerName;
public ConcreteObserver(string name)
{
observerName = name;
}
override public void Update()
{
//将数据显示出来
Console.WriteLine("In Observer {0}: data from subject = {1}",
observerName, s.ImportantSubjectData);
}
}
主函数:
public class Client
{
public static int Main(string[] args)
{
ConcreteSubject s = new ConcreteSubject();
ConcreteObserver o1 = new ConcreteObserver("first observer");
ConcreteObserver o2 = new ConcreteObserver("second observer");
//注册观察者
s.Attach(o1);
s.Attach(o2);
s. ImportantSubjectData = "This is important subject data";
s.Notify();
return 0;
}
}
模式分析:
Observer模式的优点是实现了表示层和数据逻辑层的分离,并定义了稳定的更新消息传递机制,类别清晰,并抽象了更新接口,使得可以有各种各样不同的表示层(观察者)。但是其缺点是每个外观对象必须继承这个抽像出来的接口类,这样就造成了一些不方便,比如有一个别人写的外观对象,并没有继承该抽象类,或者接口不对,我们又希望不修改该类直接使用它。虽然可以再应用Adapter模式来一定程度上解决这个问题,但是会造成更加复杂烦琐的设计,增加出错几率。
C#作为一种先进的现代面向对象语言,不但吸收了许多语言的精华,并创造了一些非常有用的新特性。在学习了C#语言之后,我发现利用C#独有的Delegate可以来较好的解决这个问题。
改进后的Observer模式实现:
先定义一个Delegate:
delegate void UpdateDelegate(string SubjectData);
Subject(抽象目标):
class Subject
{
private string strImportantSubjectData = "Initial";
//定义一个事件容器,代替前面的观察者对象列表
public event UpdateDelegate UpdateHandle;
public string ImportantSubjectData
{
get
{
return strImportantSubjectData;
}
set
{
strImportantSubjectData = value;
}
}
public void Notify()
{
//发出事件
if(UpdateHandle != null) UpdateHandle(strImportantSubjectData);
}
}
Observer(抽象观察者):无,因为不需要抽象接口类了,所以可以省去抽象观察者类。
ConcreteSubject(实体目标):
//没有任何改变
class ConcreteSubject : Subject
{
public void GetState()
{
}
public void SetState()
{
}
}
ConcreteObserver(实体观察者):
//为了能更加清楚的说明问题,这里定义了两个实体观察者,注意,它们之间并没有任何关系
class Observer1
{
private string observerName;
public Observer1(string name)
{
observerName = name;
}
public void Update1(string ImportantSubjectData)
{
Console.WriteLine("In Observer {0}: data from subject = {1}",
observerName, ImportantSubjectData);
}
}
class Observer2
{
private string observerName;
public Observer2(string name)
{
observerName = name;
}
public void Update2(string ImportantSubjectData)
{
Console.WriteLine("In Observer {0}: data from subject = {1}",
observerName, ImportantSubjectData);
}
}
主函数:
public class Client
{
public static int Main(string[] args)
{
ConcreteSubject s = new ConcreteSubject();
Observer1 o1 = new Observer1("first observer");
Observer2 o2 = new Observer2("second observer");
//向目标注册对象两个观察者,请注意,这里仅仅只是添加了两个方法,不需要关心方法从何而来,也不需要关心目标类如何去调用他们。
s.UpdateHandle += new UpdateDelegate(o1.Update1);
s.UpdateHandle += new UpdateDelegate(o2.Update2);
s.ImportantSubjectData = "This is important subject data";
s.Notify();
return 0;
}
}
在这段代码里,没有看到链表,没有看到遍历操作,没有看到更新的方法是如何被调用,甚至没有看到那些被联系到一起的类的抽象类和抽象接口,但是目标对象却能将数据更新信息逐一发送到每个观察者对象,并且还能更加容易的增加新的不同的观察者对象,根本不需要知道它从何处继承而来,也不需要统一他们的接口调用方法。这一切都归功于灵活强大的C#。
以下是完整的源码:
namespace Observer_DesignPattern
{
using System;
delegate void UpdateDelegate(string SubjectData);
class Subject
{
private string strImportantSubjectData = "Initial";
public event UpdateDelegate UpdateHandle;
public string ImportantSubjectData
{
get
{
return strImportantSubjectData;
}
set
{
strImportantSubjectData = value;
}
}
public void Notify()
{
if(UpdateHandle != null) UpdateHandle(strImportantSubjectData);
}
}
class ConcreteSubject : Subject
{
public void GetState()
{
}
public void SetState()
{
}
}
class Observer1
{
private string observerName;
public Observer1(string name)
{
observerName = name;
}
public void Update1(string ImportantSubjectData)
{
Console.WriteLine("In Observer {0}: data from subject = {1}",
observerName, ImportantSubjectData);
}
}
class Observer2
{
private string observerName;
public Observer2(string name)
{
observerName = name;
}
public void Update2(string ImportantSubjectData)
{
Console.WriteLine("In Observer {0}: data from subject = {1}",
observerName, ImportantSubjectData);
}
}
public class Client
{
public static int Main(string[] args)
{
ConcreteSubject s = new ConcreteSubject();
Observer1 o1 = new Observer1("first observer");
Observer2 o2 = new Observer2("second observer");
s.UpdateHandle += new UpdateDelegate(o1.Update1);
s.UpdateHandle += new UpdateDelegate(o2.Update2);
s.ImportantSubjectData = "This is important subject data";
s.Notify();
return 0;
}
}
}