概念
我们都知道解决一个问题有N种解决方式,但在面向对象的设计中如何能做到“高内聚,低耦合”,设计可重用的对象才是我们追求的。
观察者模式有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
理解
- 我们可以把观察目标理解为主动方、发布方、主体等;
- 把观察者理解为被动方、订阅方、观察器等。
- 目标是整个行为链的源头,其它观察者都依赖于它的变化而作出响应。
- 为了实现低耦合,我们不能使用“直接调用”的方式而需要利用“关注 – 通知”的机制去完成设计。
- 通俗地说就是观察者“关注”目标它的改变,而目标发生改变后就“通知”所有已经“关注”了它的改变的观察者,从而执行“关注”的内容.
- 这种机制的好处在于降低耦合度,分工明确,目标只负责在自身状态发生改变或做出某种行为时,向自身的”关注”清单发出“通知”,而不是直接调用观察者的行为(方法);
- 观察者只负责“关注”目标它的变化,以及定义自身在收到目标“通知”后所需要做出的具体行为(也就是订阅的内容)。
- 比如:就像博客一样,关注了我之后,我更新一篇文章,你就会收到通知,没关注我,我肯定也没办法塞给你,想要看到我的文章,关注走一波是不是就很方便了
- 主要作用
将一个系统分割成一些相互协作的类,有一个不好的副作用,那就是需要维护相关对象间的一致性。这样会给维护、扩展和重用都带来不便。观察者模式就是解决类与类之间的的耦合关系的。
观察者代码示例
观察者基类
(泛型在这里不做多的介绍,主题是观察者模式)
/// <summary>
/// 观察者派发基类
/// </summary>
/// <typeparam name="T">子类(实现单利)</typeparam>
/// <typeparam name="P">事件的参数</typeparam>
/// <typeparam name="X">字典键的类型</typeparam>
public class DispatcherBase<T, P, X> : IDisposable
where T : new()//子类必须能new出实例
where P : class//事件的参数必须是类
{
#region 单例
private static T instance;
public static T Instance
{
get
{
if (instance == null)
{
instance = new T();
}
return instance;
}
}
public virtual void Dispose()
{
}
#endregion
#region 属性
/// <summary>
/// 委托原型
/// </summary>
/// <param name="p">泛型P 子类可以根据需要改变参数类型</param>
public delegate void OnActionHandler(P p);
//存放观察者的字典
//键X可以理解为 关注了我的博客
//值List可以理解为关注我的人,
//委托OnActionHandler可以理解为我改变了你们的动作(立马看/无视它/等有空了看...)
public Dictionary<X, List<OnActionHandler>> dic = new Dictionary<X, List<OnActionHandler>>();
#endregion
#region 添加监听 AddEventListener
/// <summary>
/// 添加监听(通过这个函数可以关注我的博客)
/// </summary>
/// <param name="key">关注的类型(我的博客)</param>
/// <param name="handler">具体动作(立马看/无视它/等有空了看...)</param>
public void AddEventListener(X key, OnActionHandler handler)
{
//字典中有这个类型
//有人关注过我,我有粉丝群
if (dic.ContainsKey(key))
{
//获取对应类型的集合,把委托加进去
//把新来的人加入到我的粉丝圈里
dic[key].Add(handler);
}
//字典中没有这个类型
//没人关注我,我还没有粉丝群
else
{
//新建一个粉丝群
List<OnActionHandler> lstHandler = new List<OnActionHandler>();
//新来的加到粉丝群里
lstHandler.Add(handler);
//我对应我的粉丝群(key是我,lstHandler是粉丝群)
//key是关注的类型
//lstHandler是观察者
dic[key] = lstHandler;
}
}
#endregion
#region 移除监听 RemoveEventListener
/// <summary>
/// 移除监听(不关注我的博客了)
/// </summary>
/// <param name="protoCode"></param>
/// <param name="handler"></param>
public void RemoveEventListener(X key, OnActionHandler handler)
{
//想要解除关注首先得有被观察者
//假如我不存在,你说不关注我了,可是我本来就不存在啊(想要不关注我,首先我得是存在的)
if (dic.ContainsKey(key))
{
//观察者们(粉丝群)
List<OnActionHandler> lstHandler = dic[key];
//移除对应的观察者handler
//把不关注我的人踢出粉丝群
lstHandler.Remove(handler);
//如果我的粉丝群没有一个人就解散粉丝群
if (lstHandler.Count == 0)
{
dic.Remove(key);
}
}
}
#endregion
#region 派发 Dispatch
/// <summary>
/// 派发事件通知观察者(通知关注我的人,我更新博客了)
/// </summary>
/// <param name="key"></param>
/// <param name="param"></param>
public void Dispatch(X key, P p = null)
{
//想要通知我的粉丝首先得有"我"
if (dic.ContainsKey(key))
{
//粉丝群
List<OnActionHandler> lstHandler = dic[key];
//判断这个粉丝群是不是真的存在(里面有没有人)
if (lstHandler != null && lstHandler.Count > 0)
{
//给每个观察者通知
for (int i = 0; i < lstHandler.Count; i++)
{
//判断观察者是不是存在
if (lstHandler[i] != null)
{
//通知了观察者
//p为参数,泛型自定义
//我可以通知粉丝我更新了什么文章
//也可以不说我更新了什么文章
lstHandler[i](p);
}
}
}
}
}
#endregion
}
子类
/// <summary>
/// UI事件的观察者
/// </summary>
public class UIDispatcher : DispatcherBase<UIDispatcher, object[], string>
{
//子类代码里主要是可以扩展
//观察者代码都封装到了基类中
//子类主要是针对三个泛型
//第一个是传本类,然后本类就是一个单例类了
//第二个是通知的参数类型
//第三个是观察者的类型(可以是商品,人,或者"我")就是字典中键的类型
}
添加观察者
移除观察者
在回调函数中写具体的执行事件(立马看/无视它/等有空了看…)
通知观察者
关注了我的人就都收到我的更新文章通知了,也就是执行你传入的那个委托函数,做你该做的事情了