原文首发于我的微信公众号: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. springmvc基础学习2---简单配置文件

    1:web文件 2:spring-mvc.xml配置文件

  2. C#三层构架

    三层构架:表示层(即界面层UI)->业务逻辑层(Business logic level)->数据访问层(Database access level) 由于层是一种弱耦合结构,层与层之间的 ...

  3. ubuntu下命令使用

    sudo apt-get -f install:修复函数依赖 df -hl:查看空间使用状况 nvidia-smi:常看GPU使用率

  4. Android开发之Notification的简单使用

    创建Notification    Buider 一个Builder至少包含以下内容: 一个小的icon,用setSmallIcon())方法设置 一个标题,用setContentTitle())方法 ...

  5. 一名合格的JAVA程序员需要点亮那些技能树?

    这是从450家企业的招聘信息中统计而来,相对来说还是比较真实的,虽然有些公司的招聘要求万年不变,但还是可以大致反应企业的招聘要求的. 尽管Struts2漏洞频出,但是由于政府.银行以及传统企业遗留项目 ...

  6. spring定时器的使用

    Spring定时任务在有的项目中起到很关键的作用,它允许你通过配置来指定特定时间去调用特定类的特定方法. 一. 分类 1.作业类需要继承特定的类.特定的类有: org.springframework. ...

  7. Excel制作多选下拉框代码以及图示

    1.首先  点击Sheet1(需要显示多选框的页) ,然后右键查看代码,进入编辑界面 2.写入如下代码 Private Sub Worksheet_SelectionChange(ByVal Targ ...

  8. PuTsangTo-单撸游戏开发04 给角色添加基本动画

    一. 跳跃与移动的优化与完善 先给上一次的内容做一次补救,也就是上一次中还留存的,由于键盘按键事件的第一次回调与后续回调之间会间隔个小半秒带来的跳跃落地后动作延迟的情况. 最终的键盘按下回调的处理代码 ...

  9. 腾讯云数据库团队:PostgreSQL TOAST技术理解

    作者介绍:胡彬 腾讯云高级工程师 TOAST是"The Oversized-Attribute Storage Technique"的缩写,主要用于存储一个大字段的值.要理解TOA ...

  10. VC加载显示bmp图片的函数

    void ShowBitmap(HDC hdc,const char *srcpath) { HBITMAP hBitmap = (HBITMAP)::LoadImage(0, srcpath, IM ...