当前位置: 代码迷 >> 综合 >> 乱砍设计模式之六 -- OBSERVER 及 ITERATOR 模式
  详细解决方案

乱砍设计模式之六 -- OBSERVER 及 ITERATOR 模式

热度:85   发布时间:2023-12-12 01:55:09.0

     Observer模式的中文译名是观察者模式,定义是:定义对象间一种一对多的关系,当一个对象的状态发生变化时,所有依赖它的对象都得到通知并被自动更新。COM中的连接点事实上就是一种观察者模式,COM中的连接点主要是为过程化语言提供的,如果我们使用C++调用COM组件,那么我们可以直接利用回调函数的形式来完成同样的功能。而事实上Observer模式的具体实现,也是通过回调函数来完成。我们还是通过例子来学习该模式。

 

     彼黍离离,彼稷之苗。行迈靡靡,中心摇摇。知我者,谓我心忧;不知我者,谓我何求。悠悠苍天,此何人哉!
     彼黍离离,彼稷之穗。行迈靡靡,中心如醉。知我者,谓我心忧;不知我者,谓我何求。悠悠苍天,此何人哉!
     彼黍离离,彼稷之实。行迈靡靡,中心如噎。知我者,谓我心忧;不知我者,谓我何求。悠悠苍天,此何人哉!

 

     是从冯晓刚的《天下无贼》开始熟悉这首诗的。这首诗取自《诗经》中的《王风》,诗歌产生背景是这样的。西周王朝的最后一位君主周幽王,是个不成器的家伙。宠爱的妃子褒姒从来不笑,为了博美人一笑,他玩了一个“烽火戏诸侯”的游戏。结果等犬戎阵来攻击他的时候,烽火已经无法招来诸侯的救军。他被犬戎追杀,最后葬身于骊山脚下。西周的历史结束,继任的周平王被迫东迁,失去了多诸侯的控制权。而后一位当时周王朝的老臣,再回西周旧都的时候,已失往日的繁华,满目疮痍,感伤之下,作了此诗。不过我们的例子和此诗的关系不大,我只是觉得“知我者,谓我心忧”能很好的反映Observer模式的意义。
     人总是把自己的心境和周边联系起来,心情不好了,天空就不再是蓝色的,花草也不再美丽。事实上什么也没有变,变的只是你的心情。不过在虚拟世界里,让世界随着自己的心情来变,就不是困难的事情了。我们来模式这样一个过程,当一个人的心情发生变化的时候,他所处于的天空,身边的花木都产生相应的变化。我们来看看类图:

     从类图可以看到,我们定义了一个观察者接口,这个接口比较简单。只有一个虚函数Update。而它的子类Sky和Flower也很简单,就是实现Update。我们在看另一个接口Subject,它拥有一个Observer型的列表,用来存放所有的观察者。它的方法包括Attach添加一个观察者,Detach删除一个观察者,Notify是用来遍历所有的观察者,并调用它们的接口函数Update。Person是一个具体的类,事实上Subject的所有功能,都可以由它来实现。但我们应该记得一个规则:针对接口编程,而不要针对实现编程。这样有助于我们程序的扩展。
    我们来看看代码,首先我们需要定义一个Observer的接口,而天空花木都继承于它,代码如下:

 

#ifndef __OBSERVER__H
#define __OBSERVER__H

//observer 接口,继承者需要实现Update方法
class Observer
{
public:
    
virtual ~Observer(){}    
    
virtual void Update(Subject* pChange) = 0;

 

protected:    
    Observer(){}
};

 

//Sky类
class Sky : public Observer
{
private:
    
//具体的目标对象指针,是通过它的状态来改变观察者的行为    
    
//可以有多个具体的目标对象,不过这里我们提供一个    
    Person* m_pPerson;
public:    
    Sky(Person 
*pPerson) 
    {        
        m_pPerson 
= pPerson;    
        m_pPerson
->Attach(this);    
    } 

 

