增强的观察者模式

本文介绍了一种增强的观察者模式,通过使用Boost的shared_ptr解决了主题与观察者生命期管理的问题。文章详细展示了模式的实现代码,并介绍了Handle和RelinkHandle类如何使观察者能够动态地跟踪被观察对象的变化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

增强的观察者模式

       继上一篇观察者模式中讲到的,使用普通的观察者模式时需必须考虑主题和观察者对象的生命期,这就给编程过程中带来了麻烦和风险。下面这段增强的观察者模式借助boostshared_ptr解决了这方面的问题。

 

下面的代码来源于QL,为了更好阅读代码,将保持原有的注释不变:

 

class Observer;

 

//! Object that notifies its changes to a set of observables

/*! /ingroup patterns */

class Observable {

    friend class Observer;

  public:

    // constructors, assignment, destructor

    Observable() {}

    Observable(const Observable&);

    Observable& operator=(const Observable&);

    virtual ~Observable() {}

    /*! This method should be called at the end of non-const methods

        or when the programmer desires to notify any changes.

    */

    void notifyObservers();

  private:

    void registerObserver(Observer*);

    void unregisterObserver(Observer*);

    std::list<Observer*> observers_;

    typedef std::list<Observer*>::iterator iterator;

};

 

//! Object that gets notified when a given observable changes

/*! /ingroup patterns */

class Observer {

  public:

    // constructors, assignment, destructor

    Observer() {}

    Observer(const Observer&);

    Observer& operator=(const Observer&);

    virtual ~Observer();

    // observer interface

    void registerWith(const boost::shared_ptr<Observable>&);

    void unregisterWith(const boost::shared_ptr<Observable>&);

    /*! This method must be implemented in derived classes. An

        instance of %Observer does not call this method directly:

        instead, it will be called by the observables the instance

        registered with when they need to notify any changes.

    */

    virtual void update() = 0;

  private:

    std::list<boost::shared_ptr<Observable> > observables_;

    typedef std::list<boost::shared_ptr<Observable> >::iterator iterator;

};

 

 

// inline definitions

 

inline Observable::Observable(const Observable&) {

    // the observer list is not copied; no observer asked to

    // register with this object

}

 

/*! /warning notification is sent before the copy constructor has

             a chance of actually change the data

             members. Therefore, observers whose update() method

             tries to use their observables will not see the

             updated values. It is suggested that the update()

             method just raise a flag in order to trigger

             a later recalculation.

*/

inline Observable& Observable::operator=(const Observable& o) {

    // as above, the observer list is not copied. Moreover,

    // observers of this object must be notified of the change

    if (&o != this)

        notifyObservers();

    return *this;

}

 

inline void Observable::registerObserver(Observer* o) {

    observers_.push_front(o);

}

 

inline void Observable::unregisterObserver(Observer* o) {

    iterator i = std::find(observers_.begin(),observers_.end(),o);

    if (i != observers_.end())

        observers_.erase(i);

}

 

inline void Observable::notifyObservers() {

    bool successful = true;

    for (iterator i=observers_.begin(); i!=observers_.end(); ++i) {

        try {

            (*i)->update();

        } catch (...) {

            // quite a dilemma. If we don't catch the exception,

            // other observers will not receive the notification

            // and might be left in an incorrect state. If we do

            // catch it and continue the loop (as we do here) we

            // lose the exception. The least evil might be to try

            // and notify all observers, while raising an

            // exception if something bad happened.

            successful = false;

        }

    }

    QL_ENSURE(successful, "could not notify one or more observers");

}

 

 

inline Observer::Observer(const Observer& o)

: observables_(o.observables_) {

    for (iterator i=observables_.begin(); i!=observables_.end(); ++i)

        (*i)->registerObserver(this);

}

 

inline Observer& Observer::operator=(const Observer& o) {

    iterator i;

    for (i=observables_.begin(); i!=observables_.end(); ++i)

        (*i)->unregisterObserver(this);

    observables_ = o.observables_;

    for (i=observables_.begin(); i!=observables_.end(); ++i)

        (*i)->registerObserver(this);

    return *this;

}

 

inline Observer::~Observer() {

    for (iterator i=observables_.begin(); i!=observables_.end(); ++i)

        (*i)->unregisterObserver(this);

}

 

inline void Observer::registerWith(

                                 const boost::shared_ptr<Observable>& h) {

    if (h) {

        observables_.push_front(h);

        h->registerObserver(this);

    }

}

 

inline void Observer::unregisterWith(

                                const boost::shared_ptr<Observable>& h) {

    if (h) {

        for (iterator i=observables_.begin();

             i!=observables_.end();

             ++i) {

            if (*i == h) {

                (*i)->unregisterObserver(this);

                observables_.erase(i);

                return;

            }

        }

    }

}

 

注:

