模式简介
观察者模式是一种行为型设计模式,顾名思义它通常是用于对某个或某类对象进行观察,根据所观察的对象的状态而进行处理,例如对UI控件输入事件的监听或者游戏中敌人状态变化触发数值系统的处理等。在观察者模式中,我们习惯于划分出主题和观察者,可以有一到多个观察者对主题进行观察,观察者需要向主题进行注册以便于在主题的状态发生改变时接收到通知或者完成预定的处理。
常见的应用场景:事件处理系统、消息通知系统、发布-订阅模式、MVC架构、库和框架的事件机制、监控系统、游戏开发等。
模式结构
-
主题(Subject):
- 定义了一个接口,包含添加、删除和通知观察者的方法。
- 维护一个观察者列表,用于存储所有注册的观察者。
- 被具体主题实现。
-
具体主题(ConcreteSubject):
- 实现主题接口,具体主题是被观察的对象。
- 维护自身状态,并在状态变化时通知所有观察者。
- 当状态变化时,调用主题的通知方法,触发所有注册观察者的更新操作。
-
观察者(Observer):
- 定义了一个更新方法,用于在主题状态变化时接收通知。
- 被具体观察者实现。
-
具体观察者(ConcreteObserver):
- 实现观察者接口,具体观察者是观察主题状态变化的对象。
- 在接收到主题通知时执行具体的更新操作。
工作原理
-
主题注册观察者:
- 具体主题对象维护一个观察者列表。
- 观察者通过主题提供的注册方法将自己添加到主题的观察者列表中。
-
状态变化通知观察者:
- 具体主题的状态发生变化时,调用通知方法。
- 通知方法遍历观察者列表,调用每个观察者的更新方法。
-
观察者更新操作:
- 具体观察者的更新方法被调用,获取主题传递的新状态。
- 具体观察者根据新状态执行相应的业务逻辑。
-
解耦性增强:
- 主题和观察者之间松散耦合,主题不需要知道观察者的具体实现,只需要知道观察者遵循观察者接口。
- 这样的设计使得系统更加灵活,可以方便地增加或删除观察者,而不影响主题或其他观察者。
代码示例(C#)
提示:可在本栏目的资源篇“设计模式代码示例合集”下载所有完整代码资源。
切换UI组件:Toggle.cs
namespace ObserverPattern;
// UI控件状态(激活、禁用)
enum UIItemState
{
Enabled, Disabled
}
// UI控件接口
interface IUIItem
{
// UI控件状态
UIItemState state { get; set; }
// 注册观察者
void Register(IObserve observe);
// 注销观察者
void UnRegister(IObserve observe);
}
// Toggle切换控件
class Toggle : IUIItem
{
public UIItemState state
{
get { return _state; }
set
{
if (_state != value)
{
_state = value;
NotifyObservers();
}
}
}
private List<IObserve> observes; // 观察者集合
private UIItemState _state; // Toggle组件的状态
public Toggle()
{
observes = new List<IObserve>();
_state = UIItemState.Enabled;
}
public void Register(IObserve observe)
{
observes.Add(observe);
}
public void UnRegister(IObserve observe)
{
observes.Remove(observe);
}
// 通知所有注册的观察者
private void NotifyObservers()
{
for (int i = 0; i < observes.Count; i++)
{
observes[i].Handle(_state);
}
}
}
窗口:Window.cs
namespace ObserverPattern;
// 观察接口
interface IObserve
{
// 当观察的UI控件的状态发生变化时的处理
void Handle(UIItemState state);
}
// 窗口A
class WindowA : IObserve
{
public void Handle(UIItemState state)
{
if (state == UIItemState.Enabled)
Console.WriteLine("WindowA:The toggle's state is enabled, you have switched to Window A.");
}
}
// 窗口B
class WindowB : IObserve
{
public void Handle(UIItemState state)
{
if (state == UIItemState.Disabled)
Console.WriteLine("WindowB:The toggle's state is disabled, you have switched to Window B.");
}
}
测试代码:Program.cs
// ************* 10.观察者模式测试 **************
using ObserverPattern;
Toggle toggle = new Toggle();
WindowA windowA = new WindowA();
WindowB windowB = new WindowB();
// 向Toggle组件注册两个窗口为观察者
toggle.Register(windowA);
toggle.Register(windowB);
// 设置Toggle组件的状态为Disabled
toggle.state = UIItemState.Disabled;
// 设置Toggle组件的状态为Enabled
toggle.state = UIItemState.Enabled;
代码解说
上述代码简单模拟了通过Toggle这个UI组件来实现对窗口的切换。当Toggle组件为Enabled状态时切换为窗口A,当状态为Disabled时则切换为窗口B,当窗口A或窗口B被聚焦时在控制台打印输出相关的信息。窗口A和B作为观察者,Toggle组件作为主题,Toggle组件的状态发生变化时则会自动遍历所有注册了的观察者,并调用它们所提供的接口方法,然后窗口A和B根据Toggle组件的状态完成各自预定的处理。
如果这篇文章对你有帮助,请给作者点个赞吧!