    virtual ~Sky()
    {        
        m_pPerson
->Detach(this);
    }

 

    void Update(Subject *pChange)    
    {

        //由于可能有多个,所以这里需要判断    
        if ( pChange == m_pPerson )    
        {    
            
if ( m_pPerson->GetSpirit() == true )
            {            
                cout 
<< "天空是蓝色的!" << endl;
            }

            else    
            {    
                cout 
<< "乌云密布!" << endl;
            }    
        }    
    }
};

 

//Flower类
class Flower : public Observer
{
private:    
    Person
* m_pPerson;

public:
    Flower(Person 
*pPerson) 
    {    
        m_pPerson 
= pPerson;
        m_pPerson
->Attach(this);    
    } 

 

    virtual ~Flower()    
    {        
        m_pPerson
->Detach(this);
    }

 

    void Update(Subject *pChange)    
    {        
        
if ( pChange == m_pPerson )    
        {            
            
if ( m_pPerson->GetSpirit() == true )
            {            
                cout 
<< "百花灿烂时!" << endl;    
            }

            else    
            {        
                cout 
<< "凋零的玫瑰!" << endl;    
            }    
        }
    }
};

 

#endif

 

      以上为Observer.h头文件的内容,就是定义一个Observer接口,而后由具体的观察者类来继承。我们接着看看目标类的实现。 

      Subject.h文件的内容:

 

#ifndef __SUBJECT_HEADER__H
#define __SUBJECT_HEADER__H

//由于类Observer和Subject需要嵌套调用,所以不能直接#include"observer.h"

//所以需要先申明一下
class Observer;

 

//目标接口
class Subject
{
public:
    
//添加一个观察者    
    virtual void Attach(Observer *);  

 

    //删除一个观察者    
    virtual void Detach(Observer *);

 

    //通知观察者    
    virtual void Notify();

 

protected:    
    Subject(){}

private:    
    
//观察者列表    
    list m_ObserverList;
};

 

//具体的目标类
class Person : public Subject
{
private:    
    
bool m_bGoodSpirit;

 

public:    
    Person():m_bGoodSpirit(
true){}    
    
bool GetSpirit()
    {
        
return m_bGoodSpirit;
    }

 

    void GoodSpirit()    
    {    
        m_bGoodSpirit 
= true;    
        cout 
<< "我的心情很好,所以我看到了:" << endl;    
        Notify();    
    } 

 

    void BadSpirit()    
    {    
        m_bGoodSpirit 
= false;    
        cout 
<< "我的心情很不好,所以我看到了:" << endl;    
        Notify();    
    }
};
#endif

    这段代码就是定义了一个目标类的接口。当然你也可以不使用接口,直接定义一个Person类来实现该功能,但这样的话你在Observer的具体类的对象中就无法观察多种对象了。
    (由于此次的代码中有嵌套使用类的情况,所以我标出了代码所在的文件名。不熟悉该用法的话,你可以注意一下。)
    我们再来看看Subject类的具体实现:
    以下是Subject.cpp的文件。

 

#include "Subject.h"
#include 
"Observer.h"

void Subject::Attach(Observer *pObserver)
{    
    m_ObserverList.push_back(pObserver);
}

void Subject::Detach(Observer *pObserver)
{    
    m_ObserverList.remove(pObserver);
}

void Subject::Notify(){    
    list::iterator ite;    
    
    
//遍历所有的观察者就行通知    
    for( ite = m_ObserverList.begin() ; ite != m_ObserverList.end() ;++ite)        
        (
*ite)->Update(this);
}

    由于嵌套使用的缘故,所以在Subject.h中无法直接使用Observer的对象,必须放到cpp中来实现,所以我们分割开来。这边的实现其实也很简单。我们再看看具体的使用。

 

#include "Observer.h"
#include 
"Subject.h"

