太行新闻网

主页 > 教育科技 > > 正文

观察者模式 c++实例

2014-04-28 23:06
来源: 未知
【字号: 】【打印

最近在做摄像头图像采集程序时,参考了陆其明先生的《DirectShow实物精选》。书中有一个实例程序:AVCap,大家如果也在做这方面 的项目的话,这绝对是一个很值得参考的例子。这里只关心AVCap中关于“观察者模式”的应用。学习设计模式的最好办法,就是实际去应用,或者找到实际应 用的例子来参考。

“观察者模式”又称“发布-订阅(Publish/Subscribe)模式。它定义了一种一对多的依赖关系,可以让多个观察者对象同时监听某一个主题对象。当这个主题对象的状态发生变化时,会通知所有观察者对象。

“观察者模式”所做的工作主要是解耦合。让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化,方便程序的扩展、维护、复用。关于“观察者模式”的详细内容推荐伍迷的《大话数据结构》。

在AVCap程序中,“观察者模式”主要用来支持视频采集设备的热插拔。当一个采集设备接入系统时,“通知者”处理WM_DEVICECHANGE消息,然后通知“观察者”。

(1 )通知者基类

CMsgStation就好像是消息发送站,Broadcast()和Notify()函数相当于上面UML图中的notifyObservers(),ReceiveMessage()相当于notify()。

CMsgStation.h:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#ifndef __H_CMsgStation__
#define __H_CMsgStation__
 
#include "CObjectList.h"
 
typedef long MessageT ;
typedef long NotifyT ;
 
class CMsgReceiver ;
class CMsgStation
{
protected :
     CObjectList   mReceivers ;
 
public :
     CMsgStation ( void ) ;
     virtual ~ CMsgStation ( void ) ;
 
     //增加一个观察者(当某个界面类希望获得数据改变的通知)
     void AddMsgReceiver ( CMsgReceiver * inReceiver ) ;
 
     //删除一个观察者(当某个界面类不再需要获得数据改变的通知)
     void RemoveMsgReceiver ( CMsgReceiver * inReceiver ) ;
 
     //将数据的改变通知列表中所有的观察者
     bool Broadcast ( MessageT inMessage , void * ioParam = 0 , void * ioParam2 = 0 ) ;
 
     //另一种形式的通知函数:通知参数中携带了通知的发送者
     bool Notify ( NotifyT inNotification , long inParam1 = 0 , long inParam2 = 0 ) ;
} ;
 
const MessageT msg_StationDestroyed = 0 ;
const MessageT msg_Notify            = 1 ;
 
struct SNotificationStruct
{
     NotifyT             mNotification ;
     CMsgStation *      mStation ;
     long              mParam1 ;
     long              mParam2 ;
} ;
 
#endif // __H_CMsgStation__

CMsgStation.cpp:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include "stdafx.h"
#include "CMsgStation.h"
#include "CMsgReceiver.h"
 
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE [ ] = __FILE__ ;
#endif
 
CMsgStation :: CMsgStation ( void )
{
}
 
CMsgStation :: ~ CMsgStation ( void )
{
     Broadcast ( msg_StationDestroyed , ( void * ) this ) ;
}
 
void CMsgStation :: AddMsgReceiver ( CMsgReceiver * inReceiver )
{
     if ( inReceiver )
     {
         if ( mReceivers . GetIndex ( inReceiver ) == - 1 )
         {
             mReceivers . Add ( inReceiver ) ;
             inReceiver -> AddMsgStation ( this ) ;
         }
     }
}
 
void CMsgStation :: RemoveMsgReceiver ( CMsgReceiver * inReceiver )
{
     if ( inReceiver )
     {
         inReceiver -> RemoveMsgStation ( this ) ;
         mReceivers . Remove ( inReceiver ) ;
     }
}
 
bool CMsgStation :: Broadcast ( MessageT inMessage , void * ioParam , void * ioParam2 )
{
     for ( int i = mReceivers . GetSize ( ) - 1 ; i >= 0 ; i -- )
     {
         CMsgReceiver * receiver = ( CMsgReceiver * ) mReceivers . GetAt ( i ) ;
         if ( receiver )
         {
             receiver -> ReceiveMessage ( inMessage , ioParam , ioParam2 ) ;
         }
     }
     return true ;
}
 
bool CMsgStation :: Notify ( NotifyT inNotification , long inParam1 , long inParam2 )
{
     SNotificationStruct   notification ;
     notification . mNotification = inNotification ;
     notification . mStation = this ;
     notification . mParam1    = inParam1 ;
     notification . mParam2    = inParam2 ;
     return Broadcast ( msg_Notify , &notification ) ;
}

(2 )接收者基类

CMsgReceiver就好像是消息接收器,ReceiveMessage()相当于上面UML图中的notify()。Respond()被ReceiveMessage()调用,对于通知做出相应处理。

CMsgStation.h:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ifndef __H_CMsgReceiver__
#define __H_CMsgReceiver__
 
#include "CMsgStation.h"
 
class CMsgReceiver
{
protected :
     CObjectList   mStations ;
 
public :
     CMsgReceiver ( void ) ;
     virtual ~ CMsgReceiver ( void ) ;
 
     void AddMsgStation ( CMsgStation * inStation ) ;
     void RemoveMsgStation ( CMsgStation * inStation ) ;
 
     virtual bool ReceiveMessage ( MessageT inMessage , void * ioParam , void * ioParam2 ) ;
     virtual bool Respond ( NotifyT inNotification , CMsgStation * inStation , long inParam1 , long inParam2 ) ;
} ;
 
