• UE4 C++设计模式:观察者模式(Observer Pattern)


    目录

    描述

    套路

    使用场景

    优缺点

    UE4实践

    创建观察者抽象类

    创建具体观察者类:工作人员

    创建目标抽象类

    创建目标具体类


    描述

    定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象都得到通知并更新。

    观察者模式又称

    • 发布-订阅(Publish-Subscribe)模式
    • 模型-视图(Model-View)模式
    • 源-监听器(Source-Listener)模式
    • 从属者(Dependents)模式

    套路

    • 抽象目标(Subject)
      被观察的目标,每个目标都可以有任何数量的观察者。抽象目标提供一个接口,可以增加和删除观察者对象。
    • 具体目标(ConcreteSubject)
      具体目标持有状态,当内部状态改变时,向所有观察者发出通知。同时向观察者提供用于查询状态的接口。
    • 抽象观察者(Observer)
      为所有的具体观察者定义一个接口,在得到目标通知时更新自己。
    • 具体观察者(ConcreteObserver)
      持有具体目标的引用。实现抽象观察者角色所要求的更新接口,以便使本身的状态与目标状态协调。

    使用场景

    • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
    • 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
    • 一个对象必须通知其他对象,而并不知道这些对象是谁。
    • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
    • 示例,凡是涉及到一对一或者一对多的对象交互场景都可以使用观察者模式。
      • 电子商务网站可以在执行发送操作后给用户多个发送商品打折信息
      • 可用于各种消息分发,如游戏活动通知、队伍副本攻略进度
      • UE4中的委托代理、蓝图中的事件调度器dispatcher、按键事件绑定、碰撞事件绑定
      • MVC 架构模式就应用了观察者模式——多个 view 注册监听 model

    优缺点

    • 优点
      • 观察者模式可以实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色
      • 观察者模式在观察目标和观察者之间建立一个抽象的耦合
      • 观察者模式支持广播通信
      • 符合开闭原则
    • 缺点
      • 如果一个观察目标对象有很多直接和简介的观察者的话,将所有的观察者都通知到会花费很多时间
      • 如果在观察者和观察目标之间有循环依赖的话,会发生循环引用,可能导致系统崩溃
      • 观察者模式没有相应的机制让观察者知道所观察的目标是怎么发生变化的,而仅仅知道观察目标发生了变化
      • 订阅后如果不进行取消订阅操作,容易引起内存泄漏

    UE4实践

    不适用UE4中的委托代理,自己实现一个消息通知

    做一个核辐射检测和救援人员受影响的例子

    创建观察者抽象类

    1. // 抽象观察者类
    2. UCLASS(Abstract)
    3. class DESIGNPATTERNS_API UObserver : public UObject
    4. {
    5. GENERATED_BODY()
    6. public:
    7. virtual void Update() PURE_VIRTUAL(UObserver::IsValid, );
    8. };

    创建具体观察者类:工作人员

    1. // 具体观察者类 —— 工作人员
    2. UCLASS(Blueprintable, BlueprintType)
    3. class DESIGNPATTERNS_API UWorker : public UObserver
    4. {
    5. GENERATED_BODY()
    6. public:
    7. // 设置核辐射监测对象
    8. void SetNuclearRadiation(UNuclearRadiation* pNuclearRadiation) {
    9. m_pNuclearRadiation = pNuclearRadiation;
    10. }
    11. virtual void Update() override {
    12. if (m_pNuclearRadiation)
    13. {
    14. int32 CurrentLevel = m_pNuclearRadiation->GetRadiationDegree();
    15. if (CurrentLevel < 1)
    16. {
    17. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s报告: 生命体征正常"), *this->GetName());
    18. }
    19. else if (CurrentLevel < 2)
    20. {
    21. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s报告: 轻级损伤"), *this->GetName());
    22. }
    23. else if (CurrentLevel < 4)
    24. {
    25. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s报告: 中级损伤"), *this->GetName());
    26. }
    27. else if (CurrentLevel < 6)
    28. {
    29. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s报告: 重级损伤"), *this->GetName());
    30. }
    31. else if (CurrentLevel < 8)
    32. {
    33. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s报告: 极重级损伤"), *this->GetName());
    34. }
    35. else
    36. {
    37. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s已无生命体征"), *this->GetName());
    38. this->ConditionalBeginDestroy();
    39. // 不要在此处取消订阅,否则影响TArray遍历
    40. }
    41. }
    42. }
    43. private:
    44. UNuclearRadiation* m_pNuclearRadiation;
    45. };

    创建目标抽象类

    1. // 抽象目标类
    2. UCLASS(Abstract)
    3. class DESIGNPATTERNS_API USubject : public UObject
    4. {
    5. GENERATED_BODY()
    6. public:
    7. // 添加订阅者
    8. virtual void BindNotify(UObserver* Observer) {
    9. if (!ObserverList.Contains(Observer)) {
    10. ObserverList.Add(Observer);
    11. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s 已订阅"), *Observer->GetName());
    12. // 第一次绑定 获取当前数据
    13. Observer->Update();
    14. }
    15. }
    16. // 移除订阅者
    17. virtual void UnbindNotify(UObserver* Observer) {
    18. if (ObserverList.Contains(Observer)) {
    19. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s 已取消订阅"), *Observer->GetName());
    20. ObserverList.Remove(Observer);
    21. }
    22. }
    23. // 通知
    24. virtual void Notify() {
    25. for (auto It = ObserverList.CreateConstIterator(); It; It++)
    26. {
    27. if (IsValid(*It))
    28. {
    29. (*It)->Update();
    30. }
    31. }
    32. }
    33. protected:
    34. TArray ObserverList;
    35. };

    创建目标具体类

    1. // 具体目标类 —— 辐射地带、辐射监测
    2. UCLASS(Blueprintable, BlueprintType)
    3. class DESIGNPATTERNS_API UNuclearRadiation : public USubject
    4. {
    5. GENERATED_BODY()
    6. public:
    7. // 更新辐射程度
    8. void UpdateRadiationDegree(int32 pLevel) {
    9. m_pLevel = pLevel;
    10. Notify();
    11. }
    12. // 获取辐射程度
    13. int32 GetRadiationDegree() {
    14. return m_pLevel;
    15. }
    16. private:
    17. // 辐射程度 1-9级
    18. int32 m_pLevel;
    19. };

    测试

    1. // 调用测试用的Actor
    2. UCLASS()
    3. class DESIGNPATTERNS_API AObserverActor : public AActor
    4. {
    5. GENERATED_BODY()
    6. public:
    7. void BeginPlay() override {
    8. // 创建核辐射监测
    9. UNuclearRadiation* NuclearRadiation = NewObject();
    10. // 创建救援人员、监测核辐射程度
    11. UWorker* Worker0 = NewObject();
    12. Worker0->SetNuclearRadiation(NuclearRadiation);
    13. NuclearRadiation->BindNotify(Worker0);
    14. UWorker* Worker1 = NewObject();
    15. Worker1->SetNuclearRadiation(NuclearRadiation);
    16. NuclearRadiation->BindNotify(Worker1);
    17. UWorker* Worker2 = NewObject();
    18. Worker2->SetNuclearRadiation(NuclearRadiation);
    19. NuclearRadiation->BindNotify(Worker2);
    20. // 核辐射升级
    21. NuclearRadiation->UpdateRadiationDegree(1);
    22. NuclearRadiation->UpdateRadiationDegree(2);
    23. NuclearRadiation->UpdateRadiationDegree(3);
    24. // 有人先出来了,取消核辐射继续影响
    25. NuclearRadiation->UnbindNotify(Worker2);
    26. // 核辐射继续升级
    27. NuclearRadiation->UpdateRadiationDegree(5);
    28. NuclearRadiation->UpdateRadiationDegree(7);
    29. NuclearRadiation->UpdateRadiationDegree(9);
    30. GEngine->ForceGarbageCollection(true);
    31. }
    32. };

    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已无生命体征
     

  • 相关阅读:
    Linux 进阶 - 1
    ARM 堆栈寻址类型区分
    限制IP到全流程防控,讲解网络爬虫与技术反爬的动态攻防
    如何使用 edu 邮箱注册 Azure
    Linux信号
    【教程】部署apprtc服务中安装google-cloud-cli组件的问题及解决
    Java中HashMap类简介说明
    图文翻译-免费图文翻译-批量图文翻译软件
    洛谷 P3275 [SCOI2011]糖果(差分约束,tarjan强连通分量,scc)
    python-while循环
  • 原文地址:https://blog.csdn.net/Jason6620/article/details/126543454