观察者模式和委托
1.聊聊委托
委托就是说委托分为发布者和监听者,发布者就是说谁执行这个委托的方法,谁就是发布者,
而谁监听这个委托的方法,谁就是监听者。
比如说我们当点击一个箱子的时候,让它做相应的事件。在一个箱子上公布了一个委托,
那么这个 箱子就是发布者,而这个箱子并不知道要做什么,那么谁去注册了这个委托,
当我们点击这个箱子的时候,就会执行相应的注册方法。
委托用+=表示可以监听多个委托,如果用=的话只能监听一个委托。
所以一般监听用+=。
委托的使用,如果在某个类中某个方法执行的同时需要通知其他类,我们可以在这个类中
写一个委托,在其他类的Start方法中进行注册,在OnDestroy进行解除委托。
2.聊聊观察者模式
观察者核心是委托。
先看客户端,如果从服务器收到的数据,我是通过socket网络模块发给我的业务逻辑,发给我
客户端的各个模块,这个时候我们就要用观察者来把这个数据发送给客户端,
下面通过一个例子,说一下观察者模式:
小明下班了,需要通知他的老婆,兄弟,boss。对于这个需求我们怎么通过代码去实现呢?
这个时候我们只需要让小明发布一个委托,然后在小明下班的方法里面,调这个委托,然后老婆,
兄弟,boss需要去监听小明下班,所以需要在这三个人这里去注册一下这个方法。
小明里面定义的委托
public delegate void XiaoMingXiaBanHandle();
但是我如果想知道几点下班,我们可以往委托里面加个参数,如下
Public delegate void XiaoMingXiaBanHandle(int time);
我们在上面只是做了小明下班的动作,然后让其他人去监听小明下班,但是小明可不只有下班这一个动作,
他还有很多动作,比如我们现在把需求修改成这样,他有上班和下班,他上班的时候,他的老板会知道,
别人不知道,而他下班的时候,这些人都知道,那他的动作就有两个了,那我们怎么做呢?
我们需要再定义一个委托原型,但是如果他的动作太多的话,你定义很多委托就不合适了,
所以我们修改一下委托,把原型修改一下,如下
Public delegate void OnActionHandle(ushort actionID,params object[] param);
参数数组,可以往里面传很多参数。我们这个委托actionID表示动作,后面表示动作的参数;
比如说上班我们用1表示,后面比如说传一个时间,表示上班时间。(1,9)
我们可以在上班的时侯,调用这个委托,在其他监听的对象里面去注册委托,
每个注册委托,可以根据我们定义的actionID去判断,是哪个动作。
比如private void OnShangBan(ushort actionID,object[] param)
{
If(actionID==1)
{
Debug.Log(“上班时间到了”);
}
我们这个朋友圈是一个中间机构,但是呢,比如我发一个动作,我只想要让特定的人知道,
不想让所有人都知道,那应该去怎么解决这个问题啊?
小明里面定义的委托
public delegate void XiaoMingXiaBanHandle();
但是我如果想知道几点下班,我们可以往委托里面加个参数,如下
public delegate void XiaoMingXiaBanHandle(int time);
我们在上面只是做了小明下班的动作,然后让其他人去监听小明下班,但是小明可不只有下班这一个动作,
他还有很多动作,比如我们现在把需求修改成这样,他有上班和下班,他上班的时候,他的老板会知道,
别人不知道,而他下班的时候,这些人都知道,那他的动作就有两个了,那我们怎么做呢?
我们需要再定义一个委托原型,但是如果他的动作太多的话,你定义很多委托就不合适了,所以我们修
改一下委托,把原型修改一下,如下
public delegate void OnActionHandle(ushort actionID,params object[] param);
参数数组,可以往里面传很多参数。我们这个委托actionID表示动作,后面表示动作的参数;
比如说上班我们用1表示,后面比如说传一个时间,表示上班时间。(1,9)
我们可以在上班的时侯,调用这个委托,在其他监听的对象里面去注册委托,每个注册委托,可以根据我
们定义的actionID去判断,是哪个动作。
比如
private void OnShangBan(ushort actionID,object[] param)
{
if(actionID==1)
{
Debug.Log("上班时间到了");
}
我们这个朋友圈是一个中间机构,但是呢,比如我发一个动作,我只想要让特定的人知道,不想让所有人都知道,那应该去怎么解决这个问题啊?
我们再去回到委托原型啊,对其修改成
public delegate void OnActionHandle(params object[] param);
我们加一个字典 ,以动作编号为Id,关注这个动作的所有委托做值,也就是说一个委托的集合,
private Dictionary<ushort,List<OnActionHandle>> dic=new Dictionary<ushort,List<OnActionHandle>>();
///添加监听 监听者在初始化的时候调用 监听某个动作
public void AddEventListener(ushort actionID,OnActionHandle handle )
{
//先判断编号是否在字典中,如果在字典中,直接添加
if(dic.ContainsKey(actionID))
{
dic[actionID].Add(handle);
}
Else
{
//不存在先创建集合,然后往集合里面添加集合
List<OnActionHandle> listHandle=new List<OnActionHandle>();
List.Add(handle);
dic[actionID]=listHandle;
}
}
//移除监听 监听者在OnDestroy的时候调的,不想监听的时候移除
public void RemoveEventListener(ushort actionID,OnActionHandle handle)
{
//如果字典里存在该键actionID,找到对应的委托集合,可以移除hanle
If(dic.ContainsKey(actionID))
{
List<OnActionHandle> listHandle=dic[actionID];
listHandle.Remove(handle);
//如果集合个数为0,从字典里移除该key
if(listHadle.Count==0)
{
dic.remove(actionID);
}
}
}
//派发消息,就是发布者调的方法 只把这个消息派发给监听我这个动作id的人
public void Despatch(ushot actionID,params object[] params)
{
//首先先去字典里去找是否有这个键
if(dic.ContainsKey(actionID))
{
//找到对应的集合
List<OnActionHandl> listHandle=dic[actionID];
//判断集合的个数如果大于0
if(listHandle.Count>0)
{
//遍历集合,执行委托
for(int i=0;i<=listHandle.Count;i++)
{
if(listHande[i]!=null)
{listHandle[i](param);}
}
}
}
我们写观察者的目的一是为了调用方便,二是为了客户端发消息的时候,让服务器去监听。
客户端有了观察者之后,服务器也得有观察者。当然可能和客户端不太一样,还得去修改委托的参数,但是原理是一样的。
下面是根据需求写的观察者工具类
using System.Collections.Generic;
using UnityEngine;
using System;
public class EventDispatcher : Singleton<EventDispatcher> {
//委托
public delegate void OnActionHandle(byte[] buffer);
//一个id对应一个监听队列,一件事情可以有多个监听对象
private Dictionary<ushort,List<OnActionHandle>> dic = new Dictionary<ushort, List<OnActionHandle>> ();
/// <summary>
/// 注册监听,监听某个动作,在监听者的初始化方法中注册监听
/// </summary>
/// <param name="protoID">Proto I.</param>
/// <param name="handle">Handle.</param>
public void AddEventListener(ushort protoID,OnActionHandle handle)
{
if (dic.ContainsKey (protoID)) {
dic [protoID].Add (handle);
} else {
List<OnActionHandle> listHandle = new List<OnActionHandle> ();
listHandle.Add (handle);
dic [protoID] = listHandle;
}
}
/// <summary>
/// 移除监听,在对象销毁的时候去移除
/// </summary>
/// <param name="protoID">Proto I.</param>
/// <param name="handle">Handle.</param>
public void RemoveEventListener(ushort protoID,OnActionHandle handle)
{
if (dic.ContainsKey (protoID)) {
List<OnActionHandle> listHandle = dic [protoID];
dic [protoID].Remove (handle);
if (listHandle.Count == 0) {
dic.Remove (protoID);
}
}
}
/// <summary>
/// 派发消息 发布委托的这边调用的
/// </summary>
public void DisPatched(ushort protoID,byte[] buffer)
{
if (dic.ContainsKey (protoID)) {
List<OnActionHandle> listHandle = dic [protoID];
if (listHandle.Count > 0) {
for (int i = 0; i < listHandle.Count; i++) {
if (listHandle [i] != null) {
listHandle [i](buffer);
}
}
}
}
}
}