意图
定义对象间一对多的关系,一个对象变化时,所有依赖它的对象都得到通知并被自动更新。一个最典型的例子就是Windows中的音量控制器,我们可以打开多个音量控制器窗口。当其中一个变化的时候,其余均随之变化。
使用场合
改变一个对象需要通知其他对象,而不知道有多少个对象有待改变
一个对象必须通知其他对象,而又不能假定这些对象,几不希望这些对象是紧密耦合的。
结构
Subject(目标):知道观察者,可以有多个任意多个观察者观察一个目标。
Observer(观察者):为观察者定一个更新接口。
ConcreteSubject(具体目标):根据ConcreteSubject更新观察者。
ConcreteObserver(具体对象观察者):根据ConcreteSubject更新观察者。
这是传统的观察者模式结构,如果采用委托技术,则Subject不需要知道Observer的存在,这时只要将Notify委托给Observer对象即可。
效果
采用观察者模式的优点是降低了目标和观察者之间的耦合性。如果采用.NET的委托和事件机制实现观察者模式,可以使目标和观察者之间没有静态的耦合关系。
由于观察者模式不限定观察者的数量,因此可以支持广播,目标发送信息时不需要制定观察者。
采用委托和事件机制实现观察者模式
然而采用观察者模式的代价就是如果通讯设计不当,会产生意想不到的连锁反应。由于观察者模式动态执行,所以这种连锁反应很难调试,常见的连锁异常出现在事件处理上。
模拟声音控制器
声音控制类:
using System.Collections.Generic;
using System.Text;
namespace ObserverPattern
{
public class VolumeControl
{
//代理变化事件的代理
public delegate void VolumeChangeEvent(int v);
//音量变化的事件
public event VolumeChangeEvent VolumeChanged;
private int volume = 0;
/// <summary>
/// 音量
/// </summary>
public int Volume
{
get { return volume; }
set
{
volume = value;
if (VolumeChanged != null)
{
VolumeChanged(volume);
}
}
}
}
}
声音控制面板窗体:
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace ObserverPattern
{
public partial class FrmSub : Form
{
private VolumeControl vc1;
private VolumeControl vc2;
public FrmSub(VolumeControl v1, VolumeControl v2)
{
InitializeComponent();
vc1 = v1;
vc2 = v2;
}
private void FrmSub_Load(object sender, EventArgs e)
{
}
public void ChangeVolume1(int v)
{
vScrollBar1.Value = v;
}
public void ChangeVolume2(int v)
{
vScrollBar2.Value = v;
}
private void vScrollBar1_ValueChanged(object sender, EventArgs e)
{
vc1.Volume = vScrollBar1.Value;
}
private void vScrollBar2_ValueChanged(object sender, EventArgs e)
{
vc2.Volume = vScrollBar2.Value;
}
private void FrmSub_FormClosed(object sender, FormClosedEventArgs e)
{
//注销事件
vc1.VolumeChanged -= new VolumeControl.VolumeChangeEvent(ChangeVolume1);
vc2.VolumeChanged -= new VolumeControl.VolumeChangeEvent(ChangeVolume2);
}
}
}
测试窗体:
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace ObserverPattern
{
public partial class Form1 : Form
{
private VolumeControl vc1;
private VolumeControl vc2;
public Form1()
{
InitializeComponent();
vc1 = new VolumeControl();
vc2 = new VolumeControl();
}
private void button1_Click(object sender, EventArgs e)
{
FrmSub fs = new FrmSub(vc1,vc2);
//注册事件
vc1.VolumeChanged += new VolumeControl.VolumeChangeEvent(fs.ChangeVolume1);
vc2.VolumeChanged += new VolumeControl.VolumeChangeEvent(fs.ChangeVolume2);
fs.Show();
}
}
}
事件连锁
使用观察者模式及事件委托时,需要注意连锁情况。如果处理不当,则发生“死锁”,即使程序进入死循环。发生死锁的原因有多种,最常见的是两个对象的状态需要保持一致,并且二者互为主题和观察者。其中一个发生变化,另一个也随之变化。当某种条件导致循环调用时,会产生死锁。
相关模式