#endif // __H_CMsgReceiver__

CMsgStation.cpp:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include "stdafx.h"
#include "CMsgReceiver.h"
 
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE [ ] = __FILE__ ;
#endif
 
CMsgReceiver :: CMsgReceiver ( void )
{
}
 
CMsgReceiver :: ~ CMsgReceiver ( void )
{
     for ( int i = mStations . GetSize ( ) - 1 ; i >= 0 ; i -- )
     {
         CMsgStation * station = ( CMsgStation * ) mStations . GetAt ( i ) ;
         if ( station )
         {
             station -> RemoveMsgReceiver ( this ) ;
         }
     }
}
 
void CMsgReceiver :: AddMsgStation ( CMsgStation * inStation )
{
     mStations . Add ( inStation ) ;
}
 
void CMsgReceiver :: RemoveMsgStation ( CMsgStation * inStation )
{
     mStations . Remove ( inStation ) ;
}
 
bool CMsgReceiver :: ReceiveMessage ( MessageT inMessage , void * ioParam , void * ioParam2 )
{
     switch ( inMessage )
     {
     case msg_Notify :
         {
             SNotificationStruct * notification = ( SNotificationStruct * ) ioParam ;
             return Respond ( notification -> mNotification , notification -> mStation ,
                 notification -> mParam1 , notification -> mParam2 ) ;
         }
 
     case msg_StationDestroyed :
         RemoveMsgStation ( ( CMsgStation * ) ioParam ) ;
         return true ;
     }
     return false ;
}
 
bool CMsgReceiver :: Respond ( NotifyT inNotification , CMsgStation * inStation ,
                         long inParam1 , long inParam2 )
{
     return false ;
}

(3 )流程概要

AVCap接着定义了一系列CMsgStation和CMsgReceiver的派生类,用来具体支持热插拔。

CDeviceObserver处理WM_DEVICECHANGE消息,并作为“通知者”(CDeviceObserver的名字中有一个单词Observer,但它不是“观察者”,它的父类是CMsgStation)。

CVideoDevices是多重继承(public CMsgStation, public CMsgReceiver),即作为“接收者”,也作为“通知者”。它在ReceiveMessage()中处理从CDeviceObserver发出的 通知,然后再调用Broadcast()通知它的观察者(CAVCapDlg)。

CAVCapDlg是用来预览和操作的界面类,它有一个父类CMsgReceiver。CAVCapDlg重写了ReceiveMessage()和Respond()。

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
bool CAVCapDlg :: ReceiveMessage ( MessageT inMessage , void * ioParam , void * ioParam2 )
{
     switch ( inMessage )
     {
     case msg_PnPDeviceAdded :
         FillVideoDevices ( ) ;
         return true ;
 
     case msg_PnPDeviceRemoved :
         FillVideoDevices ( ) ;
         return true ;
     }
 
     return CMsgReceiver :: ReceiveMessage ( inMessage , ioParam , ioParam2 ) ;
}

CAVCapDlg在收到通知后,调用 FillVideoDevices()。在FillVideoDevices()中会利用CVideoDevices重新获取视频采集设备,并更新界面上 显示视频源的组合框。最后FillVideoDevices()调用CLiveCapture:: SetVideoDevice(deviceName)重新设置视频采集设备。

接着CAVCapDlg:: ReceiveMessage调用父类 CMsgReceiver::ReceiveMessage(),CMsgReceiver::ReceiveMessage()调用Respond() 处理msg_Notify, CAVCapDlg重写了Respond()虚函数用来复位或者重构filter graph。

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
bool CAVCapDlg :: Respond ( NotifyT inNotification , CMsgStation * inStation , long inParam1 , long inParam2 )
{
     switch ( inNotification )
     {
     case cVideoSourceChanged : // Reset filter graph
         CreateController ( ) ;
         return true ;
 
     case cAudioSourceChanged : // Reset filter graph
         CreateController ( ) ;
         return true ;
 
     case cVideoConnectorChanged :
         EnterVideoConnector ( ) ;
         return true ;
 
     case cVideoResolutionChanged :
         // Rebuild the filter graph, or preview will be disordered
         //当视频制式改变时对Filter Graph进行重构
         if ( controller_ )
         {
             //销毁当前Filter Graph
             controller_ -> Deactive ( ) ;
             //构建一个新的Filter Graph
             controller_ -> Activate ( ) ;
         }
         return true ;
     }
 
     return CMsgReceiver :: Respond ( inNotification , inStation , inParam1 , inParam2 ) ;
}

最后会调用CGraphController::Activate()构建新的过滤器图。

整个热插拔处理的执行流程如下:

CDeviceObserver::OnDeviceChange() -> (CDeviceObserver父类)CMsgStation::Broadcast() –> CVideoDevices::ReceiveMessage() -> (CDeviceObserver父类)CMsgStation::Broadcast() -> CAVCapDlg::ReceiveMessage() -> CAVCapDlg::FillVideoDevices() -> CLiveCapture:: SetVideoDevice() -> CAVCapDlg::Respond() –> CAVCapDlg::CreateController() -> CGraphController::Activate()

Tags:
分享到:
( 编辑: 3.wanshehui.com ) 【字号: 】【打印】【关闭

 
Copyright © 2009 - 2013 wanshehui.com
Copyright © 2009-2013 太行新闻网 冀ICP备11009293号 网站地图