【UE4设计模式】观察者模式ObserverPattern

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

【UE4设计模式】观察者模式ObserverPattern
概述
描述
定义对象间的⼀种⼀对多依赖关系,使得每当⼀个对象状态发⽣改变时,其相关依赖对象皆得到通知并被⾃动更新。

观察者模式⼜叫做
发布-订阅(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已⽆⽣命体征参考。

相关文档
最新文档