概述

描述

  • 允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。

  • 其别名为状态对象(Objects for States),状态模式是一种对象行为型模式。

  • 有限状态机(FSMs)

    • 拥有状态机所有可能状态的集合
    • 状态机同时只能在一个状态
    • 一连串的输入或事件被发送给状态机
    • 每个状态都有一系列的转移,每个转移与输入和另一状态相关
    • 动画状态机系统、行为树系统来理解
  • 并发状态机

    • 有些状态需要并行执行,例如动画状态机,经常分为上半身动画与下半身动画融合,如装备动作、射击动作、换弹动作与行走动作并行
  • 层次状态机

    • 状态中嵌套子状态,可以使用继承实现或者状态栈来实现
  • 下推自动机

    • 用栈来存储一系列状态。有限状态机有一个指向状态的指针,下推自动机有一栈指针
    • 新状态压入栈中,“当前的”状态总是在栈顶
    • 弹出最上面的状态,这个状态会被销毁,它下面的状态成为新状态。如UI界面管理

套路

  • 环境类 Context,用于改变状态
  • 抽象状态类 State
  • 具体状态类 ConcreteState

使用场景

  • 对象的行为依赖于它的状态(属性)并且可以根据它的状态改变而改变它的相关行为。
  • 代码中包含大量与对象状态有关的条件语句,这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,使客户类与类库之间的耦合增强。在这些条件语句中包含了对象的行为,而且这些条件对应于对象的各种状态。
  • 示例
    • OA办公系统中多种状态:尚未办理;正在办理;正在批示;正在审核
    • TCP 连接状态
    • 动画系统、AI行为树
    • UI界面管理

优缺点

  • 优点

    • 封装了转换规则。
    • 枚举可能的状态,在枚举状态之前需要确定状态种类。
    • 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
    • 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
    • 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
  • 缺点
    • 状态模式的使用必然会增加系统类和对象的个数。
    • 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
    • 状态模式对“开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。

UE4 实践

  • 在UE4中,实现切换UI状态

  • 创建状态抽象类,以接口的形式

    UINTERFACE(MinimalAPI)
    class UStateInterface : public UInterface
    {
    GENERATED_BODY()
    }; /**
    *
    */
    class DESIGNPATTERNS_API IStateInterface
    {
    GENERATED_BODY() // Add interface functions to this class. This is the class that will be inherited to implement this interface.
    public:
    virtual void EnterState() = 0;
    virtual void ExitState() = 0;
    };
  • 创建状态具体类UBaseStateWidget ,作为UI的父类,切换时使用

    UCLASS()
    class DESIGNPATTERNS_API UBaseStateWidget : public UUserWidget, public IStateInterface
    {
    GENERATED_BODY()
    public:
    virtual void EnterState() override;
    virtual void ExitState() override; UFUNCTION(BlueprintNativeEvent,BlueprintCallable,Category="State Pattern")
    void OnEnterState();
    UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "State Pattern")
    void OnExitState();
    };
    #include "BaseStateWidget.h"
    
    void UBaseStateWidget::EnterState()
    {
    OnEnterState();
    } void UBaseStateWidget::ExitState()
    {
    OnExitState();
    } void UBaseStateWidget::OnEnterState_Implementation()
    {
    AddToViewport();
    } void UBaseStateWidget::OnExitState_Implementation()
    {
    RemoveFromParent();
    }
  • 创建状态管理类

    UCLASS()
    class DESIGNPATTERNS_API AUIStateManager : public AActor
    {
    GENERATED_BODY() public:
    // Sets default values for this actor's properties
    AUIStateManager(); protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override; public:
    // Called every frame
    virtual void Tick(float DeltaTime) override; // 改变状态
    UFUNCTION(BlueprintCallable, Category = "State Pattern")
    void EnterState(TSubclassOf<UBaseStateWidget> StateWidgetClass); // 退出所有状态
    UFUNCTION(BlueprintCallable, Category = "State Pattern")
    void ExitAllState(); // 当前状态实例
    UPROPERTY(BlueprintReadWrite, Category = "State Pattern")
    UBaseStateWidget* CurrentStateWidget; private:
    // 存储状态实例
    TMap<TSubclassOf<UBaseStateWidget>, UBaseStateWidget*> WidgetInstances;
    };
    void AUIStateManager::EnterState(TSubclassOf<UBaseStateWidget> StateWidgetClass)
    {
    if (CurrentStateWidget != nullptr)
    {
    CurrentStateWidget->ExitState();
    } if (WidgetInstances.Contains(StateWidgetClass))
    {
    CurrentStateWidget = WidgetInstances.FindRef(StateWidgetClass);
    }
    else
    {
    APlayerController* PC = UGameplayStatics::GetPlayerController(GetWorld(), 0);
    CurrentStateWidget = CreateWidget<UBaseStateWidget>(PC, StateWidgetClass);
    WidgetInstances.Add(StateWidgetClass,CurrentStateWidget);
    }
    CurrentStateWidget->EnterState();
    } void AUIStateManager::ExitAllState()
    {
    for (auto& Elem : WidgetInstances)
    {
    (Elem.Value)->ExitState();
    }
    }
  • 创建状态类UBaseStateWidget 的蓝图派生类

    • WBP_Start —— 开始界面
    • WBP_LoadingScreen —— 加载界面
    • WBP_Save —— 存档界面
    • WBP_Option —— 选项界面



  • 创建 UIStateManager 蓝图派生类

  • 效果

  • 扩展,可进一步用栈状态机实现界面管理


