侧边栏壁纸
博主头像
极客日记 博主等级

行动起来,活在当下

  • 累计撰写 93 篇文章
  • 累计创建 17 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

设计模式 - 状态模式

Jack.Jia
2024-01-07 / 0 评论 / 0 点赞 / 11 阅读 / 0 字

状态模式

引言

设计模式在面向对象编程中是一种常见且实用的策略,它们提供了解决软件设计中常见问题的经过验证的解决方案。状态模式是其中的一种行为设计模式,它允许一个对象在其内部状态改变时改变其行为。

状态模式在实际开发中非常有用,特别是当一个对象的行为取决于其状态,并且必须能够在运行时根据当前状态改变其行为。这种模式将与特定状态相关的行为封装在单独的类中,这样可以保持主对象代码的简洁,并提高代码的可维护性和灵活性。

状态模式结构

状态模式.png

举例说明

账号登录

假设我们正在开发一个网站,用户需要登录才能访问某些页面。用户账号可能有多种状态,如未登录、已登录和已锁定。每种状态下,账号的行为都有所不同。

首先,我们定义一个 AccountState 抽象类,声明所有具体状态类需要实现的方法:

class AccountState
  def handle(context)
    raise 'You must implement handle method'
  end
end

然后,我们为每种具体的账号状态创建一个类:

class NotLoggedInState < AccountState
  def handle(context)
    # 处理未登录状态下的行为...
    puts 'You are currently not logged in.'
  end
end

class LoggedInState < AccountState
  def handle(context)
    # 处理已登录状态下的行为...
    puts 'You are already logged in.'
  end
end

class LockedState < AccountState
  def handle(context)
    # 处理已锁定状态下的行为...
    puts 'Your account is locked.'
  end
end

接着,我们创建一个 LoginContext 类,它持有一个 AccountState 对象,这个对象表示当前账号的状态。当账号的状态改变时,LoginContext 会更新其持有的 AccountState 对象:

class LoginContext
  attr_accessor :state

  def initialize(state)
    @state = state
  end

  def request_handle
    @state.handle(self)
  end

  def change_state_to(new_state)
    @state = new_state
  end

end

现在,无论账号处于哪种状态,我们都可以通过调用 LoginContext#request_handle 方法来处理请求。当账号的状态改变时,只需要更新 LoginContext 持有的 AccountState 对象即可。

例如,在用户尝试登录时,如果他们提供了有效的凭据,则可以将状态更改为“已登录”:

context = LoginContext.new(NotLoggedInState.new)

# 用户尝试登录
if user_provided_valid_credentials
  context.change_state_to(LoggedInState.new)
end

context.request_handle

这个例子展示了如何使用状态模式来管理账号的状态并根据当前状态改变行为。当账号的状态从未登录变为已登录时,我们只需要更改 LoginContext 持有的 AccountState 对象即可,而无需修改其他代码。这使得代码更易于理解和维护,也更容易添加新的状态和行为。

电梯系统

假设我们正在开发一个电梯控制系统。电梯可能有多种状态,如停止、上升和下降。每种状态下,电梯的行为都有所不同。

首先,我们定义一个 ElevatorState 抽象类,声明所有具体状态类需要实现的方法:

class ElevatorState
  def handle(context)
    raise 'You must implement handle method'
  end
end

然后,我们为每种具体的电梯状态创建一个类:

class StoppedState < ElevatorState
  def handle(context)
    # 处理停止状态下的行为...
    puts 'The elevator is stopped.'
  end
end

class RisingState < ElevatorState
  def handle(context)
    # 处理上升状态下的行为...
    puts 'The elevator is rising.'
  end
end

class FallingState < ElevatorState
  def handle(context)
    # 处理下降状态下的行为...
    puts 'The elevator is falling.'
  end
end

接着,我们创建一个 ElevatorContext 类,它持有一个 ElevatorState 对象,这个对象表示当前电梯的状态。当电梯的状态改变时,ElevatorContext 会更新其持有的 ElevatorState 对象:

class ElevatorContext
  attr_accessor :state

  def initialize(state)
    @state = state
  end

  def request_handle
    @state.handle(self)
  end

  def change_state_to(new_state)
    @state = new_state
  end
end

现在,无论电梯处于哪种状态,我们都可以通过调用 ElevatorContext#request_handle 方法来处理请求。当电梯的状态改变时,只需要更新 ElevatorContext 持有的 ElevatorState 对象即可。

例如,当电梯从停止状态开始上升时:

