本文展示了如何在Swift中实现State模式,利用Swift的强大功能使实现更简单,更灵活。我们通过一个例子来探索状态模式的各个方面,并评估其处理需求变化的能力。然后,我们将了解关键实现注意事项以及模式的优点和实际应用。
有限状态自动机
真实世界域通常包含可以在任何时间点处于有限数量的可能状态之一的实体。根据实体所处的状态,它可能对同一个动作或事件的反应不同。
考虑一个典型的自动售货机。当没有插入钱时,按下应该销售特定项目的按钮无效。这是因为机器未处于执行该操作的正确状态。即使插入了钱,机器也可以出售一些物品而不是其他物品,这取决于插入了多少钱以及每件物品的成本。一旦给出了支付的物品,机器就会回到不收钱的状态下卖掉的东西。
可以使用有限状态自动机或有限状态机有效地对这些类型的实体进行建模。有限状态自动机或有限状态机,或简称为状态机,是一种抽象机器,可以处于有限数量的状态中的任何一个状态,并且可以响应于某些触发事件在这些状态之间转换。
可以使用条件语句(通常是嵌套的switch语句)在代码中实现简单状态机。对于非常简单的状态机实现,这可能是足够的,并且非常有效。但是,这种方法不能很好地扩展。在处理更复杂的状态机时,它可能导致代码繁琐,难以理解且难以维护。
状态模式是实现状态机的强大且可扩展的方式。特定于状态的行为由各种状态类型封装,被建模的实体将事件和操作委托给它们。管理国家之间转换的规则也可以由州自己管理。
状态模式
1. 定义
状态模式允许类型的实例通过其内部状态的更改来更改其行为。从外部角度来看,实例可能会改变其类型。
2. 结构与参与者
下面的类图说明了State模式的结构:Context类型定义客户端使用的接口。抽象状态类型反过来定义了一个由Context使用的接口,该接口封装了Context的特定于状态的行为。每个具体的State类型实现与Context的特定状态相关联的行为。因此,Context能够将特定于州的行为委托给具体的State类型。
模式的灵活性来自于Context仅依赖于抽象State类型而不依赖于任何具体State类型的事实。因此,当Context从一个状态转换到另一个状态时,可以交换具体状态类型的实例。
状态机图
状态机图或状态图是可视化状态机的好方法。每个状态由圆角矩形表示,连接矩形的箭头表示状态之间的转换。如果状态机具有默认初始状态,则使用小的实心圆来表示初始伪状态,其通常自动转换到第一适当状态。
让我们假设我们的任务是为安全系统构建控制单元,当存在入侵或入侵威胁时,它会发出警报。安全系统将连接到传感器,每个传感器都可以检测到漏洞,例如正在打开的门或窗户。当系统布防时,这种违规行为应该引起警报。该系统还将具有一个或多个应急开关,即使没有实际的突破也可用于发出警报,但可能存在潜在入侵者的突然威胁。当系统未布防时,违规或恐慌信号不应起作用。
我们还需要确保一旦武装起来,只能通过使用代码解除系统的武装。系统初始化时将设置此代码,并且必须用于撤防系统。同样的代码也必须用于重置系统,即一旦发出警报就停止警报。
如果我们将上述安全系统视为状态机,我们可以通过以下状态图直观地表示它:虽然该图非常直观,但是根据统一建模语言(UML)规范,让我们简要地看一下它的组成部分和一些管理状态图构造的形式。
1. 状态,事件和过渡
我们的系统可以处于以下三种状态中的任何一种:解除武装,武装和警报。 Disarmed状态是默认的初始状态。
状态之间的转换可能由以下事件引起:
- 手臂:仅适用于撤防状态。导致转换到武装状态。
- 解除武装:仅适用于武装状态。导致转换到撤防状态。
- 违反:表示传感器已被破坏。仅适用于武装状态。导致转换到警报状态。
- 恐慌:表示已激活紧急切换。仅适用于武装状态。导致转换到警报状态。
- 重置:仅适用于警报状态。导致转换到撤防状态。
2. 各个状态的进入退出活动
状态机中的任何状态都可以具有进入和退出活动。在输入状态之后执行输入活动,而在退出状态之前执行退出活动。为状态指定活动不是强制性的,但它是确保状态封装与其关联的行为的有用方法。在我们的示例中,每个州都有一个条目活动,而只有警报状态有一个退出活动。
3. 保证条件和过渡效果
转换的完整规范可以包含四个部分 - 事件,保护条件,效果和目标状态。事件是触发转换的事件,目标状态是在转换完成时变为活动状态。保护条件是一个布尔表达式,它被计算并且仅当表达式求值为true时才会触发转换。效果是在转换完成之前(即,在目标状态变为活动之前)执行的可选活动。
转换标签可以包括事件,保护条件和效果,格式为:'event [guard condition] / effect'。在我们的示例中,两个转换具有保护条件。这些确保只能使用有效代码撤防和重置系统。我们在示例中的任何转换中都没有使用效果。