第21章 行为型模式—观察者模式

1. 观察者模式(Observer Pattern)的定义

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

(2)观察者模式的结构和说明(拉模型)

  ①Subject:目标对象,通常具如的功能:一个目标可以被多个观察者观察;目标提供对观察者的注册和退订的维护;当目标的状态发生变化时,目标负责通知所有注册的、有效的观察者

  ②Observer:定义观察者的接口,提供目标通知时对应的更新方法,这个更新方法进行相应的业务处理,可以在这个方法里面回调目标对象,以获取目标对象的数据。

  ③ConcreteSubject:具体的目标实现对象,用来维护目标状态,当目标对象的状态发生改变时,通知所有注册的、有效的观察者,让观察者执行相应的处理。

  ④ConcreteObserver:观察者的具体实现对象,用来接收目标的通知,并进行相应的后续处理,比如更新自身状态以保持和目标的相应状态一致。

【编程实验】图书促销活动

//行为型模式——观察者模式
//场景:图书促销活动(拉模型)
/*
 观察者模式在现实的应用系统中也有好多应用,比如像当当网、京东商城一类的电子商务网站,
 如果你对某件商品比较关注,可以放到收藏架,那么当该商品降价时,系统给您发送手机短信或邮件。
 这就是观察者模式的一个典型应用,商品是被观察者,有的叫主体;关注该商品的客户就是观察者
*/
#include <iostream>
#include <string>
#include <list>

using namespace std;

class Subject; //前向声明(被观察者)

//******************************观察者接口********************************
class Observer
{
public:
    virtual void update(Subject* subject) = 0;
};

//******************************目标对象(Subject)***************************
//抽象目标对象(被观察者)
class Subject //Java中命名为Observable
{
private:
    bool changed;
    list<Observer*> obs;
public:
    void setChanged()
    {
        changed = true;
    }

    void clearChanged()
    {
        changed = false;
    }

    bool hasChanged()
    {
        return changed;
    }

    void addObserver(Observer* o)
    {
        obs.push_back(o);
    }

    void delObserver(Observer* o)
    {
        obs.remove(o);
    }

    void notifyObservers()
    {
        list<Observer*>::iterator iter = obs.begin();
        while(iter != obs.end())
        {
            (*iter)->update(this);
            ++iter;
        }
    }
};

//书——被观察者
class Book : public Subject
{
private:
    string name;
    double price;
public:
    string& getName()
    {
        return name;
    }

    void setName(string value)
    {
        name = value;
    }

    double getPrice()
    {
        return price;
    }

    void setPrice(double value)
    {
        price = value;
    }

    //当书的价格修改时调用该方法
    void modifyPrice()
    {
        //调用父类方法,改变被观察者的状态
        setChanged();
        //通知客户该书己降价
        notifyObservers();
    }

};

//*****************************具体的观察者*****************************
//具体观察者(一般顾客,假设只留手机号码)
class Buyer : public Observer
{
private:
    string buyerId;
    string mobileNo;
public:
    string& getBuyerId(){return buyerId;}
    void setBuyerId(string value) {buyerId = value;}

    string& getMobileNo(){return mobileNo;}
    void setMobileNo(string value){mobileNo = value;}

    //收到通知时要进行的操作
    void update(Subject* s)
    {
        Book* b = (Book*)s;
        cout <<"给一般顾客(" <<buyerId <<")发的手机短信:"<< b->getName()
             <<"降价了,目前的价格为" << b->getPrice()<<"元" << endl;
    }
};

//具体观察者(新华书店,假设只留email)
class BookStore : public Observer
{
private:
    string buyerId;
    string email;
public:
    string& getBuyerId(){return buyerId;}
    void setBuyerId(string value) {buyerId = value;}

    string& getEmail(){return email;}
    void setEmail(string value){email = value;}

    //收到通知时要进行的操作
    void update(Subject* s)
    {
        Book* b = (Book*)s;
        cout <<"给新华书店(" <<buyerId <<")发的电子邮件:"<< b->getName()
             <<"降价了,目前的价格为" << b->getPrice()<<"元" << endl;
    }
};

int main()
{
    //书促销活动
    Book bk;
    bk.setName("《设计模式:可复用面向对象软件的基础》");
    bk.setPrice(45.00); //假设原价是60,现在是降价促销

    //一般客户
    Buyer by;
    by.setBuyerId("001");
    by.setMobileNo("1359912XXXX");

    //新华书店
    BookStore bs;
    bs.setBuyerId("002");
    bs.setEmail("[email protected]");

    //增加观察者,在实际应用中就是哪些人对该书做了关注
    bk.addObserver(&by); //一般客户对该对做了关注
    bk.addObserver(&bs); //新华书店对该书做了关注

    //发送降价通知
    bk.modifyPrice();

    return 0;
}

/*
给一般顾客(001)发的手机短信:《设计模式:可复用面向对象软件的基础》降价了,目前的价格为45元
给新华书店(002)发的电子邮件:《设计模式:可复用面向对象软件的基础》降价了,目前的价格为45元
*/

