在用UE开发游戏的过程中使用设计模式可以帮助我们解耦对象依赖关系,使得程序逻辑清晰,也有助于复用代码。甚至UE引擎本身就使用了很多的设计模式,比如组件模式、状态模式等。
概述
观察者模式(Observer Pattern)又称为发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式、从属者模式(Dependents)模式。
观察者模式属于设计模式中的行为型模式,行为型模式用于定义对象之间的交互行为,观察者模式主要用于对象间的一种一对多的依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。或者说观察者监听目标是否发生变化,如果发生变化则更新观察者状态。
我们以游戏中的一个场景举例,当玩家击杀场景中的敌人,则更新Widget中的敌人剩余数量信息,当敌人被消灭殆尽后,大门打开。分析一下,如果我们使用最粗暴的方式,在敌人类的代码中,死亡时调用Widget更新敌人数量信息函数,之后再调用大门中检查是否消灭完敌人要打开门的函数。这样我们“成功”地将敌人对象和Widget/大门对象耦合在了一起。这样做的坏处是它破坏了开放封闭原则,比如我们想新加入一个粒子发射器,当敌人被消灭完后播放粒子特效,那么我们就需要修改敌人类中的代码,在死亡方法中加入调用粒子发射器播放粒子特效的函数。
那么观察者模式是如何做到解耦的呢?观察者模式抽象出抽象的观察者类和抽象的目标类,具体目标比如上面场景中的敌人类是抽象目标类的子类,而上面场景中的Widget和大门类是抽象观察者类的子类。
- 抽象目标(Subject)
被观察的目标,每个目标都可以有任何数量的观察者。抽象目标提供一个接口,可以增加和删除观察者对象。 - 具体目标(ConcreteSubject)
具体目标持有状态,当内部状态改变时,向所有观察者发出通知。同时向观察者提供用于查询状态的接口。 - 抽象观察者(Observer)
为所有的具体观察者定义一个接口,在得到目标通知时更新自己。 - 具体观察者(ConcreteObserver)
持有具体目标的引用。实现抽象观察者角色所要求的更新接口,以便使本身的状态与目标状态协调。
经过上面的抽象,我们可以看到具体的目标并不需要知道观察者是谁,而是通过抽象的目标进行通知,而在前文提到场景中,加入一个粒子发射器只需要将其继承自抽象的观察者即可,并实现相应的update()
方法即可,不破坏开放封闭原则。
其时序图如下
UE实践
下面我们在UE中使用观察者模式实现上文提到的敌人死亡时调用Widget和大门的相关函数,这里利用UE蓝图中的事件分发器,下面我们创建一个BP_Enemy
蓝图类,静态网格体就用cube代替。敌人蓝图类中新增一个OnKilled
事件分发器,并实现组件碰撞事件和销毁事件。
BP_Door
大门蓝图类将每一个敌人的OnKilled
事件分发器和CS_KiillAEnemy
事件绑定
同理,Widget中也将每个敌人的OnKilled
与CS_EnemyIsKilled
事件绑定
现在我们就使用观察者模式实现了击杀敌人更新UI和打开大门
完成项目下载链接
总结
优点
- 观察者模式可以实现表示层和数据逻辑层的分离(MV模式),定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者
- 观察者模式在观察目标和观察者之间建立一个抽象的耦合
- 观察者模式支持广播通信(一对多)
- 观察者模式符合开闭原则的要求
缺点
- 如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间(引入中介者模式?)
- 如果在观察者和观察目标之间有循环依赖的话,观察目标(比如在观察者更新的时候改变了目标的状态导致又触发通知 无限循环😓)会触发它们之间进行循环调用,可能导致系统崩溃
- 观察者没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化(所以需要我们要根据使用的场景决定,不知道信息也是解耦合的)
适用环境
在以下情况下可以使用观察者模式:
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
- 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。(可能后续会有新的类被添加到观察者中比如上文场景的粒子发射器)
- 一个对象必须通知其他对象,而并不知道这些对象是谁。
- 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
参考资料
Graph Design Patterns —— 观察者模式
【双字精译】虚幻引擎中的设计模式:观察者模式——Ali Elizoheiry|游戏开发游戏编程模式游戏设计模式虚幻蓝图编程事件分发器教程UnrealUE4UE5