设计模式详解:Observer(观察者模式)

Observer 观察者模式

设计模式学习:概述

意图

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知并自动更新。

Observer(观察者模式),或者又叫Event(事件模式),是我们在编写UI程序时一定会使用到的一种设计理念。Java中的EventListener接口就是完成的这一功能。

  1. 当一个类模块需要从另一个类模块获取信息,而这两者又无法用继承、组合的方式来描述时。
  2. 获取通知的的模块需要获取发送通知的模块的信息,而发送通知的模块事实上不需要知道前者的存在时。

在上述情况满足的情况下,为了降低对象之间的耦合度,我们引入Observer来实现我们的目标。

从稳定-变化的角度来分析的话,使用Observer的程序,接受者获取通知的方式是稳定的,而发送者和接收者可以随时变化

代码案例

考虑下面的类, 它描述了企业的财务报表情况。

class Financial
{
    
    
private:
    //若干信息,我们这里抽象成一个int类型。
    int info;
public:
    //接口
    int getInfo();
    void changeInfo();
}

如上,为了方便,我们把若干财务信息简写成一个int,然后把它置为Private,这显然是处于封装性良好的目的。如果想要获取信息,则需要调用公有函数getInfo()。

现在,我们的需求是,让info以图形化的显示方式显示出来,比如饼状图,柱状图之类的,并且随info的更新而实时更新。这是一个很常见的功能。

想法一:把一个GUI组件组合到该类中,Info改变时顺势改变GUI。

//IDEA 1
class Financial
{
    
    
private:
    Pie pie;
    int info;
public:
    //接口,
    int getInfo();
    void changeInfo();
}

这显然是有问题的。

  1. 逻辑上说不通:财务报表 Has-A Pie?
  2. 硬编码,难修改,难扩展。如果我不想用饼状图呢?换成柱状图的过程代价或许很大。或者说我可能需要多种GUI同时显示?扩展性差
  3. 违反了依赖倒置原则。这才是最严重的问题:我在编写一个反映公司财务信息的类之前,必须要编写一个Pie类,否则无法通过编译。以我们的常识来看,显然,一个GUI组件的层级应当高于Financial类,然而,为了编写一个低层次组件,我必须先实现一个高层次组件,就好像先建1楼再打地基,这是行不通的。

现在,我们尝试着用Observer的思想来解决这个问题。

class BaseObserver
{
    
    
    virtual void Update() = 0;
}

class Financial
{
    
    
private:
    BaseObserver* observer;
    int info;
public:
    //接口
    int getInfo();
    void attach(BaseObserver *b){
    
    observer = b;};
    void detach(){
    
    delete observer;};
    void changeInfo();
}

我们定义了一个Observer基类。并且,在原来的Financial类中,我们组合一个Observer组件(事实上是多态指针)。现在,我们来编写changeInfo()函数。

void Financial::changeInfo()
{
    
    
    //无关代码
    ...
    if(observer != NULL)
        observer -> Update();
    //无关代码
	...
}

这里,在changeInfo的函数体中,observer得到了通知并成功更新。

那么,如果我们想使用一个饼状图,只需要:

class Pie : public BaseObserver
{
    
    
    ...
    void update(){
    
    ...}
    ...
}

这里的BaseObserver就相当于Java中的EventListener接口。不过C++语法中没有接口这一功能罢了。采用继承虚基类可以模拟继承接口,效果是一样的。

当然别忘了,我们还需要向通知发送者注册才能获取信息:

Financial f;
Pie* pie = new Pie();
f.attach(pie);

这里只实现了一个发送者到一个接收者。事实上,将BaseObserver*改为 vector<BaseObserver*>, 也可以轻松实现多个接收者。只需要原本的observer -> Update()改为循环遍历即可。

解释

在Observer模式中,我们实现了发送者和接收者的松耦合关系,使得发送者完全不需要依赖于实际的接收者,而只依赖于它们的基类,这满足了依赖倒置原则

Observer模式试图维护多个模块的一致性(在上面的例子中,也就是UI界面和数据的一致性),同时避免将两个不相干的模块紧耦合。

上面的做法只是一种实现方式。还有一种实现方式是:将Event事件作为一种对象,抽象出一种Event基类,用继承自Event的子类作为第三方转发事件信息。这种做法与上述思想相同,但做法略有不同,因此后者常称为Event模式。Qt的信号-槽机制就是后者的应用。

你会在GUI的编写中常常用到observer模式,但通常只是使用:你大概会通常采用使用Observer模式的第三方图形库或平台,而不会亲自编写它。但它在程序的其他组件上也大有用武之地

总结

设计模式 Observer(观察者模式)
稳定点: 通知的发送-接收流程
变化点: 发送者和接收者本身
效果: 维护了不相干模块间的一致性,同时避免紧耦合
特点: 发送者依赖于观察者基类

类图
2021.1.27 转载请标明出处

猜你喜欢

转载自blog.csdn.net/natrick/article/details/113242967