2. 思考观察者模式

(1)观察者模式的本质触发联动。当修改目标对象的状态时,就会触发相应的通知,然后会循环调用所有观察者对象的相应方法,通知这些观察者,让其做相应的反应。其实就相当于联动调用这些观察者的方法。它将目标对象与观察者解耦,这样目标对象与观察者可以独立变化,但又可以正确联动起来。

(2)目标对象和观察者之间的关系

  ①一个目标只有一个观察者,也可以被多个观察者观察。

  ②一个观察者可以观察多个目标对象,但这里一般观察者内要提供不同的update方法,以便让不同的目标对象来回调。

  ③在观察者模式中,观察者和目标是单向依赖的,只有观察者依赖于目标,而目标是不依赖于具体的观察者(只依赖于接口)。

  ④它们之间的联系主动权掌握在目标对象手中,只有目标对象知道什么时候需要通知观察者。在整个过程中,观察者始终是被动地等待目标对象的通知。

  ⑤对于目标对象而言,所有的观察者都是一样的,会一视同仁。但也可以在目标对象里面进行控制,实现有区别的对待观察者

(3)基本的实现说明

  ①具体的目标实现对象要能维护观察者的注册信息,最简单的实现方案就是采用链表来保存观察者的注册信息。

  ②具体的目标实现对象需要维护引起通知的状态,一般情况下是目标自身的状态。变形使用的情况下,也可以是别的对象的状态。

  ③具体的观察者实现对象需要能接收目标的通知,能够接收目标传递的数据或主动去获取目标的数据,并进行后续处理。

  ④如果是一个观察者观察多个目标,那在观察者的更新方法里面,需要去判断是来自哪一个目标的通知。一种简音的解决方案是扩展update方法,比哪里在方法里多传递一个参数进行区分。还有一种更简单的方法,就是定义不同的回调方法。

(4)触发通知的时机

  ①一般是在完成状态维护后触发,因为通知会传递数据,不能够先通知后改数据,这很容易导致观察者和目标对象的状态不一致。

  ②可能出错的示例代码片段

void setContent(string content)
{
    //目标先发出通知,再修改自己的数据,会造成问题
    notifyAllObservers();    //先发通知(此时可能有的观察者就来取数据,结果
                             //目标的数据还没更新)。

    this->content = content; //再修改自身数据
}

(5)相互观察

  在某些应用中,可能会出现目标和观察者相互观察的情况。这种情况要防止可能出现的死循环现象。

3. 推模型和拉模型

(1)推模型:目标对象主动向观察者推送目标的详细信息,不管观察者是否需要,推送的消息通常是目标对象的全部或部分数据。相当于是在广播通知

(2)拉模型:目标对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到目标对象中获取,相当于是观察者从目标对象中拉数据。一般这种模型的实现中,会把目标对象自己通过update方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个引用来获取。

(3)关于两种模型的比较

  ①推模型是假定目标对象知道观察者需要的数据;而拉模型是目标对象不知道观察者具体需要什么数据,在没有办法的情况下,干脆把自身传递给观察者,让观察者自己去按需取值。

  ②推模式可能会使观察者对象难以复用,因为观察者定义的update方法是按需定义的,可能无法兼顾没有考虑到的情况。这意味者出现新的情桨叶时,就可以需要提供新的update方法。或干脆重新实现观察者。而拉模式不会造成这种情况,因为update方法的参数是目标对象本身。这基本上是目标对象能传递的最大数据集合,基本可以适应各种情况的需要。

4. 观察者模式的优缺点

(1)优点

  ①观察者模式实现了观察者和目标之间的抽象耦合

  ②实现了动态联动。由于观察者模式对观察者的注册实行管理,那就可以在运行期间,通过动态地控制注册的观察者,来控制某个动作的联动范围,从而实现动态联动。

  ③支持广播通信

(2)缺点

  可能会引起无谓的操作。由于观察者模式每次都是广播通信,不管观察者需不需要,每个观察者都会被调用update方法。如果观察者不需要执行相应处理,那这次操作就浪费了,甚至可能会误操作。如本应在执行这次状态更新前把某个观察者删除掉,但现在这个观察者都还没删除,消息就又到达了,那么就会引起误操作。

5. 观察者模式的应用场景

(1)聊天室程序,服务器转发给所有客户端,群发消息等

(2)网络游戏(多人联机对战)场景中,服务器将客户端的状态进行分发。

(3)事件处理模型,基于观察者模式的委派事件模型(事件源:目标对象;事件监听器:观察者)

6. 相关模式

(1)观察者与状态模式

  ①这两者有相似之处。观察者模式当目标状态发生改变时,触发并通知观察者,让观察者去执行相应的操作。而状态模式是根据不同的状态,选择不同的实现,这个实现类的主要功能是针对状态进行相应的操作,它不像观察者,观察者本身还有很多其他的功能,接收通知并执行相应处理只是观察者的部分功能。

  ②这两者可以结合使用。观察者模式的重心在触发联动,但到底决定哪些观察者会被联动,这里可以采用状态模式来实现,也可以使用策略模式来选择需要联动的观察者。

