如何将使用状态模式的对象转换为下一个状态?

时间:2009-04-14 14:46:58

标签: design-patterns state-machine

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

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

补充说明 我认为添加一些关于我的实现的更多细节可能是有用的,因为我想知道我是否真的首先正确地使用了模式。这是用于创建和授权订单的pulbic 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?

5 个答案:

答案 0 :(得分:3)

考虑通过暗示而不是分配来改变状态。

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

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

答案 1 :(得分:2)

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

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

然后,如果你没有很多关于不同状态变化的代码,你的类内部的SetState()方法(从你的每个特定方法或事件中调用)可能没问题:但我不会将其暴露为外部接口。缺点是您的内部IOrderState接口的方法和外部公开的Order接口之间可能会有一些重叠。

这是一个判断电话,但如果我是你,我会凭你的直觉不要向客户公开你的国家实施细节。使用Order类的代码应该是可读且易于理解的。

答案 2 :(得分:1)

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

答案 3 :(得分:1)

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

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

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

答案 4 :(得分:1)

我认为,国家之间的过渡应该在课堂上。

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

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

其他替代方法可能是创建一个新的Class for Example Transformer,它实现了这样的方法。

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

或者yoy可以使用枚举来设置那里的状态:

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

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

最诚挚的问候!