在这篇文章中出现的QL_REQUIRE(condition,message)QL_ENSURE(condition,message)是两个异常宏,表达的意思是当不满足条件condition的时候就会抛出message异常。

 

    从上面的这段代码中可以看到,主题不再受观察者生命期的影响,观察者在程序执行过程中,任何时候被释放掉都不需要考虑主题。

       这里与经典的观察模式中有一些不同。这里允许出现主题与观察者多对多的关系。如果一个观察者需要观察一个主题对象,观察者需要注册到主题对象中,调用registerWith函数。经典的观察者模式中是主题去注册观察者,在这里主题的注册函数registerObserver是私有的,通过观察者的registerWith函数注册到一个主题中,主题再通过registerObserver将观察者增加到观察者队列当中去。这样繁琐的原因是因为观察者保存了主题的智能指针,也就是观察者维护了主题的生命期,同时观察者也可以观察多个主题。观察者每次被销毁的时候会找到观察的主题对象,并从主题对象的观察者列表中删除。主题对象不会先于观察者对象之前被销毁,当最后一个观察者对象被销毁的时候,自动销毁主题对象。

 

使用:

struct A : public Observable

{

     A (int i) : i_(i) {}

 

     int i_;

};

 

struct B : public Observer

{

     int i_;

 

     B(boost::shared_ptr<A>& a) : a_(a)

     {

         registerWith(a);

         i_ = a_->i_;

     }

 

     virtual void update()

     {

         i_ = a_->i_;

     }

 

private:

     boost::shared_ptr<A> a_;

};

void test()

{

     boost::shared_ptr<A> a(new A(10));

     B* b = new B(a);

 

     //delete b; // 如果这里被释放掉,下面a->notifyObservers的时候也不会崩溃。

 

     cout << b->i_ << endl;

 

     a->i_ = 100;

     a->notifyObservers();

 

     cout << b->i_ << endl;

 

     delete b;

}

 

输出:

10

100

 

强大的Handle功能。

将上面的测试代码更改一下

 

void test()

{

     boost::shared_ptr<A> a(new A(10));

     boost::shared_ptr<B> b(new B(a));

 

     cout << b->i_ << endl;

 

     a->i_ = 100;

     a->notifyObservers();

 

     cout << b->i_ << endl;

 

     a = boost::shared_ptr<A>(new A(200));

     a->notifyObservers();

 

     cout << b->i_ << endl;

}

 

输出:

10

100

100

 

测试表明,如果a重新指向了另一个新的指针以后,b的值是不会被通知到的。如果要被通知到,观察者需要重新注册到这个主题中。这样在编码的过程中未免显的有些麻烦。QL中提供了另一个功能强大的类HandleRelinkHandle解决这个问题。

 

//! Shared handle to an observable

/*! All copies of an instance of this class refer to the same

    observable by means of a relinkable smart pointer. When such

    pointer is relinked to another observable, the change will be

    propagated to all the copies.

 

    /pre Class T must inherit from Observable

*/

template <class T>

class Handle {

  protected:

    class Link : public Observable, public Observer {

      public:

        explicit Link(const boost::shared_ptr<T>& h,

                      bool registerAsObserver);

        void linkTo(const boost::shared_ptr<T>&,

                    bool registerAsObserver);

        bool empty() const { return !h_; }

        const boost::shared_ptr<T>& currentLink() const { return h_; }

        void update() { notifyObservers(); }

      private:

        boost::shared_ptr<T> h_;

        bool isObserver_;

    };

    boost::shared_ptr<Link> link_;

  public:

    /*! /warning <tt>registerAsObserver</tt> is left as a backdoor

                 in case the programmer cannot guarantee that the

                 object pointed to will remain alive for the whole

                 lifetime of the handle---namely, it should be set

                 to <tt>false</tt> when the passed shared pointer

                 does not own the pointee (this should only happen

                 in a controlled environment, so that the

                 programmer is aware of it). Failure to do so can

                 very likely result in a program crash.  If the

                 programmer does want the handle to register as

                 observer of such a shared pointer, it is his

                 responsibility to ensure that the handle gets

                 destroyed before the pointed object does.

    */

    explicit Handle(

                   const boost::shared_ptr<T>& h = boost::shared_ptr<T>(),

                   bool registerAsObserver = true);

    //! dereferencing

    const boost::shared_ptr<T>& currentLink() const;

    const boost::shared_ptr<T>& operator->() const;

    const boost::shared_ptr<T>& operator*() const;

    //! checks if the contained shared pointer points to anything

    bool empty() const;

    //! allows registration as observable

    operator boost::shared_ptr<Observable>() const;

    //! equality test

    template <class U>

    bool operator==(const Handle<U>& other) {

        return link_ == other.link_;

    }

    //! disequality test

    template <class U>

    bool operator!=(const Handle<U>& other) {

        return link_ != other.link_;

    }

    //! strict weak ordering

    template <class U>

    bool operator<(const Handle<U>& other) {

        return link_ < other.link_;

    }

};

 

//! Relinkable handle to an observable

/*! An instance of this class can be relinked so that it points to

    another observable. The change will be propagated to all

    handles that were created as copies of such instance.

 

    /pre Class T must inherit from Observable

*/

template <class T>

class RelinkableHandle : public Handle<T> {

  public:

    /*! /warning see the Handle documentation for issues

                 relatives to <tt>registerAsObserver</tt>.

    */