int main(int argc, char* argv[])
{    
    Person p;    

    Sky sky(
&p);
    Flower flower(
&p);    
    p.GoodSpirit();
    p.BadSpirit();

 

    return 0;
}

运行结果如下:

 

     在我们的例子,Observer的列表是通过C++标准库的list来管理的,但为了讲解我们的下一个模式Iterator,我们将把它变为一个数组。我们还是先来看看Iterator的定义:提供一种方法顺序访问一个聚合对象中的各个元素,而不是暴露该对象的内部表示。其实Iterator就是为我们的数组,线性表一类的容器提供一个遍历的方法。但如今C++的标准库中已经很好的实现了这一功能,我们应该更多的是用标准库。在.net平台和Java的库中一般也都提供了很好的容器类,也都提供了迭代器。一般情况下,我们已经无需自己去实现迭代器。不过还是来简单看看的具体实现。为了便于扩展,我们还是提供一个Iterator的接口类:

 

template<class Item>
class Iterator
{
public:    
    
virtual bool hasNext() = 0;
    
virtual Item Next() = 0;
};

 

     这里提供的接口比较简单,只是判断是否有下一个元素和返回下一个元素的操作。一般的迭代器会提供重载加号,等于号等操作,这里省略了。我对STL也不太熟悉,下一阶段,准备学习一下,有时间的话,再和大家分享。
    我们来实现一个数组的迭代器:

 

template<class Item>
class ArrayIterator : public Iterator
{
private:  
    
//指向数组的指针    
    Item *m_pItems;

 

    //当前迭代器的位置    
    int m_iPosition;

 

    //数组的最大长度    
    int m_iMaxSize;

 

public:

    //初始化函数    
    ArrayIterator(Item *pItems,int iMaxSize):m_pItems(pItems),m_iPosition(0),m_iMaxSize(iMaxSize)
    {
    }

 

    bool hasNext()    
    {    
        
//判断当前的元素是否有效

        if ( m_iPosition <  m_iMaxSize && m_pItems[m_iPosition] != NULL )    
        {            
            
return true;    
        }        
        
else    
        {            
            
return false;
        }    
    }

    Item Next()
    {      
        //返回当前的元素,并将位置加一        
        return m_pItems[m_iPosition++];    
    }
};

也没有什么复杂的地方。我们再来看看Subject类的实现:

//目标接口
class Subject
{
public:    
    
//添加一个观察者
    virtual void Attach(Observer *);   

    //删除一个观察者    
    virtual void Detach(Observer *);

    //通知观察者    
    virtual void Notify();

    //创建迭代器    
    Iterator<Observer*> *CreateIterator()    
    {        
        
return new ArrayIterator<Observer*>(m_ObserverList,10);    
    }

protected:
    Subject():m_iPosition(
0)    
    {        
        
for(int i = 0; i < 10 ; i++ )        
            m_ObserverList[i] 
= NULL;    
    }

private:    
    
int m_iPosition;

    //观察者列表    
    Observer* m_ObserverList[10];
};

可以看到现在的Observer列表被定义成了Observer* m_ObserverList[10]。相应的我们帮它增加一个函数:

// 创建迭代器    
Iterator < Observer *>   * CreateIterator()    
{        
    
return   new  ArrayIterator < Observer *> (m_ObserverList, 10 );    
}

 

使用该函数来帮助我们生成一个迭代器。相应的我们以前用到遍历的函数Notify就需要进行相应的修改:
void Subject::Notify()
{    
    
//遍历所有的观察者就行通知    
    Iterator<Observer*> *pObserver = CreateIterator();    
    
while( pObserver->hasNext() )
    {        
        pObserver
->Next()->Update(this);
    }    delete pObserver;
}
    这里就是我们所要介绍的迭代器了。在STL中,迭代器有很好的解决方案,可以通过候捷的《STL源码剖析》来学习。我也正准备看这本书。
    好了,这讲先到这儿。下次我们接着聊聊Visitor模式。

  相关解决方案