【UE4 设计模式】观察者模式 Observer Pattern
概述
描述
定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做
- 发布-订阅(Publish/Subscribe)模式
- 模型-视图(Model/View)模式
- 源-监听器(Source/Listener)模式
- 从属者(Dependents)模式。

套路
- 抽象目标(Subject)
被观察的目标,每个目标都可以有任何数量的观察者。抽象目标提供一个接口,可以增加和删除观察者对象。 - 具体目标(ConcreteSubject)
具体目标持有状态,当内部状态改变时,向所有观察者发出通知。同时向观察者提供用于查询状态的接口。 - 抽象观察者(Observer)
为所有的具体观察者定义一个接口,在得到目标通知时更新自己。 - 具体观察者(ConcreteObserver)
持有具体目标的引用。实现抽象观察者角色所要求的更新接口,以便使本身的状态与目标状态协调。
使用场景
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
- 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
- 一个对象必须通知其他对象,而并不知道这些对象是谁。
- 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
- 示例,凡是涉及到一对一或者一对多的对象交互场景都可以使用观察者模式。
- 电子商务网站可以在执行发送操作后给用户多个发送商品打折信息
- 可用于各种消息分发,如游戏活动通知、队伍副本攻略进度
- UE4中的委托代理、蓝图中的事件调度器dispatcher、按键事件绑定、碰撞事件绑定
- MVC 架构模式就应用了观察者模式——多个 view 注册监听 model
优缺点
- 优点
- 观察者模式可以实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色。
- 观察者模式在观察目标和观察者之间建立一个抽象的耦合。
- 观察者模式支持广播通信。
- 观察者模式符合“开闭原则”的要求。
- 缺点
- 如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
- 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
- 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
- 订阅后如果不进行取消订阅操作容易引起内存泄露
UE4 实践
不使用 UE4 中的委托代理,自己实现一个消息通知
做一个核辐射监测和救援人员受其影响的例子(代码都写在头文件中,有些声明问题需自己解决)
创建观察者抽象类、具体类
// 抽象观察者类
UCLASS(Abstract)
class DESIGNPATTERNS_API UObserver : public UObject
{
GENERATED_BODY()
public: virtual void Update() PURE_VIRTUAL(UObserver::IsValid, );
}; // 具体观察者类 —— 工作人员
UCLASS(Blueprintable, BlueprintType)
class DESIGNPATTERNS_API UWorker : public UObserver
{
GENERATED_BODY()
public: // 设置核辐射监测对象
void SetNuclearRadiation(UNuclearRadiation* pNuclearRadiation) {
m_pNuclearRadiation = pNuclearRadiation;
} virtual void Update() override {
if (m_pNuclearRadiation)
{
int32 CurrentLevel = m_pNuclearRadiation->GetRadiationDegree();
if (CurrentLevel < 1)
{
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s报告: 生命体征正常"), *this->GetName());
}
else if (CurrentLevel < 2)
{
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s报告: 轻级损伤"), *this->GetName());
}
else if (CurrentLevel < 4)
{
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s报告: 中级损伤"), *this->GetName());
}
else if (CurrentLevel < 6)
{
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s报告: 重级损伤"), *this->GetName());
}
else if (CurrentLevel < 8)
{
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s报告: 极重级损伤"), *this->GetName());
}
else
{
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s已无生命体征"), *this->GetName());
this->ConditionalBeginDestroy();
// 不要在此处取消订阅,否则影响TArray遍历
}
}
} private:
UNuclearRadiation* m_pNuclearRadiation;
};
创建目标抽象类 、具体类
- 观察者可以用TArray 或者 TMap 存储
// 抽象目标类
UCLASS(Abstract)
class DESIGNPATTERNS_API USubject : public UObject
{
GENERATED_BODY()
public: // 添加订阅者
virtual void BindNotify(UObserver* Observer) {
if (!ObserverList.Contains(Observer)) {
ObserverList.Add(Observer);
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s 已订阅"), *Observer->GetName());
// 第一次绑定 获取当前数据
Observer->Update();
}
} // 移除订阅者
virtual void UnbindNotify(UObserver* Observer) {
if (ObserverList.Contains(Observer)) {
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s 已取消订阅"), *Observer->GetName());
ObserverList.Remove(Observer);
}
} // 通知
virtual void Notify() {
for (auto It = ObserverList.CreateConstIterator(); It; It++)
{
if (IsValid(*It))
{
(*It)->Update();
}
}
} protected:
TArray<UObserver*> ObserverList;
}; // 具体目标类 —— 辐射地带、辐射监测
UCLASS(Blueprintable, BlueprintType)
class DESIGNPATTERNS_API UNuclearRadiation : public USubject
{
GENERATED_BODY()
public:
// 更新辐射程度
void UpdateRadiationDegree(int32 pLevel) {
m_pLevel = pLevel;
Notify();
} // 获取辐射程度
int32 GetRadiationDegree() {
return m_pLevel;
} private:
// 辐射程度 1-9级
int32 m_pLevel;
};
调用测试
// 调用测试用的Actor
UCLASS()
class DESIGNPATTERNS_API AObserverActor : public AActor
{
GENERATED_BODY()
public: void BeginPlay() override { // 创建核辐射监测
UNuclearRadiation* NuclearRadiation = NewObject<UNuclearRadiation>(); // 创建救援人员、监测核辐射程度
UWorker* Worker0 = NewObject<UWorker>();
Worker0->SetNuclearRadiation(NuclearRadiation);
NuclearRadiation->BindNotify(Worker0); UWorker* Worker1 = NewObject<UWorker>();
Worker1->SetNuclearRadiation(NuclearRadiation);
NuclearRadiation->BindNotify(Worker1); UWorker* Worker2 = NewObject<UWorker>();
Worker2->SetNuclearRadiation(NuclearRadiation);
NuclearRadiation->BindNotify(Worker2); // 核辐射升级
NuclearRadiation->UpdateRadiationDegree(1);
NuclearRadiation->UpdateRadiationDegree(2);
NuclearRadiation->UpdateRadiationDegree(3); // 有人先出来了,取消核辐射继续影响
NuclearRadiation->UnbindNotify(Worker2); // 核辐射继续升级
NuclearRadiation->UpdateRadiationDegree(5);
NuclearRadiation->UpdateRadiationDegree(7);
NuclearRadiation->UpdateRadiationDegree(9); GEngine->ForceGarbageCollection(true);
}
};
调式输出
LogTemp: Warning: USubject::BindNotify Worker_0 已订阅
LogTemp: Warning: UWorker::Update Worker_0报告: 生命体征正常 LogTemp: Warning: USubject::BindNotify Worker_1 已订阅
LogTemp: Warning: UWorker::Update Worker_1报告: 生命体征正常 LogTemp: Warning: USubject::BindNotify Worker_2 已订阅
LogTemp: Warning: UWorker::Update Worker_2报告: 生命体征正常 LogTemp: Warning: UWorker::Update Worker_0报告: 轻级损伤
LogTemp: Warning: UWorker::Update Worker_1报告: 轻级损伤
LogTemp: Warning: UWorker::Update Worker_2报告: 轻级损伤 LogTemp: Warning: UWorker::Update Worker_0报告: 中级损伤
LogTemp: Warning: UWorker::Update Worker_1报告: 中级损伤
LogTemp: Warning: UWorker::Update Worker_2报告: 中级损伤 LogTemp: Warning: UWorker::Update Worker_0报告: 中级损伤
LogTemp: Warning: UWorker::Update Worker_1报告: 中级损伤
LogTemp: Warning: UWorker::Update Worker_2报告: 中级损伤 LogTemp: Warning: USubject::UnbindNotify Worker_2 已取消订阅 LogTemp: Warning: UWorker::Update Worker_0报告: 重级损伤
LogTemp: Warning: UWorker::Update Worker_1报告: 重级损伤 LogTemp: Warning: UWorker::Update Worker_0报告: 极重级损伤
LogTemp: Warning: UWorker::Update Worker_1报告: 极重级损伤 LogTemp: Warning: UWorker::Update Worker_0已无生命体征
LogTemp: Warning: UWorker::Update Worker_1已无生命体征
参考
【UE4 设计模式】观察者模式 Observer Pattern的更多相关文章
- 设计模式-观察者模式(Observer Pattern)
观察者模式(Observer Pattern):定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己. 观察者 ...
- 设计模式 - 观察者模式(Observer Pattern) 详细说明
观察者模式(Observer Pattern) 详细说明 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26583157 版权全部 ...
- 设计模式 - 观察者模式(Observer Pattern) 详细解释
观察者模式(Observer Pattern) 详细解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26583157 版权全部 ...
- 设计模式 - 观察者模式(Observer Pattern) Java内置 用法
观察者模式(Observer Pattern) Java内置 用法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26601659 ...
- C#设计模式——观察者模式(Observer Pattern)1
一.概述在软件设计工作中会存在对象之间的依赖关系,当某一对象发生变化时,所有依赖它的对象都需要得到通知.如果设计的不好,很容易造成对象之间的耦合度太高,难以应对变化.使用观察者模式可以降低对象之间的依 ...
- C#设计模式——观察者模式(Observer Pattern)
一.概述在软件设计工作中会存在对象之间的依赖关系,当某一对象发生变化时,所有依赖它的对象都需要得到通知.如果设计的不好,很容易造成对象之间的耦合度太高,难以应对变化.使用观察者模式可以降低对象之间的依 ...
- 23种设计模式--观察者模式-Observer Pattern
一.观察者模式的介绍 观察者模式从字面的意思上理解,肯定有两个对象一个是观察者,另外一个是被观察者,观察者模式就是当被观察者发生改变得时候发送通知给观察者,当然这个观察者可以是多个对象,在项 ...
- 乐在其中设计模式(C#) - 观察者模式(Observer Pattern)
原文:乐在其中设计模式(C#) - 观察者模式(Observer Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 观察者模式(Observer Pattern) 作者:weba ...
- 二十四种设计模式:观察者模式(Observer Pattern)
观察者模式(Observer Pattern) 介绍定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新. 示例有一个Message实体类,某些对象 ...
- java设计模式--观察者模式(Observer)
java设计模式--观察者模式(Observer) java设计模式--观察者模式(Observer) 观察者模式的定义: 定义对象间的一种一对多的依赖关系.当一个对象的状态发生改变时,所有依赖于它的 ...
随机推荐
- nRF52832蓝牙iBeacon广播
开发环境 SDK版本:nRF5_SDK_15.0.0 芯片:nRF52832-QFAA 蓝牙iBeacon实现 iBeacon的核心就是广播,不需要进行连接,通过在广播包中插入信息然后广播出去. 广播 ...
- poll?transport=longpoll&connection...烦人的请求c
1.问题描述: 最近使用miniui做了一个后台管理系统,打开浏览器调试时,总发现一堆无关的请求,结构大致是:poll?transport=longpoll&connection.....一直 ...
- 别再自建仓库了,云效Maven仓库不限容量免费用
别再自建仓库了,云效Maven仓库不限容量免费用云效制品仓库 Packages提供maven私有仓库.npm私有仓库.通用制品仓库等企业级私有制品仓库,用于maven.npm等软件包和依赖管理.不限容 ...
- sync 修饰符在Vue中如何使用
在有些情况下,我们可能需要对一个 prop 进行"双向绑定".不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以修改父组件,且在父组件和子组件都没有明显的改动来源. 这 ...
- 【第七篇】- Git 分支管理之Spring Cloud直播商城 b2b2c电子商务技术总结
Git 分支管理 几乎每一种版本控制系统都以某种形式支持分支.使用分支意味着你可以从开发主线上分离开来,然后在不影响主线的同时继续工作. 有人把 Git 的分支模型称为必杀技特性,而正是因为它,将 ...
- GMAP.NET系列学习文档
http://www.cnblogs.com/enjoyeclipse/archive/2013/01/13/2858392.html http://www.cnblogs.com/luxiaoxun ...
- freeswitch编译安装依赖
ncurses:提供字符界面 zlib:数据压缩 libjpeg:JPEG图片格式数据的解码/编码/其他. lua:lua解释器 libedit:一种编辑操作的库,对一些可以交互操作的场景,或转为了自 ...
- 动态规划精讲(一)LC最长公共子序列
P1439 [模板]最长公共子序列 题目描述 给出1,2,-,n 的两个排列P1 和P2 ,求它们的最长公共子序列. 输入格式 第一行是一个数 n. 接下来两行,每行为 n 个数,为自然数 1,2 ...
- 知乎vscode插件修改和重新编译
需求来源 vscode插件修改代码要怎样重新编译并安装到vscode中? 起源于我使用一个vscode插件,它可以在vscode中发布文章到知乎上,然后我修改了插件的部分源代码,希望在vscode中安 ...
- ecshop调用商品的购买次数方法
这时候我们修改一下 写成一个函数放到lib_goods.php 这样就可以随便调用了 --------------------------------------------------------- ...