原文首发于我的微信公众号:GeekArtT.

Observer设计模式是为了解决“信息同步更新”的问题而存在的。它试图解决这样一个问题:如果有“一堆对象”都跟随“某一对象”的变化而变化,那么,如何能够保持“这堆对象”能够同步更新呢?特别是,“这堆对象”很可能在运行时(run-time)不断被添加或者被删除,你设计的机制该如何既能保持增添/删除的灵活性,而又能使“当前这堆对象”同步更新呢?

仅仅是抽象地看待上述这个问题,或许很难有思路。解决方案的形成可以依托于生活中具体业务场景的类比。

很多书籍介绍观察者(Observer)这个设计模式喜欢运用“内容订阅者”和“发布信息平台”的对应关系。这个比喻从生产流程上讲,完全符合,但是作为比喻,还是显得有些生硬。我认为,一个更好的、更富有启发性的类比是“品牌商店”与“旗下加盟商/分店”在价格上的依存关系。好不夸张的说,一旦做出这个类比,解决方案就会自然浮现。

例如,McDonald在一座城市开店,其价格都是高度统一。如果某一个分店的价格低于别的分店,就会打乱跨国大公司的整体战略部署,是绝不允许的。所以,一个核心的目标是,每一个分店的价格都必须时刻同步统一。另一方面,每个地区的分店,都有可能随着市场的反馈而做出调整:或者因为销量过低而关闭,或者因为某个新的商圈的出现而增加分店。那么,在分店的数目、位置都在随时变动的情形下,应该用一套什么机制才能保证分布在城市各地的分店都使用统一的价格呢?

一旦有了恰当的比喻,问题自然迎刃而解。保持分店的价格有什么困难的?

在总店的通讯本上,记录下各个分店的电话号码。一旦有了新的价格策略,立马根据通讯本上记录的电话号码,拨打各个分店的电话,通知各个分店。如果建立了新的分店,就在总店的通讯本上添加新分店的电话号码;如果关闭了一家分店,就在总店通讯本上去掉相应分店的电话号码。由此,各分店的价格便能够同步统一,且保持相对好的灵活性。

有了这段生活场景作为指导纲领,我们便可以写出具体的Observer设计模式的代码了。考虑到GoF(Design Patterns)这本书的巨大影响,我首先展示如何运用上述类比一步步写出GoF中Observer设计模式这一节的示例代码。

在GoF中,示例代码的应用场景是:如果我有一个class能够实时获取当前时间,但是,我又想用两种方式Digital Clock和Analog Clock的方式去做展示(可以想象为`10:28`和`10h28min`这样不同的展示方式)。

首先设计“分店”类。它最主要的功能就是根据“总店”通报的信息,将自己的信息更新。对应GoF的C++代码,Subject类充当了“总店”的角色。

class Subject;

class Observer {
public:
virtual ~Observer();
virtual void Update(Subject* theChangedSubject) = 0;
protected:
Observer();
}

这里Update()方法是需要有Subject类作为参数的。因为,作为分店,自己是不可以任意地改动价格的,你有且只能根据“总店”的消息去做变更,别的数据源都是禁止的。

然后设计“总店”类,用它来衍生出“能够实时获取当前时间的类”。按照之前的思路,这个类除了必要的构造/析构函数外:

  • 需要有一个“通讯本”记录各分店的电话号码。

  • 相应地,应该具备添加/删除电话号码的方法。

  • 另外,还应当有一个方法,能够根据通讯本上记录的信息拨打各个分店的电话号码。这一行为抽象地说,即是能够根据一个信息记录本,来逐一通知各个“分店”进行信息更新。

GoF中的C++的代码如下:

class Subject {
public:
virtual ~Subject(); virtual void Attach(Observer *);
virtual void Detach(Observer *);
virtual void Notify();
protected:
Subject();
private:
List<Observer*> *_observers;
};

