● 意图
1> 在N个对象之间,建立一对多的联系:一个对象(主体)状态改变,其它对象(客体)都得到通知,并且做出相应的更新。
2> 核心、通用的模块,封装到主体类;因个体而异的可变的功能,封装到客体类。
3> 实现MVC体系中的View。
● 问题
一个软件设计在规模逐渐庞大时,其“图形化系统”/“监视系统”如果和系统耦合较紧,则做不到相应的、很好的更新。此时,可用observer模式进行重构。
● 实现步骤
1> 找到应用程序中相似的、可选的、并行的、外围的功能/模块。
2> 给它们定义一个通用接口(最好是个抽象类),这样它们就具备了互换性,多态了,可在运行时自适应自己的行为。此之为:observer。
3> 然后给各个相似、可选、并行、外围的模块,都定义一个派生类。
4> 找到应用程序中核心、通用功能/模块,封装到一个主体类。主体只和上面定义的抽象observer相耦合。
5> 客户在使用时,把那些相似、可选、外围的对象,全都注册给这个主体对象。
6> 程序运行时,不用单独地和那些外围模块交互。它只需要调用那个“核心”,发布一个广播即可。
7> 所有的外围对象接收到“更新”通知,做出相应的改变。还可以做主体的回调。
● 讨论
所谓“主体”,就是程序的数据模型、应用逻辑。把所有外围的功能,交给互相并不耦合的客体。客体在主体上注册。主体用广播的形式通知所有客体。客体用查询的形式监视主体的变量。
这样,外围功能的类型、数量就全都可以在运行时决定。
这是一种“被动查询式”的交互模型,也可以用“主动推送式”模型。推送式交互限制了重用;查询式交互效率低。
设计者要考虑:广播的数目、类型不能太多;是否允许一个客体同时监视多个主体;主体销毁前要通知客体。
●结构图
● 重构前的例子(主体客体耦合在一起):
class DivObserver { int m_div; public: DivObserver( int div ) { m_div = div; } void update( int val ) { cout << val << " div " << m_div << " is " << val / m_div << '\n'; } }; class ModObserver { int m_mod; public: ModObserver( int mod ) { m_mod = mod; } void update( int val ) { cout << val << " mod " << m_mod << " is " << val % m_mod << '\n'; } }; class Subject { int m_value; DivObserver m_div_obj; ModObserver m_mod_obj; public: Subject() : m_div_obj(4), m_mod_obj(3) { } void set_value( int value ) { m_value = value; notify(); } void notify() { m_div_obj.update( m_value ); m_mod_obj.update( m_value ); } }; int main( void ) { Subject subj; subj.set_value( 14 ); } // 14 div 4 is 3 // 14 mod 3 is 2
● 重构后
class Observer { public: virtual void update( int value ) = 0; }; class Subject { int m_value; vector m_views; public: void attach( Observer* obs ) { m_views.push_back( obs ); } void set_val( int value ) { m_value = value; notify(); } void notify() { for (int i=0; i < m_views.size(); ++i) m_views[i]->update( m_value ); } }; class DivObserver : public Observer { int m_div; public: DivObserver( Subject* model, int div ) { model->attach( this ); m_div = div; } void update( int v ) { cout << v << " div " << m_div << " is " << v / m_div << '\n'; } }; class ModObserver : public Observer { int m_mod; public: ModObserver( Subject* model, int mod ) { model->attach( this ); m_mod = mod; } void update( int v ) { cout << v << " mod " << m_mod << " is " << v % m_mod << '\n'; } }; int main( void ) { Subject subj; DivObserver divObs1( &subj, 4 ); DivObserver divObs2( &subj, 3 ); ModObserver modObs3( &subj, 3 ); subj.set_val( 14 ); } // 14 div 4 is 3 // 14 div 3 is 4 // 14 mod 3 is 2