首先要明确的是观察者与被观察者之间的关系

对于观察者,如果存在指定的被观察者,则需要将其观察事件派发给被观察者或者其他中介,因为被观察者(中介)才是响应端,被观察者来响应观察事件,而非观察者,如果该观察者将要销毁,则需要回收观察事件。

对于被观察者,如果存在指定的观察者,则需要接收观察者事件,并在特定时机响应他们,如果已有的观察者丢失,也需要观察者/被观察者/中介剔除其对应的观察者事件

最简单的观察者模式实现

来个情境:

在电车之狼情景下,你是一名正直的路人,你观察着痴汉,如果他有什么变态动作,你就会揭穿他的丑恶行径

有两个问题需要解决:

  1. 如果众人皆浊你独清该怎么办?

    电车里1000个人,除了你和Beauty,其余998个人都是痴汉,并且这些痴汉都很独特,那我一个路人里面岂不是要时刻观察记录998个不同种的对象?

  2. 如果众人皆清你独浊该怎么办?

    这次你变成了痴汉,1000个人的电车里只有你是痴汉,你被999个人观察着,那岂不是999个人都得被你占用精力,记录着除了痴汉属性外的其他无用属性?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//首先定义痴汉(被观察者)
public class Hentai:MonoBehaviour
{
//观察者事件(痴汉执行痴汉行为时响应)
public event Action Obsever;

private void Update()
{
if(Input.GetKeyDown(KeyCode.R))
{
Hentaing();
}
}
//痴汉开始行动函数
public void Hentaing()
{
痴汉行动中...
//行动后响应观察者事件
Observer();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//路人观察者
public class Spectator:MonoBehaviour
{
//记录被观察者
public Hentai hentai;
private void Start()
{
//给痴汉添加路人揭穿事件
hentai.Observer+=揭穿;
}
void (假设是易语言)揭穿() {}
//剔除观察事件
private void OnDestory()
{
hentai.Observer-=MouseRun;
}
}

你也可以用面向接口设计替换event来实现,区别在于,前者,观察事件的接收和剔除由被观察者执行,而在event实现下,观察事件的接收和剔除由观察者执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
定义观察者接口
public interface IObserver{    void Monitor();}
旁观者继承该接口
public class Spectator:MonoBehaviourIObserver
就变成了
//路人观察者
public class Spectator:MonoBehaviour,IObserver
{
属性字段...
void Monitor() {}
}
//痴汉(被观察者)
public class Hentai:MonoBehaviour
{
//观察者事件(痴汉执行痴汉行为时响应)
public List<IObserver> Obsevers;
void NotifyObserver()
{
foreach(var a in Obsevers)
{
a.Monitor();
}
}
//添加观察者
Add()
//移除
Remove()

void Awake()
{
Obsevers=new List<IObserver>();
}
private void Update()
{
if(Input.GetKeyDown(KeyCode.R))
{
Hentaing();
}
}
//痴汉开始行动函数
public void Hentaing()
{
痴汉行动中...
//行动后响应观察者事件
NotifyObserver();
}
}

继续优化:

问题:

  1. 之前路人对痴汉的关系都是简单的二分关系,如果路人也被痴汉观察着呢?如果再加上美女观察痴汉,痴汉观察美女,路人,路人观察痴汉,美女的关系呢?

    当关系越来越复杂,既是观察者也是被观察者的身份存在时,继续用上面的方法些就会越来越纠缠不清,所以需要解耦.

  2. 观察者路人不但有正直的,还有冷漠的,若是正直的路人要揭发,就可能存在要传递痴汉的某个信息,就代表了要传参,

    若是冷漠的路人,他将会无动于衷,就代表了他的观察事件是无需传参的。

    所以我们还需要对事件进行一定程度的封装。

优化之事件中心

为了解耦,事件中心就产生了。

它相当于在观察者于被观察者间增加了个中介,所有(被)观察者只知道中介,而不知道对方,中介负责所有观察事件的接收和响应以及删除

事件中心是全局唯一的,所有用单例模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
using UnityEngine.Events;
//事件封装
public interface IEventInfo
{

}

public class EventInfo<T> : IEventInfo
{
public UnityAction<T> actions;

public EventInfo( UnityAction<T> action)
{
actions += action;
}
}

public class EventInfo : IEventInfo
{
public UnityAction actions;

public EventInfo(UnityAction action)
{
actions += action;
}
}

//事件中心
public class EventCenter : BasicManager<EventCenter>
{
//key —— 事件的名字
//value —— 对应的委托函数
private Dictionary<string, IEventInfo> eventDic = new Dictionary<string, IEventInfo>();

/// <summary>
/// 监听需要参数传递的事件
/// </summary>
/// <param name="name">事件的名字</param>
/// <param name="action">准备用来处理事件 的委托函数</param>
public void AddEventListener<T>(string name, UnityAction<T> action)
{
//有对应事件的情况
if( eventDic.ContainsKey(name) )
{
(eventDic[name] as EventInfo<T>).actions += action;
}
//没有的情况
else
{
eventDic.Add(name, new EventInfo<T>( action ));
}
}

/// 监听不需要参数传递的事件
public void AddEventListener(string name, UnityAction action)
{
//有的情况
if (eventDic.ContainsKey(name))
{
(eventDic[name] as EventInfo).actions += action;
}
//没有的情况
else
{
eventDic.Add(name, new EventInfo(action));
}
}

/// 移除需要参数的事件监听
public void RemoveEventListener<T>(string name, UnityAction<T> action)
{
if (eventDic.ContainsKey(name))
(eventDic[name] as EventInfo<T>).actions -= action;
}

/// 移除不需要参数的事件
public void RemoveEventListener(string name, UnityAction action)
{
if (eventDic.ContainsKey(name))
(eventDic[name] as EventInfo).actions -= action;
}

/// 触发需要参数的事件
public void EventTrigger<T>(string name, T info)
{
if (eventDic.ContainsKey(name))
{
if((eventDic[name] as EventInfo<T>).actions != null)
(eventDic[name] as EventInfo<T>).actions.Invoke(info);
}
}

///触发事件(不要参数)
public void EventTrigger(string name)
{
if (eventDic.ContainsKey(name))
{
if ((eventDic[name] as EventInfo).actions != null)
(eventDic[name] as EventInfo).actions.Invoke();
}
}

/// 清空事件
public void Clear()
{
eventDic.Clear();
}
}