🤔
观察者模式概述
- 观察者模式也被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
观察者模式用处
- 将一个系统分割成一个一些类相互协作的类有一个不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。观察者就是解决这类的耦合关系的。
观察者模式角色
- 抽象主题(Subject):它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
- 具体主题(ConcreteSubject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。
- 抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
- 具体观察者(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。
以下是观察者模式的类图:
观察者模式实现
1 | /// <summary> |
观察者模式解除了主题和具体观察者的耦合,让耦合的双方都依赖于抽象,而不是依赖具体。从而使得各自的变化都不会影响另一边的变化。
但此例中依赖关系并未完全解除,抽象通知者依旧依赖抽象的观察者
在c#中的可以使用事件委托来彻底解除通知者和观察者之间的耦合。
委托:委托是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法有相同的行为。委托方法可以像其它任何方法一样,具有参数和返回值。委托可以看作是对函数(方法)的的抽象,是函数的“类”,委托的实例代表一个(或多个)具体的函数,它可以是多播的。
事件:事件基于委托,为委托提供了一种发布/订阅机制。事件的订阅与取消与我们刚才讲的观察者模式中的订阅与取消类似,只是表现形式有所不同。在观察者模式中,订阅使用方法Attach()来进行;在事件的订阅中使用“+=”。类似地,取消订阅在观察者模式中用Dettach(),而事件的取消用“-=”。
Unity中的观察者模式
- 在unity游戏开发中,最常用到的设计模式就是单例模式和观察者模式,在之上已经介绍了观察者模式,以下通过代码来直观的了解在unity中,观察者模式的简单使用。
- RPG游戏中我们拥有自己的角色,角色通常拥有名称和等级,以下程序中通过观察者模式实现点击鼠标左键增加角色等级。
ShowPlayer.cs
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 using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 观察者
/// </summary>
public class ShowPlayer : MonoBehaviour {
public PlayerData mHero;
//设置通知者
public void SetData(PlayerData player){
mHero = player;
//事件绑定
mHero.Register(Show);
Show();
}
private void Show(){
print("Hero's name " + mHero.name);
print("Hero's rank " + mHero.Rank);
}
}
PlayerData.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 数据类
/// 通知者
/// </summary>
public class PlayerData : SubjestBase {
public string name;
private string rank;
public string Rank{
get{
return rank;
}
set{
rank = value;
Notified();
}
}
}
SubjestBase.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 using System;
/// <summary>
/// 通知者基类
/// </summary>
public class SubjestBase {
private event Action eventHander;
//注册事件
public void Register(Action func){
eventHander += func;
}
//删除事件
public void UnRegister(Action func){
eventHander -= func;
}
//发送通知
public void Notified(){
if(eventHander != null){
eventHander();
}
}
}
PlayerSelectCtrl.cs
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 using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 控制器
/// </summary>
public class PlayerSelectCtrl : MonoBehaviour {
private ShowPlayer showPlayer;
private PlayerData player;
void Start () {
showPlayer = GetComponentInChildren<ShowPlayer>();
player = User.GetPlayer();
showPlayer.SetData(player);
}
//数据更新
void Update () {
if(Input.GetMouseButtonDown(0)){
player.Rank = (int.Parse(player.Rank) + 1).ToString();
}
}
}
User.cs
1
2
3
4
5
6
7
8
9
10
11
12
13 using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class User{
public static PlayerData GetPlayer(){
PlayerData player = new PlayerData();
player.name = "Yu";
player.Rank = "7";
return player;
}
}