(2)观察者与中介者模式

  如把一个界面所有的事件用一个中介者对象封装处理,当一个组件触发事件以后,只需要通知中介者,由于中介者封装了需要操作其他组件的动作。这样就可以实现目标对象与观察者之间的联动。

时间: 2024-11-07 10:02:15

第21章 行为型模式—观察者模式的相关文章

(21):(行为型模式) Memento 备忘录模式

(21):(行为型模式) Memento 备忘录模式,布布扣,bubuko.com

20 行为型模式-----观察者模式

模式动机(Observer Pattern):将数据的存储与显示进行分离设计,能够很好地降低模块直接的耦合性.但是我们在后台更新数据时总希望前台的显示也做出相应的变化,观察者模式很好地解决了这个问题. 观察者模式定义了对象之间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动更新状态,即对于一份后台数据,可以存在多个显示器负责显示并同时接收后台数据变更的通知. 观察者模式包含如下参与者: Subject: 1> 可以有任意多个观察者同时观察同一目标 2> 可

第24章 行为型模式—模板方法模式

1. 模板方法模式(Template Method Pattern)的定义 (1)定义:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤. (2)模板方法模式的结构和说明 ①AbstractClass:抽象类.用于定义算法骨架和抽象的原语操作,具体的子类通过重定义这些原语操作来实现一个算法的各个步骤.在这个类里面,还可以提供算法中通用的实现.此外,该类还实现了一个模板方法,它定义了算法的骨架.该模板方法不仅调用原语操作,

第17章 行为型模式—解释器模式

1. 解释器模式(Interpreter Pattern)的定义 (1)定义 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子. ①文法:即语法规则.在解释器模式中每一个语法都将对应一个解释器对象,用来处理相应的语法规则.它对于扩展.改变文法以及增加新的文法规则都很方便. ②解释器模式描述了如何为简单的语言定义一个文法,如何在该语言中表示一个句子,以及如何解释这些句子. ③在解释器模式中可以通过一种称之为抽象语法树(Abstract Syntax T

第16章 行为型模式—命令模式

1. 命令模式(Command Pattern)的定义 (1)定义 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化:对请求排队或记录请求日志,以及支持可撤销的操作. ①封装请求:抽象出需要执行的动作,封装成对象(有统一的接口). ②参数化:可以用不同的命令对象,去参数化配置客户的请求.(即,将命令对象作为参数去供Invoker调用). (2)命令模式的结构和说明 ①Command:定义命令的接口,声明执行的方法 ②ConcreteCommand:命令接口实现对象,是“虚”的实现

第5章 创建型模式—抽象工厂模式

1. 抽象工厂的定义 (1)提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类 ①只需各要知道创建一系列对象的接口,而无需知道具体使用的是哪一个实现 ②这一系列对象是相关或相互依赖的,也就是说既要创建对象,还要约束它们之间的关系. ③一系列对象是构建新对象所需要的组成部分,并且对象之间相互有约赖.如电脑由CPU和主板等组成,但CPU的针脚数和主板提供的插口必须是匹配的,否则无法组装. (2)产品族和产品等级 ①产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等

第4章 创建型模式—工厂方法模式(2)

2. 工厂方法模式 2.1工厂方法模式的定义 (1)定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂模式使一个类的实例化延迟到其子类 ①Product:定义了工厂方法创建对象的接口.也就是实际需要使用的产品对象的接口 ②ConcreteProduct:具体的Product接口的实现对象. ③Factory(Creator):定义了工厂方法的抽象类并返回一个产品对象. ④ConcreteCreator:具体的创建器对象,该类实现和覆盖了父工厂类声明的方法.返回一个具体的Product实例

第4章 创建型模式—工厂方法模式(1)

1. 简单工厂 1.1 简单工厂的定义 (1)提供一个创建对象实例的功能,而无须关心其具体实现. ①Api接口:定义客户所需要的功能接口 ②Impl:具体实现Api的实现类,可能会有多个 ③SimpleFatory类:工厂,选择合适的实现类来创建Api接口对象 ④Client:客户端,通过Factory来获取Api接口对象,然后面向Api接口编程. (2)思考简单工厂 ①简单工厂的本质:选择实现,其重点在选择.工厂类内部的主要功能是“选择合适的实现类”来创建实例对象. ②简单工厂的目的:为客户端

第6章 创建型模式—建造者模式

1. 建造者(Builder,或生成器)模式的定义 (1)将一个复杂对象构建与它的表示分离,使得同样的构建过程可以创建不同的表示. ①上述提到的“构建”是指构造过程的算法(即构造顺序,位于director类中),“表示”指生成各部件的具体细节(或叫实现,位于Builder或其子类中). ②由指导者(director)来指导构造过程,而建造者(builder)负责每步的对象的具体实现和组装各部件. ③指导者可以重用构建过程,而生成器是可以被切换的具体实现. (2)建造者模式的结构和说明 ①Buil