这里,列表_observers充当了通讯本的角色,用它来保存各个“分店”的指针。本来,指针即是变量的门牌号。把“分店”的这个号码记录在通讯本上,就能够任意地访问“分店”。从这点上来说,它比仅仅记录的电话号码还要强大有用。

Attach()与Detach()方法自然就是用于添加/删除这个通讯本的信息了。而Notify()即是字面意思所表述的,用于通知各个分店“价格有变”。而通知的目的,实质是让各分店做更新,所以,这里作为父类的Notify()是有统一的更新方法:

void Subject::Notify() {
ListIterator<Observer*> i(_observers); for(i.First(); !i.IsDone(); i.Next()) {
i.CurrentItem()->Update(this);
}
}

这里所用的方法,即是依次将通讯本上的分店调取出来,让它们各自调用自己的Update()方法,并将自己,this,作为参数传入。告知分店把自己作为信息源来进行同步更新。

构造完父类,下面可以构建具体的时钟类。首先是能够实时获取时间的“总店”类:

class ClockTimer : public Subject {
public:
ClockTimer(); virtual int GetHour();
virtual int GetMinute();
virtual int GetSecond(); void Tick();
} void ClockTimer::Tick() {
// update internal time-keeping state
// ...
Notify();
}

这里,Tick()方法用于定时抓取最新的时间状态信息。

然后是第一个“分店”类:

class DigitalClock: public Widget, public Observer {
private:
ClockTimer* _subject;
public:
DigitalClock(ClockTimer*);
virtual ~DigitalClock(); virtual void Update(Subject*);
// overrides Observer operation virtual void Draw();
// overrides Widget operation;
// defines how to draw the digital clock
}; DigitalClock::DigitalClock(ClockTimer* s) {
_subject = s;
_subject->Attach(this);
} DigitalClock::~DigitalClock() {
_subject->Detach(this);
}

这个类里面,运用了private变量_subject去保存它的数据源,并在构造函数中将其初始化。之后便可以运用这个_subject去核对Update是否是被正确的数据源通知,以此来作为更新与否的标准:

void DigitalClock::Update(Subject* theChangedSubject) {
if(theChangedSubject == _subject) {
Draw();
}
} void DigitalClock::Draw() {
// get the new values from the subject
int hour = _subject->GetHour();
int minute = _subject->GetMinute();
// etc. // draw the digital clock
}

之后是第二个“分店”AnalogClock的构建:

class AnalogClock : public Widget, public Observer {
private:
ClockTimer* _subject;
public:
AnalogClock(ClockTimer*);
virtual ~AnalogClock(); virtual void Update(Subject*);
virtual void Draw();
// ...
}; // ...

构建完了这三大部分,便可以直接使用上面完成的Observer模式了:

ClockTimer* timer = new ClockTimer;

AnalogClock* analogClock = new AnalogClock(timer);
DigitalClock* digitalClock = new DigitalClock(timer);

这样,每当timer中的Tick()方法抓取到了新的时间信息,两个Clock就会得到及时更新,并作出相应地不同展示。


如果你喜欢我的文章或分享,请长按下面的二维码关注我的微信公众号,谢谢!