context = ElevatorContext.new(StoppedState.new)

# 电梯开始上升
context.change_state_to(RisingState.new)

context.request_handle

这个例子展示了如何使用状态模式来管理电梯的状态并根据当前状态改变行为。当电梯的状态从停止变为上升时,我们只需要更改 ElevatorContext 持有的 ElevatorState 对象即可,而无需修改其他代码。

文档审批

假设我们正在开发一个文档审批系统。文档可能有多种状态,如草稿、待审核和已发布。每种状态下,文档的行为都有所不同。

首先,我们定义一个 DocumentState 抽象类,声明所有具体状态类需要实现的方法:

class DocumentState
  def handle(context)
    raise 'You must implement handle method'
  end
end

然后,我们为每种具体的文档状态创建一个类:

class DraftState < DocumentState
  def handle(context)
    # 处理草稿状态下的行为...
    puts 'The document is in draft state.'
  end
end

class UnderReviewState < DocumentState
  def handle(context)
    # 处理待审核状态下的行为...
    puts 'The document is under review.'
  end
end

class PublishedState < DocumentState
  def handle(context)
    # 处理已发布状态下的行为...
    puts 'The document is published.'
  end
end

接着,我们创建一个 DocumentContext 类,它持有一个 DocumentState 对象,这个对象表示当前文档的状态。当文档的状态改变时,DocumentContext 会更新其持有的 DocumentState 对象:

class DocumentContext
  attr_accessor :state

  def initialize(state)
    @state = state
  end

  def request_handle
    @state.handle(self)
  end

  def change_state_to(new_state)
    @state = new_state
  end

end

现在,无论文档处于哪种状态,我们都可以通过调用 DocumentContext#request_handle 方法来处理请求。当文档的状态改变时,只需要更新 DocumentContext 持有的 DocumentState 对象即可。

例如,在文档从草稿状态提交审批后:

context = DocumentContext.new(DraftState.new)

# 文档提交审批
context.change_state_to(UnderReviewState.new)

context.request_handle

这个例子展示了如何使用状态模式来管理文档审批流程中文档的状态,并根据当前状态改变行为。当文档的状态从草稿变为待审核时,我们只需要更改 DocumentContext 持有的 DocumentState 对象即可,而无需修改其他代码。

状态模式适合应用场景

  • 如果对象需要根据自身当前状态进行不同行为, 同时状态的数量非常多且与状态相关的代码会频繁变更的话, 可使用状态模式。

    模式建议你将所有特定于状态的代码抽取到一组独立的类中。 这样一来, 你可以在独立于其他状态的情况下添加新状态或修改已有状态, 从而减少维护成本。

  • 如果某个类需要根据成员变量的当前值改变自身行为, 从而需要使用大量的条件语句时, 可使用该模式。

    状态模式会将这些条件语句的分支抽取到相应状态类的方法中。 同时, 你还可以清除主要类中与特定状态相关的临时成员变量和帮手方法代码。

  • 当相似状态和基于条件的状态机转换中存在许多重复代码时, 可使用状态模式。

    状态模式让你能够生成状态类层次结构, 通过将公用代码抽取到抽象基类中来减少重复。

状态模式优缺点

优点

  • 单一职责原则。 将与特定状态相关的代码放在单独的类中。
  • 开闭原则。 无需修改已有状态类和上下文就能引入新状态。
  • 通过消除臃肿的状态机条件语句简化上下文代码。

缺点

  • 如果状态机只有很少的几个状态, 或者很少发生改变, 那么应用该模式可能会显得小题大作。

与其他模式的关系

  • 桥接模式状态模式策略模式 (在某种程度上包括适配器模式) 模式的接口非常相似。 实际上, 它们都基于组合模式——即将工作委派给其他对象, 不过也各自解决了不同的问题。 模式并不只是以特定方式组织代码的配方, 你还可以使用它们来和其他开发者讨论模式所解决的问题。
  • 状态可被视为策略的扩展。 两者都基于组合机制: 它们都通过将部分工作委派给 “帮手” 对象来改变其在不同情景下的行为。 策略使得这些对象相互之间完全独立, 它们不知道其他对象的存在。 但状态模式没有限制具体状态之间的依赖, 且允许它们自行改变在不同情景下的状态。

总结

状态模式是一种能够管理对象状态转换和与状态相关的行为的设计模式。它将与特定状态相关的行为封装在单独的类中,这样可以保持主对象代码的简洁,并提高了代码的可维护性和灵活性。

0

评论区