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

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

优点:

​​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》

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

相关推荐
码农秋8 小时前
设计模式系列(10):结构型模式 - 桥接模式(Bridge)
设计模式·桥接模式
GodKeyNet8 小时前
设计模式-桥接模式
java·设计模式·桥接模式
N_NAN_N14 小时前
类图+案例+代码详解:软件设计模式----原型模式
java·设计模式·原型模式
缘来是庄14 小时前
设计模式之组合模式
java·设计模式·组合模式
DKPT14 小时前
Java组合模式实现方式与测试方法
java·笔记·学习·设计模式·组合模式
鼠鼠我呀214 小时前
【设计模式09】组合模式
设计模式·组合模式
N_NAN_N17 小时前
类图+案例+代码详解:软件设计模式----单例模式
java·单例模式·设计模式
尤物程序猿17 小时前
设计模式之代理模式--数据库查询代理和调用日志记录
设计模式·代理模式
GodKeyNet1 天前
设计模式-模板模式
设计模式·模板模式
缘来是庄1 天前
设计模式之建造者模式
java·设计模式·建造者模式