    explicit RelinkableHandle(

                   const boost::shared_ptr<T>& h = boost::shared_ptr<T>(),

                   bool registerAsObserver = true);

    /*! /warning see the Handle documentation for issues

                 relatives to <tt>registerAsObserver</tt>.

    */

    void linkTo(const boost::shared_ptr<T>&,

                bool registerAsObserver = true);

};

 

 

// inline definitions

 

template <class T>

inline Handle<T>::Link::Link(const boost::shared_ptr<T>& h,

                             bool registerAsObserver)

: isObserver_(false) {

    linkTo(h,registerAsObserver);

}

 

template <class T>

inline void Handle<T>::Link::linkTo(const boost::shared_ptr<T>& h,

                                    bool registerAsObserver) {

    if ((h != h_) || (isObserver_ != registerAsObserver)) {

        if (h_ && isObserver_)

            unregisterWith(h_);

        h_ = h;

        isObserver_ = registerAsObserver;

        if (h_ && isObserver_)

            registerWith(h_);

        notifyObservers();

    }

}

 

template <class T>

inline Handle<T>::Handle(const boost::shared_ptr<T>& h,

                         bool registerAsObserver)

: link_(new Link(h,registerAsObserver)) {}

 

template <class T>

inline const boost::shared_ptr<T>& Handle<T>::currentLink() const {

    QL_REQUIRE(!empty(), "empty Handle cannot be dereferenced");

    return link_->currentLink();

}

 

template <class T>

inline const boost::shared_ptr<T>& Handle<T>::operator->() const {

    QL_REQUIRE(!empty(), "empty Handle cannot be dereferenced");

    return link_->currentLink();

}

 

template <class T>

inline const boost::shared_ptr<T>& Handle<T>::operator*() const {

    QL_REQUIRE(!empty(), "empty Handle cannot be dereferenced");

    return link_->currentLink();

}

 

template <class T>

inline bool Handle<T>::empty() const {

    return link_->empty();

}

 

template <class T>

inline Handle<T>::operator boost::shared_ptr<Observable>() const {

    return link_;

}

 

template <class T>

inline RelinkableHandle<T>::RelinkableHandle(const boost::shared_ptr<T>& h,

                                             bool registerAsObserver)

: Handle<T>(h,registerAsObserver) {}

 

template <class T>

inline void RelinkableHandle<T>::linkTo(const boost::shared_ptr<T>& h,

                                        bool registerAsObserver) {

    this->link_->linkTo(h,registerAsObserver);

}

 

Handle的模板参数必须是一个主题对象(被观察的对象)。该模板类中含有一个link的智能指针,link继承了ObservableObserverlink通过linkTo函数将一个主题保存在link中,同时注册到主题中,也就是说link作为观察者观察这个主题, 当主题发生状态改变时会被通知到。因为Handle中保存了link的指针,link中又保存了主题的指针,Handle拷贝对象都指向同一个link指针。当Handle重新指向另一个主题的时候,link指针保持不变,改变的只有link保存的那个主题对象,所以所有的拷贝也都间接指向了那个指针。Handle封装了指针重载操作符,通过这个指针操作就相当于直接在访问主题对象。

operator boost::shared_ptr<Observable>() const,是Handle非常重要的一个函数。这个函数将link作为主题对象返回,就是说Handle的对象是可以被观察者注册的,其实就是把link作为主题注册给这个观察者。我们再找一下link何时通知这些观察者。在link的成员函数updatelinkTo两个函数中调用了父类的notifyObservers函数通知已经注册的观察者。明白了吗?当link作为观察者被主题通知的时候会继而通知link作为主题注册在它里面的那些观察者。

上面两段话可以把link的功能归纳为,link作为主题和观察者的中间介质,中转主题的通知。HandleRelinkHandle封装了linkHandle可以透明的看成主题对象,使用的时候将Handle指向一个主题对象即可。RelinkHandle可以重新指定主题对象。只要Handle的任意一个拷贝改变了主题的对象,Handle的所有拷贝都会随着改变。

 

简单的调用:

 

struct C : public Observer

{

     int i_;

 

     C(Handle<A>& a) : a_(a)

     {

         registerWith(a);

         i_ = a_->i_;

     }

 

     virtual void update()

     {

         i_ = a_->i_;

     }

 

private:

     Handle<A> a_;

};

 

void test()

{

     boost::shared_ptr<A> a1(new A(10));

     boost::shared_ptr<A> a2(new A(20));

 

     RelinkableHandle<A> h1(a1);

     Handle<A> h2 = h1;

 

     C c1(h1);

     cout << h2->i_ << endl; // = 10

     cout << c1.i_ << endl; // = 10

 

     h1.linkTo(a2);

     cout << h2->i_ << endl; // = 20

     cout << c1.i_ << endl; // = 20

 

     a2->i_ = 30; // <=> h1->i_ = 30;

     cout << h2->i_ << endl; // = 30

     cout << c1.i_ << endl; // = 20

 

     a2->notifyObservers(); // <=> h1->notifyObservers();

     cout << h2->i_ << endl; // = 30

     cout << c1.i_ << endl; // = 30

}

 

因为有源码和注释,link的另一个成员变量isObserver_就不详细说了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值