观察者模式(Observer):在对象间定义一种
一对多
的依赖关系,以便当某对象的状态改变时,与它存在依赖关系的所有对象都能收到通知并自动进行更新。
- MVC 模式的底层可以说就是利用了观察者模式
# 基础设计
我们可以使用如下的方式设计一个简单的观察者模式,在被观察者中需要存储一个观察者组,提供一个发送消息的方法对观察组进行消息推送。
在观察者中提供一个消息响应的方法,响应消息。
/// <summary> | |
/// 事件 | |
/// </summary> | |
public enum Event | |
{ | |
捡到金币, | |
通关 | |
} |
/// <summary> | |
/// 观察者接口 | |
/// </summary> | |
public interface IObserver | |
{ | |
/// <summary> | |
/// 接收通知 | |
/// </summary> | |
/// <param name="subject"></param> | |
/// <param name="eventMsg"></param> | |
void OnNotify(Subject subject, Event eventMsg); | |
} | |
/// <summary> | |
/// 被观察者接口 | |
/// </summary> | |
public interface ISubject | |
{ | |
List<IObserver> Observers(); | |
void AddObserver(IObserver observer); | |
void RemoveObserver(IObserver observer); | |
} |
/// <summary> | |
/// 观察者 | |
/// </summary> | |
public class Observer : IObserver | |
{ | |
public void OnNotify(Subject subject, Event eventMsg) | |
{ | |
switch (eventMsg) | |
{ | |
case Event.捡到金币: | |
subject.Score += 10; | |
break; | |
case Event.通关: | |
subject.Score += 100; | |
break; | |
} | |
} | |
} | |
/// <summary> | |
/// 被观察者 | |
/// </summary> | |
public class Subject : ISubject | |
{ | |
public int Score; | |
private readonly List<IObserver> _observers; | |
/// <summary> | |
/// 发送消息 | |
/// </summary> | |
/// <param name="eventMsg"></param> | |
public void Notify(Event eventMsg) | |
{ | |
_observers.ForEach(x=>x.OnNotify(this,eventMsg)); | |
} | |
public Subject() | |
{ | |
_observers = new List<IObserver>(); | |
} | |
public List<IObserver> Observers() | |
{ | |
return _observers; | |
} | |
public void AddObserver(IObserver observer) | |
{ | |
_observers.Add(observer); | |
} | |
public void RemoveObserver(IObserver observer) | |
{ | |
_observers.Remove(observer); | |
} | |
} |
# 使用委托和事件
在 Csharp 中内置了委托和事件来处理这种一对多的响应方式。
using System; | |
namespace ObserverPattern | |
{ | |
/// <summary> | |
/// 观察者模式 | |
/// </summary> | |
internal static class Program | |
{ | |
public static void Main(string[] args) | |
{ | |
var c = new Cat() {Name = "小白"}; | |
var m = new Mouse(c) {Name = "小鼠"}; | |
c.OnCatCom(); | |
} | |
} | |
class Cat | |
{ | |
public string Name { get; set; } | |
public event Action catCom; | |
public virtual void OnCatCom() | |
{ | |
Console.WriteLine("{0}来了",Name); | |
catCom?.Invoke(); | |
} | |
} | |
class Mouse | |
{ | |
public string Name { get; set; } | |
public Mouse(Cat cat) | |
{ | |
// 将事件绑定到方法 | |
cat.catCom += this.MouseRun; | |
} | |
private void MouseRun() | |
{ | |
Console.WriteLine("{0}快跑!!",Name); | |
} | |
} | |
} |
# 思考
其实观察者模式就是对象的一种 间接同步调用
,使用这种方法是为了解除耦合,达到统一分配管理的作用。
从上文提到的可以知道,这种模式是同步的,也就是说当存在一组观察者时,那么可能会导致 阻塞
,但在实际中可能没这么严重,但在使用中我们还需注意。
如果你在每次响应后给出一个状态,表示通知的响应情况,那么这就接近
责任链模式
了。
# 销毁对象?
在书中提到一个问题很有趣,在观察者模式中销毁对象该怎么办呢?
在销毁对象前,需要解除监听,确保对象被正确销毁。
# 现状
观察者模式出现于 1994 年(比我还大😄), 因此基本都是基于类来实现的,但现在更流行
函数式编程
,可能不太符合现在的编程美学。例如 C# 的 event 就是现代化的观察者模式。