观察者模式
前言
观察者模式是为了将观察者和被观察者进行解耦,时被观察者在被改变时观察者可以及时的做出响应。例如:邮件通知,各种短信消息的订阅,扣费通知等等。
一、观察者模式(发布-订阅模式,事件订阅者)
是一种订阅机制,在对象事件发生时通知多个观察者。例如发布订阅中的订阅者,在消息被发布到指定位置后,订阅该位置的订阅者监听到消息,然后可以进行消息处理。
二、角色分析
- 抽象主题(ISubject):指被观察的对象,是一个抽象类或接口,定义了增删通知观察者对象的方法。
- 具体主题(ConcreteSubject):具体被观察者,当内部发生变化时,会通知已注册的观察者。
- 抽象观察者(IObserver):定义了相应通知的更新方法。
- 具体观察者(ConcreteObserver):当得到状态更新的通知时,会自动做出响应。
三、应用场景
- 当一个抽象模型包含两方面内容,其中一方面依赖另一方面的紧耦合情况。
- 一个对象的变化,将影响另外一个或多个对象的一系列变化。
- 实现类似广播机制的功能,不需要知道具体的收听者,只需要分发广播,系统中感兴趣的对象会自动接收该广播。
- 多层级嵌套使用,形成一种链式触发机制,是的事件具备跨域通知
四、代码实现
1.抽象观察者
代码如下(示例):抽象消息的处理方法
/// <summary>
/// 抽象观察者
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IObserver<T>
{
public void update(T t);
}
2.抽象主题
代码如下(示例):抽象观察者的管理
/// <summary>
/// 抽象主题
/// </summary>
public interface ISubject<T>
{
ISubject<T> attach(IObserver<T> observer);
bool detach(IObserver<T> observer);
void notify(T t);
}
3.具体观察者
代码如下(示例):对观测到的消息的处理
/// <summary>
/// 具体观察者
/// </summary>
/// <typeparam name="T"></typeparam>
public class ConcreteObserver<T> : IObserver<T>
{
/// <summary>
/// 答应观察到的消息
/// </summary>
/// <param name="t"></param>
public void update(T t)
{
Console.WriteLine("receive event:" + JsonConvert.SerializeObject(t));
}
}
4.具体主题
代码如下(示例):对订阅者的管理(注册,注销),以及消息的发布,
/// <summary>
/// 具体主题
/// </summary>
/// <typeparam name="T"></typeparam>
public class ConcreteSubject<T> : ISubject<T>
{
private List<IObserver<T>> observers = new List<IObserver<T>>();
/// <summary>
/// 绑定发布者和订阅者关系
/// </summary>
/// <param name="observer"></param>
/// <returns></returns>
public ISubject<T> attach(IObserver<T> observer)
{
if (!this.observers.Contains(observer))
{
Console.WriteLine($"将{JsonConvert.SerializeObject(observer)}添加到 List<IObserver<{typeof(T).Name}>>集合");
this.observers.Add(observer);
return this;
}
else
{
return this;
}
}
/// <summary>
/// 删除发布订阅关系
/// </summary>
/// <param name="observer"></param>
/// <returns></returns>
public bool detach(IObserver<T> observer)
{
return this.observers.Remove(observer);
}
/// <summary>
/// 调用接收者消息
/// </summary>
/// <param name="t"></param>
public void notify(T t)
{
for (var i = 0; i < observers.Count; i++) {
Console.Write(i+"----------");
observers[i].update(t);
}
}
}
5.调用客户端
代码如下(示例):客户端创建观察者及被观察者,并绑定它们之间的关系,发布消息进行实现。
/// <summary>
/// 调用客户端
/// </summary>
public class Clienter {
public void mains() {
//创建被观察者
ISubject<string> observable = new ConcreteSubject<string>();
//实例化观察者
IObserver<string> observer = new ConcreteObserver<string>();
IObserver<string> observer1 = new ConcreteObserver<string>();
//注册观察者
observable
.attach(observer)
.attach(new ConcreteObserver<string>())
.attach(new ConcreteObserver<string>())
.attach(new ConcreteObserver<string>())
.attach(new ConcreteObserver<string>())
.attach(new ConcreteObserver<string>());
//发布消息
observable.notify("hello");
}
}
6.输出
将{}添加到 List<IObserver<String>>集合
将{}添加到 List<IObserver<String>>集合
将{}添加到 List<IObserver<String>>集合
将{}添加到 List<IObserver<String>>集合
将{}添加到 List<IObserver<String>>集合
将{}添加到 List<IObserver<String>>集合
0----------receive event:"hello"
1----------receive event:"hello"
2----------receive event:"hello"
3----------receive event:"hello"
4----------receive event:"hello"
5----------receive event:"hello"
总结
优点
- 观察者及被观察者是松散耦合的,复合依赖倒置原则。
- 分离了观察者和被观察者,并建立了一套触发机制,使数据的变化可以影响到多个观察者。
- 实现了一对多的通讯机制,支持事件注册机制,支持兴趣分发机制,当被观察者触发事件时,只有感兴趣的观察者会做出响应。
缺点
- 如果观察者数量过多,则事件通知会耗时较长。
- 事件通知称线性关系,如果其中一个观察者处理事件卡壳,则会影响后续的观察者接受该事件。
- 如果观察者和被观察者之间存在循环依赖,则可以能造成两者之间的循环调用,导致系统崩溃。
- 只能保持线性依赖,不能存在闭环依赖。