观察者模式是一种行为设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都会得到通知并自动更新。
优点:
1.解耦性强:
观察者(订阅者)与主题(发布者)通过抽象接口通信,不直接依赖具体实现,修改主题或观察者的代码时,无需影响对方,提升代码可维护性。
2.动态订阅机制:
观察者可随时注册或注销,运行时灵活调整事件响应,支持热插拔功能,增强系统扩展性。
3.一对多通信高效:
一个主题状态变化可同时通知多个观察者,避免重复调用逻辑,提升事件处理效率。
4.支持事件驱动架构:
通过事件总线(Event Bus)实现全局通信, 简化跨模块通信,适合复杂系统交互。
缺点:
1.性能开销:
高频事件(如每帧更新)或大量观察者时,遍历通知列表耗时(物理引擎的CollisionEvent在百个对象碰撞时,触发千次通知)。
优化:
1.使用事件合并(如积累多次位置更新后批量通知)。
2.异步处理(将事件推送到队列,分帧处理)。
2.内存泄漏风险:
观察者未正确注销时,Subject(主题者)持有其引用导致无法回收(Unity中,UI面板销毁前未取消订阅事件)。
优化:
使用弱引用(WeakReference)或者使用扩展方法在MonoBehaviour绑定取消事件注册的方法,使其在OnDestroy时调用取消注册方法。
public static void UnregisterOnDestroy(this MonoBehaviour obj, Action unsubscribe) {obj.gameObject.AddComponent<OnDestroyDispatcher>().OnDestroyEvent += unsubscribe;
}class OnDestroyDispatcher : MonoBehaviour {public event Action OnDestroyEvent;void OnDestroy() => OnDestroyEvent?.Invoke();
}
注:弱引用的使用 (会加一篇Unity中弱引用的测试使用的文章)。
3.事件顺序不可控:
观察者处理事件的顺序依赖注册顺序,可能导致逻辑错误。
优化:
引入优先级字段,按优先级排序观察者。
4.调试困难:
问题:事件流分散,难以追踪事件触发源头和传递链路。
优化:
添加事件日志:记录每个事件的发布者和订阅者。
说明例子:
1.UML图:
2.实现:
1.实现Subject(主题者)基类:
public abstract class Subject{List<Observer> m_Observers = new List<Observer>();//加入观察者public void Attach(Observer theObserver){m_Observers.Add(theObserver);}//删除观察者public void Detach(Observer theObserver){m_Observers.Remove(theObserver);}//通知所有观察者public void Notify(){foreach (Observer theObserver in m_Observers){theObserver.Update();}}}
2.实现Observer(观察者)基类:
public abstract class Observer{public abstract void Update();}
3.实现具体Subject:
public class ConcreteSubject : Subject{string m_SubjectState;public void SetState(string state){this.m_SubjectState = state;this.Notify();}public string GetState(){return this.m_SubjectState;}}
4.实现具体Observer:
//实现Observer1public class ConcreteObserver1 : Observer{ConcreteSubject m_Subject = null;public ConcreteObserver1(ConcreteSubject theSubject){this.m_Subject = theSubject;}public override void Update(){Debug.Log("ConcreteObserver1.Update");//获取Subject状态Debug.Log("ConcreteObserver1 : Subject 当前主题:" + m_Subject.GetState());}}//实现Observer2public class ConcreteObserver2 : Observer{ConcreteSubject m_Subject = null;public ConcreteObserver2(ConcreteSubject theSubject){this.m_Subject = theSubject;}public override void Update(){Debug.Log("ConcreteObserver2.Update");//获取Subject状态Debug.Log("ConcreteObserver2 : Subject 当前主题:" + m_Subject.GetState());}}
游戏中使用场景:
1.成就/统计系统:监听关键游戏事件(击杀、死亡、收集)--->玩家击杀Boss后解锁成就。
2.UI更新:将UI元素与游戏数据解耦--->血量变化时自动刷新血条UI。
3.跨系统通信:避免系统间直接引用--->背包系统物品使用后通知技能系统。
4.AI行为触发:基于事件触发的敌人反应---->玩家进入警戒范围触发敌人警报。
5.网络同步:将本地事件广播给其他客户端--->玩家位置同步事件。
总结:
观察者模式的核心价值在于解耦和动态通信,但其性能、内存和调试问题需谨慎处理。
在游戏开发中,观察者模式适用于以下场景:
1.需要松耦合的跨系统通信。
2.需要动态处理大量事件类型。
3.需要支持模块化扩展(如MOD系统)。
参考书籍:
《Hands-On Game Development Patterns with Unity 2019》
《设计模式与游戏完美开发》