把书读薄 | 《设计模式之美》设计模式与范式(行为型-状态模式)(下)

简介: 本文对应设计模式与范式:行为型(64),状态模式 (State Pattern),描述了对象 状态变化 及如何在每种状态下表现出不同的 行为~

代码运行结果如下


网络异常,图片无法展示
|


通过状态模式,我们把事件触发的 状态转移和动作执行,拆分到不同的状态类中,避免了分支判断结构


顺带带出UML类图、组成角色、使用场景及优缺点~


网络异常,图片无法展示
|


  • Context (上下文信息类) → 存储当前状态类,并负责具体状态的切换;


  • State (抽象状态类) → 定义声明状态更新的操作方法,可以是接口或抽象类;


  • ConcreteState (具体状态类) → 实现抽象状态类中定义的方法,根据具体场景指定对应状态改变后的代码逻辑;


使用场景:


  • 某个操作含有庞大的分支判断结构,且分支决定于对象的状态时;


  • 对象行为取决于状态,且必须在运行时根据状态改变其行为时;


优点:


  • 符合单一职责原则:将与特定状态相关的代码组织到单独的类中;


  • 更好的扩展性:扩展新的状态只需增加实现类,在需要维护的地方设置下新状态即可;


  • 提前定好可能的状态,降低代码实现复杂度,避免写大量的if-else条件语句;


缺点:


  • 类增加,每个状态对应一个具体状态类;


  • 不满足开闭原则,状态模式虽然降低了状态之间的耦合,但是新增或修改状态都会涉及前/后一个状态的修改;


  • 逻辑零散,无法在一个地方就看出整个状态机的转换逻辑;


0x3、补充:有限状态机的概念


英文翻译 Finite State Machine,缩写FSM,简称状态机,它有三个组成部分:状态(State)事件(Event)动作(Action)。其中的事件又称为 转移条件,事件触发状态的转移和动作的执行(非必须)。


也可以理解为一种数学模型,该模型中有几个状态(有限的),在不同场景下,不同的状态间发生转移,在状态转移过程中可能伴随着不同的事件发生。


状态机有三种常见的实现方式:


  • 分支逻辑法 → 缺点是改变业务逻辑,改起来容易出错,代码也不易看懂。适合简单状态机;


  • 查表法 → 适用于状态很多、状态转移比较复杂的状态机,用二维数组表示状态转移图,可极大提高代码的可读性与可维护性;


  • 状态模式 → 适用于状态并不多、状态转移较简单,事件触发动作包含的业务逻辑可能较复杂的状态机。


0x4、加餐:Android源码中是如何使用16进制进行状态管理的?


在Android系统源码中涉及到 多状态 管理总是通过十六进制数字来表示,如ViewGroup中:


static final int FLAG_CLIP_CHILDREN = 0x1;
private static final int FLAG_CLIP_TO_PADDING = 0x2;
static final int FLAG_INVALIDATE_REQUIRED  = 0x4;
private static final int FLAG_RUN_ANIMATION = 0x8;
static final int FLAG_ANIMATION_DONE = 0x10;
private static final int FLAG_PADDING_NOT_NULL = 0x20;
private static final int FLAG_ANIMATION_CACHE = 0x40;
static final int FLAG_OPTIMIZE_INVALIDATE = 0x80;
static final int FLAG_CLEAR_TRANSFORMATION = 0x100;
private static final int FLAG_NOTIFY_ANIMATION_LISTENER = 0x200;


这是为什么呢?先复习下几种二进制运算:


  • 按位与(&)对应位都为1才为1,否则为0,如0x1 & 0x2 → 0001 & 0010 → 0000;


  • 按位或(|)对应位有一个为1即为1,如0x1 | 0x2 → 0001 | 0010 → 0011


  • 取反(~)按位取反,如~0x1 → 0001 → 1110


接着以上面手机状态为例,写个状态管理的例子:


private static int state = 0;
private final static int CLOSE = 0x1; // 关机状态
private final static int FIRST_BOOT = 0x2;   // 首次启动
private final static int NOT_FIRST_BOOT = 0x4;    // 非首次启动
private final static int AFTER_BOOT = 0x8;    // 启动后


