4

我有一个 Order 类,它经历了一系列定义的状态。为了解决这个问题,我实现了 State 模式,使得 Order 对象有一个 CurrentState 成员,该成员实现了 IOrderState 接口。然后我有这个接口的具体实现,例如 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?

4

5 回答 5

3

考虑通过暗示而不是赋值来改变状态。

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

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

于 2009-04-14T15:26:26.173 回答
2

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

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

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

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

于 2009-04-14T15:03:41.190 回答
1

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

于 2009-04-14T15:00:07.143 回答
1

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

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

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

于 2009-04-14T15:00:58.560 回答
1

我认为,状态之间的转换应该在课堂上。

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

 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.
    }
 }

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

最好的祝福!

于 2009-04-14T15:16:42.220 回答