定义
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己。
结构图
例子
//观察者类:
class Observer
{
public:
Observer(string strName, Subject* strSub)
{
name = strName;
sub = strSub;
}
void Update()
{
cout << name << ":" << sub->action << endl;
}
private:
string name;
Subject* sub;
};
//主题类
class Subject
{
public:
string action;
void Add(Observer ob)
{
observers.push_back(ob);
}
void Remove(int addIndex)
{
if(addIndex >= 0 && addIndex < observers.size())
observers.erase(observers.begin() + addIndex);
}
void Notify()
{
vector<StockObserver>::iterator it;
for(it = observers.begin(); it != observers.end(); ++it)
{
(*it).Update();
}
}
private:
vector<StockObserver> observers;
};
//客户端调用
int main()
{
Subject* p = new Subject();
Observer* s1 = new Observer("a", p);
Observer* s2 = new Observer("b", p);
p->Add(*s1);
p->Add(*s2);
p->action = "Message";
p->Notify();
p->Remove(0);
p->Notify();
}
适用性
- 当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
- 当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
- 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。
优点
(1) 观察者模式在被观察者和观察者之间建立一个抽象的耦合。被观察者角色所知道的只是一个具体现察者聚集,每一个具体现察者都符合一个抽象观察者的接口。被观察者并不认识任何一个具体观察者,它只知道它们都有一个共同的接口。由于被观察者和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。
(2) 观察者模式支持广播通信。被观察者会向所有的登记过的观察者发出通知。
缺点
(1) 如果一个被观察者对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
(2) 如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察考模式时要特别注意这一点。
(3) 如果对观察者的通知是通过另外的线程进行异步投递的话,系统必须保证投递是以自恰的方式进行的。
(4) 虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎么发生变化的。
多语言应用
- 在.NET中可以利用Delegate与Event机制来实现观察者模式。
- Java提供了Observer 和Observable来帮助我们简化实现观察者模式。
特点
观察者模式所做的工作其实就是在接触耦合,让耦合的双方都依赖于抽象,而不是依赖于具体,从而使得各自的变化的都不会影响另一边的变化。
委托
(1) 委托是一种引用方法的类型,一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托和函数一样,具有参数和返回值。委托可以看成是对函数的抽象,是函数的类,委托的实例将代表一个具体的函数。
(2) 一个委托可以搭载多个函数,所有函数被一次唤起,还可以使得委托对象所搭载的方法并不属于同一个类。
(3) 委托对象所搭载的所有函数必须具有相同的原形和形式,也就是参数列表和返回值类型完全相同。