设计模式之“Observer”注疏#01的更多相关文章

  1. java设计模式--观察者模式(Observer)

    java设计模式--观察者模式(Observer) java设计模式--观察者模式(Observer) 观察者模式的定义: 定义对象间的一种一对多的依赖关系.当一个对象的状态发生改变时,所有依赖于它的 ...

  2. [置顶] head first 设计模式之----Observer pattern

    浅谈设计模式之----观察者模式      观察者模式也是我们日常程序编写中碰到比较多的一种设计模式.首先,所谓观察者模式定义就是指:在对象之间定义了一对多的依赖,这样一来,当一个对象的状态发生变化的 ...

  3. 面向对象设计模式——观察者(OBSERVER)模式

    定义 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新.  Observer模式描述了如何建立这种关系.这一模式中的关键对象是目标(subject ...

  4. 设计模式--观察者模式Observer(对象行为型)

    一.观察者模式 观察者模式是在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新.观察者模式也被称之为:主题-观察者模式,发布-订阅模式,前者是一,后者是多. ...

  5. 设计模式之Observer(观察者)(转)

    Java深入到一定程度,就不可避免的碰到设计模式(design pattern)这一概念,了解设计模式,将使自己对java中的接口或抽象类应用有更深的理解.设计模式在java的中型系统中应用广泛,遵循 ...

  6. 设计模式-观察者模式(Observer Pattern)

    观察者模式(Observer Pattern):定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己. 观察者 ...

  7. 大话设计模式--观察者模式 Observer -- C++ 实现实例

    大话设计模式--1.观察者模式: 定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生变化时,会通知所有的 观察者对象,使他们能够自动更新自己. 使用场合: 当一 ...

  8. 设计模式之observer and visitor

    很长时间一直对observer(观察者)与visitor(访问者)有些分不清晰. 今天有时间进行一下梳理: 1.observer模式 这基本就是一个通知模式,当被观察者发生改变时,通知所有监听此变化的 ...

  9. [工作中的设计模式]观察者模式observer

    一.模式解析 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己. 观察者模式又叫订阅发布模式, ...

随机推荐

  1. 使用slice和concat对数组的深拷贝和浅拷贝

    一.数组浅拷贝 在使用JavaScript对数组进行操作的时候,我们经常需要将数组进行备份. 如下代码,如果只是简单才用赋值的方法,那么我们只要更改其中的任何一个,然后其他的也会跟着改变,这就导致了问 ...

  2. JsonCpp(C++程序使用)

    C++ json解析库 github C++: Makefile目录cmd:make 得到build 得到.a静态库 Eclipse引入头文件  (include目录) 引入.a静态库 编译设置: O ...

  3. Python中的内置函数__init__()的理解

    有点意思,本来我是学习java的.总所周知,java也有构造函数,而python在面向对象的概念中,也有构造函数.它就是 __init__(self) 方法. 其实类似于__init__()这种方法, ...

  4. Node.js开发工具、开发包、框架等总结

    开发工具 1.WebStorm,毫无疑问非他莫属,跨平台,强大的代码提示,支持Nodejs调试,此外还支持vi编辑模式,这点我很喜欢.2.做些小型项目用Sublime Text.3.Browserif ...

  5. Pycharm集成PyQt4并使用

  6. spring+mybatis之声明式事务管理初识(小实例)

    前几篇的文章都只是初步学习spring和mybatis框架,所写的实例也都非常简单,所进行的数据访问控制也都很简单,没有加入事务管理.这篇文章将初步接触事务管理. 1.事务管理 理解事务管理之前,先通 ...

  7. 不使用回调函数的ajax请求实现(async和await简化回调函数嵌套)

    在常规的服务器端程序设计中, 比如说爬虫程序, 发送http请求的过程会使整个执行过程阻塞,直到http请求响应完成代码才会继续执行, 以php为例子 $url = "http://www. ...

  8. 学习了php之后再来看php怎样学java

    我用了一天时间学会了php,真的.我现在已经可以流畅的用thinkphp框架开发php了.学习过程是这样的:我接了个php的项目,包括两个部分:老系统添加功能和优化,再新做一个系统.已经答应给人家做了 ...

  9. WebApi client 的面向切面编程

    .Net的面向切面编程 .Net的服务端应用AOP很常见,在Asp.net MVC与Asp.net WebApi等新框架里到处都有AOP的影子,我们可以把一个服务方法“切”为很多面,日志面.验证面.请 ...

  10. Knockout Mvc Compoment FrameSet

    Knockout Mvc Compoment FrameSet 框架文件结构 01-   网站(表现层),mvc主要作用视图展示. 02-   模型(Model),主要作用承载视图数据结构,网站前后台 ...