状态增加 → 或运算


state | CLOSE → (0000 | 0001) → 0001 → 此时状态:CLOSE
state | FIRST_BOOT → (0001 | 0010) → 0011 → 此时状态:CLOSE + FIRST_BOOT
state | NOT_FIRST_BOOT → (0011 | 0100) → 0111 → 此时状态:CLOSE + FIRST_BOOT + NOT_FIRST_BOOT


状态移除 → 对应的位数从1改为0,先取反,再与运算


state &= ~NOT_FIRST_BOOT → (0111 & 1011) → 0011 → 此时状态:CLOSE + FIRST_BOOT
state &= ~CLOSE → (0011 & 1110) → 0010 → 此时状态:FIRST_BOOT


状态判断 → 与运算判断结果是否为0


// 假设此时状态为:CLOSE + FIRST_BOOT + NOT_FIRST_BOOT
state & FIRST_BOOT → 0111 & 0010 = 0010 → 0010 → 结果不为0,包含此状态;
// 假设此时状态为:CLOSE + FIRST_BOOT
state & NOT_FIRST_BOOT → 0011 & 0100 → 结果为0,不包含此状态;


疑惑:用来标识状态的十六进制并不是连续的,如跳过了0x3:


如果把上面的NOT_FIRST_BOOT从0x4改为0x3,而CLOSE + FIRST_BOOT 结果为0011,同为0x3,此时进行状态判断结果不为0,难道说增加了NOT_FIRST_BOOT状态吗?所以这里的取值是有固定规则的,即 左移一位


private final static int CLOSE = 1 << 0; 
private final static int FIRST_BOOT = 1 << 1;   
private final static int NOT_FIRST_BOOT = 1 << 2;    
private final static int AFTER_BOOT = 1 << 3;


选择十六进制的原因而不用其他进制的原因(如十进制):


计算机中,一个字节有八位,最大值为1111111,对应十进制255,十六进制FF,半个字节用十六进制 通过一个字母 就能表示,而转换成十进制则是一个无规律的数字,相比起十进制,十六进制转二进制 更直观一些。


参考文献:



相关文章
|
3月前
|
设计模式 Java 测试技术
Java设计模式-状态模式(18)
Java设计模式-状态模式(18)
|
4月前
|
设计模式 网络协议 Java
【十五】设计模式~~~行为型模式~~~状态模式(Java)
文章详细介绍了状态模式(State Pattern),这是一种对象行为型模式,用于处理对象在其内部状态改变时的行为变化。文中通过案例分析,如银行账户状态管理和屏幕放大镜工具,展示了状态模式的应用场景和设计方法。文章阐述了状态模式的动机、定义、结构、优点、缺点以及适用情况,并提供了Java代码实现和测试结果。状态模式通过将对象的状态和行为封装在独立的状态类中,提高了系统的可扩展性和可维护性。
【十五】设计模式~~~行为型模式~~~状态模式(Java)
|
5月前
|
设计模式 JavaScript Go
js设计模式【详解】—— 状态模式
js设计模式【详解】—— 状态模式
91 7
|
6月前
|
设计模式 算法
行为型设计模式之模板模式
行为型设计模式之模板模式
|
6月前
|
设计模式
状态模式-大话设计模式
状态模式-大话设计模式
|
6月前
|
设计模式 存储
行为设计模式之状态模式
行为设计模式之状态模式
|
6月前
|
设计模式 存储
行为型设计模式之观察者模式
行为型设计模式之观察者模式
|
6月前
|
设计模式 算法
行为型设计模式
行为型设计模式
|
7月前
|
设计模式 存储 SQL
第四篇 行为型设计模式 - 灵活定义对象间交互
第四篇 行为型设计模式 - 灵活定义对象间交互
142 0
|
7月前
|
设计模式 算法 Java
[设计模式Java实现附plantuml源码~行为型]定义算法的框架——模板方法模式
[设计模式Java实现附plantuml源码~行为型]定义算法的框架——模板方法模式