设计模式 --- 观察者模式

观察者模式是一种行为设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都会得到通知并自动更新。

优点:

​​1.解耦性强​​

​​观察者(订阅者)与主题(发布者)通过抽象接口通信,不直接依赖具体实现,​​修改主题或观察者的代码时,无需影响对方,提升代码可维护性。

2.​​动态订阅机制​​

​​观察者可随时注册或注销,运行时灵活调整事件响应,支持热插拔功能,增强系统扩展性。

3.​​一对多通信高效​​

一个主题状态变化可同时通知多个观察者,避免重复调用逻辑,提升事件处理效率。

​​4.支持事件驱动架构​​

通过事件总线(Event Bus)实现全局通信, ​​简化跨模块通信,适合复杂系统交互。

缺点:

1.​性能开销​​:

高频事件(如每帧更新)或大量观察者时,遍历通知列表耗时(物理引擎的CollisionEvent在百个对象碰撞时,触发千次通知)。

​​优化​​

1.使用事件合并(如积累多次位置更新后批量通知)。

2.异步处理(将事件推送到队列,分帧处理)。

2.​​内存泄漏风险​​:

​​观察者未正确注销时,Subject(主题者)持有其引用导致无法回收(Unity中,UI面板销毁前未取消订阅事件)。

​​优化​​

使用弱引用(WeakReference)或者使用扩展方法在MonoBehaviour绑定取消事件注册的方法,使其在OnDestroy时调用取消注册方法。

cs 复制代码
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(主题者)基类:

cs 复制代码
    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(观察者)基类:

cs 复制代码
    public abstract class Observer
    {
        public abstract void Update();
    }

3.实现具体Subject:

cs 复制代码
    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:

cs 复制代码
   //实现Observer1
   public 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());
       }
   }

   //实现Observer2
   public 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》

《设计模式与游戏完美开发》

相关推荐
on the way 1239 小时前
结构性设计模式之Flyweight(享元)
java·设计模式·享元模式
暴躁哥13 小时前
深入理解设计模式之访问者模式
设计模式·访问者模式
佩奇的技术笔记14 小时前
从Java的JDK源码中学设计模式之装饰器模式
java·设计模式·装饰器模式
on the way 12314 小时前
结构型设计模式之Proxy(代理)
设计模式·代理模式
YGGP17 小时前
【结构型模式】装饰器模式
设计模式
将编程培养成爱好19 小时前
《复制粘贴的奇迹:小明的原型工厂》
c++·设计模式·原型模式
liang_jy20 小时前
设计模式中的几大原则
设计模式·面试
huangyujun99201231 天前
设计模式杂谈-模板设计模式
java·设计模式
magic 2451 天前
Java设计模式:责任链模式
java·设计模式·责任链模式
YGGP1 天前
【结构型模式】代理模式
设计模式