意图
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。[GOF《设计模式》]
下面以自动化测试为例来说明观察者模式的含义:
做过自动化测试的都知道一个自动化的测试case主要分三个部分:Init-在一个case开始之前做一些初始化的工作,想读取测试数据阿,打开应用程序之类的, TestMethod-测试步骤都在这个测试方法中,最后一个是CleanUp-在一个case结束后作一些清理的工作,像关闭数据库,写日志之类的,当然可能不同的人名字叫的不一样,不过大体都是分这三部的。
首先,我们有一个TestBase类:
public abstract class TestBase
{
protected abstract void Init();
protected abstract void TestMethod();
protected abstract void CleanUp();
public void Run()
{
Init();
TestMethod();
CleanUp();
}
}
可以看出,基类TestCase的Run()方法其实就是跑一个case的入口,这里当然还用刀了模板方法模式,不过我们可以暂时不用管它。我们只要知道,在一个自动化case运行的时候(即Run方法被调用的时候),必须经过上述的三个阶段。
现在我们想,在case开始的时候,我们可能会写一些记录,或告诉用户case已经开始了,在case运行过程中,我们可能会收集一些数据,在case结束的时候,我们可能会将最终的结果写入XML文件,或写入一个case结果管理工具中。这些其实都是一种“通知”,即在不同的阶段通知不同的对象,即便同一个阶段,在不同的case或不同的运行时间,我们也能通知不同的对象。这就用到了观察者模式。在这个例子中,其实就是在调用Init()的时候,我们要在其中作出CaseInit的通知,在调用TestMethod的时候我们要做出CaseRun的通知,在调用CleanUp()的时候要做出CaseCleanUp的通知,因为通知其实就是要调用一个函数,所以,观察者模式其实就是要在我们发通知的地方放一个“函数的占位符”。因为有三种不同类型的通知,所以我们有下面三种类:
public interface InitObserver
{
void CaseStart(EventArgs arg);
}
public interface TestMethodObserver
{
void CaseRun(EventArgs arg);
}
public interface CleanUpObserver
{
void CaseCleanUp(EventArgs arg);
}
上面的EventArgs类型的参数是我临时用的,可以根据需要改成其他类型的参数来更好的获得需要的数据。
然后修改TestBase来放我们所谓的“占位符”:
public abstract class TestBase
{
public List<InitObserver> initObserverList = new List<InitObserver>();
public List<TestMethodObserver> testMethodObserverList = new List<TestMethodObserver>();
public List<CleanUpObserver> cleanUpObserverList = new List<CleanUpObserver>();
private void Init()
{
foreach (InitObserver elem in initObserverList)
elem.CaseStart(new EventArgs());
TestInitialize();
}
private void TestStep()
{
TestMethod();
foreach (TestMethodObserver elem in testMethodObserverList)
elem.CaseRun(new EventArgs());
}
private void CleanUp()
{
foreach (CleanUpObserver elem in cleanUpObserverList)
elem.CaseCleanUp(new EventArgs());
TestCleanUp();
}
protected abstract void TestInitialize();
protected abstract void TestMethod();
protected abstract void TestCleanUp();
public void Run()
{
Init();
TestStep();
CleanUp();
}
}
这样,在case开始之前,只要初始化好initObserverList,testMethodObserverList cleanUpObserverList,在相应的阶段就会对那些对象做出相应的通知。
在.NET的环境中,观察者模式有了更简单的做法:
首先将 InitObserver,TestMethodObserver,public interface CleanUpObserver这三个接口去掉,
然后定义三个代理:
public delegate void initObserverHandler(EventArgs arg);
public delegate void testMethodObserverHandler(EventArgs arg);
public delegate void cleanUpObserverHandler(EventArgs arg);
修改TestBase:
public abstract class TestBase
{
initObserverHandler initObserver;
testMethodObserverHandler testMethodObserver;
cleanUpObserverHandler cleanUpObserver;
private void Init()
{
initObserver(new EventArgs());
TestInitialize();
}
private void TestStep()
{
TestMethod();
testMethodObserver(new EventArgs());
}
private void CleanUp()
{
cleanUpObserver(new EventArgs());
TestCleanUp();
}
protected abstract void TestInitialize();
protected abstract void TestMethod();
protected abstract void TestCleanUp();
public void Run()
{
Init();
TestStep();
CleanUp();
}
}