参考

【UE4 设计模式】状态模式 State Pattern的更多相关文章

  1. C#设计模式——状态模式(State Pattern)

    一.概述在面向对象软件设计时,常常碰到某一个对象由于状态的不同而有不同的行为.如果用if else或是switch case等方法处理,对象操作及对象的状态就耦合在一起,碰到复杂的情况就会造成代码结构 ...

  2. 乐在其中设计模式(C#) - 状态模式(State Pattern)

    原文:乐在其中设计模式(C#) - 状态模式(State Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 状态模式(State Pattern) 作者:webabcd 介绍 允 ...

  3. 二十四种设计模式:状态模式(State Pattern)

    状态模式(State Pattern) 介绍允许一个对象在其内部状态改变时改变它的行为.对象看起来似乎修改了它所属的类. 示例有一个Message实体类,对它的操作有Insert()和Get()方法, ...

  4. 状态模式-State Pattern(Java实现)

    状态模式-State Pattern 在状态模式(State Pattern)中,类的行为是基于它的状态改变的.当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类. State接口 ...

  5. 设计模式(十二):通过ATM取款机来认识“状态模式”(State Pattern)

    说到状态模式,如果你看过之前发布的重构系列的文章中的<代码重构(六):代码重构完整案例>这篇博客的话,那么你应该对“状态模式”并不陌生,因为我们之前使用到了状态模式进行重构.上一篇博客我们 ...

  6. [设计模式] 20 状态模式 State Pattern

    在GOF的<设计模式:可复用面向对象软件的基础>一书中对状态模式是这样说的:允许一个对象在其内部状态改变时改变它的行为.对象看起来似乎修改了它的类.状态模式的重点在于状态转换,很多时候,对 ...

  7. 十一个行为模式之状态模式(State Pattern)

    定义: 当一个对象有多个状态,并且在每个状态下有不同的行为,可以使用状态模式来在其内部改变状态时改变其行为,而客户端不会察觉状态的改变,仍使用同样的方法或接口与对象进行交互. 结构图: Context ...

  8. 状态模式(State Pattern)

    当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类. 状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂时的情况.把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂 ...

  9. 大话设计模式--状态模式 State -- C++实现实例

    1.状态模式: 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来就像是改变了其类. 状态模式解决的是当控制一个对象状态转换的条件表达式过于复杂, 把状态的判断逻辑转移到表示不同状态的一系列类 ...

随机推荐

  1. 常用ADB命令汇总

    网络连接 通过TCP/IP连接设备 adb connect <ip:port> 断开已有的TCP/IP连接 adb disconnect <ip:port> 监听设备上指定的端 ...

  2. 第20篇-加载与存储指令之ldc与_fast_aldc指令(2)

    ldc指令将int.float.或者一个类.方法类型或方法句柄的符号引用.还可能是String型常量值从常量池中推送至栈顶. 这一篇介绍一个虚拟机规范中定义的一个字节码指令ldc,另外还有一个虚拟机内 ...

  3. TCP头部格式和封装

    文章目录 12.3 TCP头部和封装 12.3.1 端口号 12.3.2 序列号 12.3.3 头部长度 12.3.4 相关控制位 12.3.5 窗口大小 12.3.6 校验和 12.3.7 选项字段 ...

  4. 【MyBatis】几种批量插入效率的比较

    批处理数据主要有三种方式: 反复执行单条插入语句 foreach 拼接 sql 批处理 一.前期准备 基于Spring Boot + Mysql,同时为了省略get/set,使用了lombok,详见p ...

  5. Fastjson 1.2.22-24 反序列化漏洞分析(1)

    Fastjson 1.2.22-24 反序列化漏洞分析(1) 前言 FastJson是alibaba的一款开源JSON解析库,可用于将Java对象转换为其JSON表示形式,也可以用于将JSON字符串转 ...

  6. xshell与小键盘问题

    有些程序员的键盘是带有小数字键的,在使用xshell中文版时就可能出现一些小状况,本集就同大家分析一下使用数字键盘出现乱码的情况怎么办. 图1:使用数字小键盘出现乱码 问题描述: 在xshell上用v ...

  7. 机器学*——K*邻算法(KNN)

    1 前言 Kjin邻法(k-nearest neighbors,KNN)是一种基本的机器学*方法,采用类似"物以类聚,人以群分"的思想.比如,判断一个人的人品,只需观察他来往最密切 ...

  8. 开源物联网平台(Thingsboard)-编译

    环境准备 Jdk8+ (3.2.2版本开始使用Jdk11) Maven3.2.1+ release-3.2分支 获取代码 ##get source from mirror git clone http ...

  9. Docker系列(26)- 发布镜像到阿里云容器服务

    1.登录阿里云 2.找到容器镜像服务 3.创建命名空间 4.创建镜像仓库 5.上传镜像

  10. 华为云计算IE面试笔记-桌面云中的用户组、虚拟机模板、模板虚拟机、虚拟机组和桌面组的关系及区别。发放完整复制和链接克隆虚拟机时,步骤有什么区别,要怎么选择桌面组?

    概念解释: 模板虚拟机:FC上创建的裸虚拟机,用于制作不同类型的虚拟机模板. 虚拟机模板:用于创建虚拟机的模板,对裸虚拟机(模板虚拟机)进行配置或自定义安装软件后,转为模板.虚拟机模板类型有完整复制, ...