繁体   English   中英

使用状态模式的对象应该如何转换到下一个状态?

[英]How should an object using the state pattern be transitioned to the next state?

我有一个 Order 类,它经历了一系列定义的状态。 为了帮助解决这个问题,我实现了状态模式,使得 Order 对象有一个实现 IOrderState 接口的 CurrentState 成员。 然后我有这个接口的具体实现,例如 OrderStateNew、OrderStateDelivered 等

我的问题是,在状态之间转换 Order 对象的正确方法是什么? 有一个允许外部服务设置状态的 Order.SetState() 方法是否可以接受? 确定状态变化的标准存储在 Order 对象的外部,所以这似乎是显而易见的答案,但我有点不安在我的对象上有一个公共方法来改变像这样基本的东西。

额外的说明我认为添加一些关于我的实现的更多细节可能会很有用,因为我想知道我是否真的首先正确地使用了该模式。 这是用于创建和授权订单的公共 API

Dim orderFacade As New OrderFacade
Dim order = orderFacade.createFrom(customer)

' Add lines etc

' This will validate the order and transition it to status 'Authorised'
Dim valid = orderFacade.Authorise(order)

' This will commit the order, but only if it is at status 'Authorised'
Dim result = orderFacade.Commit()

OrderFacade.Authorise() 函数看起来像这样

Public Function Authorise(ByRef originalOrder As Order) As ValidationSummary

    If originalOrder.CurrentState.CanAuthorise() Then

       Dim validator = OrderValidatorFactory.createFrom(originalOrder)
       Dim valid = validator.ValidateOrder(originalOrder)

      If valid.IsValid Then
          originalOrder.SetOrderStatus(OrderStatus.Authorised)
      End If

     Return valid

    End If

 End Function

如您所见,CurrentState 成员是当前 IOrderState 实现,它确定哪些活动对对象有效。 我想知道这是否应该负责确定过渡而不是 OrderFacade?

考虑通过隐含而不是赋值来改变状态。

在我见过的几乎所有情况下,都可以从其他属性(希望在类中)推断出状态。 如果是这样,不要持久化状态,而是在需要时派生它。 否则,您通常会在推断值和分配值之间出现有问题的分歧。 (根据我的经验,“派生”总是正确的。)

(复杂性通常是查看班级的事务日志,并考虑最近的事件。但无论如何这都是值得的。)

SetState() 方法在扩展具有更多状态的订单以及检测更改方面具有优势——但我不推荐它。 状态模式是关于在不同的类中收集特定于不同状态的行为,而不是关于如何向其他类呈现有状态的接口。

对于订单,考虑自然发生的业务事件(例如确认、确认、发货通知、发货、发票等)并围绕它们设计一个显式接口。 设计界面的具体方式取决于应用程序逻辑的结构方式以及其他层如何使用它。 经典的答案是为每个业务事件定义抽象方法(例如 Confirm()、Acknowledge()、ShipDateChanged())。 如果您正在使用例如 C#,您可能决定使用来自 Order 对象的传入和传出事件。 或者你可以尝试一些混合或组合。 主要的一点是 SetOrderState() 接口不是很具有描述性,并且可能导致笨拙的实现(每个 OrderState 类中的大型方法)。

再说一次,如果您没有围绕不同状态更改的大量代码,则类内部的 SetState() 方法(从您的每个特定方法或事件调用)可能没问题:但我不会将其公开为一个外部接口。 缺点是您的内部 IOrderState 接口和外部公开的 Order 接口的方法之间可能会有一些重叠。

这是一个判断电话,但如果我是你,我会按照你的直觉不向客户公开你的 State 实现的细节。 使用您的 Order 类的代码应该是可读和可理解的。

例如,您可以降低打包私有方法的可见性。 但在你的情况下,我认为这是唯一的方法,另一种方法是拥有一个实现状态机的父抽象类,并且只有一组 nextState(inputParameter) 方法可以将订单的状态转移到相应的状态。

我不认为有一个“正确”的答案。 它实际上取决于您为状态机选择的架构。 如果更改状态的所有逻辑都封装在您的 Order 类中,那么我会说公开 SetState 方法将是一种糟糕的做法。 但是,由于您已经在 Order 类之外放置了一些状态确定逻辑,因此公开一个公共 SetState 方法或类似方法似乎是合适的(并且是必要的)。

当然,您的另一个选择是将状态确定逻辑移到 Order 类中,尽管根据您发布的内容,这似乎会创建一个非常复杂的类。

无论如何,简而言之,模式实际上只是帮助您构建代码,而不是设置硬性规则。 您应该以最有效的方式将模式应用于您的架构,而不是架构到模式。

我认为,状态之间的转换应该在类中。

例如,当您在订单中执行某种操作时,订单知道如何移动到其他状态。 例如:

 public void setPaid(int amount)
 {
    //Code to pay.
    this.State = new PaidState();   //State implements the IState Interface
 }

其他替代方法可能是为实现此类方法的 Example Transformer 创建一个新类。

 public class Transformer
 {
    public static void setState(Order o, IState s)
    {
         //Change the State.
    }
 }

或者 yoy 可以使用 Enum 在那里设置状态:

 public class Transformer
 {
    public static void setState(Order o, StatesEnum s)
    {
         //Change the State.
    }
 }

但我建议该类操作自己的状态。 但请记住,另一方面,您将拥有复杂性。

最好的祝福!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM