我有一个Order类,它经过一系列定义的状态。为此,我实现了State模式,以便Order对象具有一个CurrentState成员,该成员实现IOrderState接口。然后,我将有此接口的具体实现,例如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?
考虑通过暗示而不是赋值来更改状态。
[在我见过的几乎所有情况下,都可以从其他属性(最好是在Class中)推断出State。如果是这样,请不要保留状态,而应在需要时派生它。否则,您经常会在推断值和分配值之间出现问题。 (根据我的经验,“派生”总是正确的。)
(复杂度通常是查看该类的事务日志,并考虑最近发生的事件。但是还是值得的。)
SetState()方法在扩展具有更多状态的订单以及检测更改方面将具有优势-但我不建议这样做。 State pattern与收集特定于单独类中不同状态的行为有关,而不与如何向其他类提供有状态接口有关。
对于订单,请考虑自然而然的业务事件(例如,确认,确认,装运通知,装运,发票等),并围绕它们设计一个明确的界面。接口的确切设计方式取决于应用程序逻辑的结构以及在其他层中的使用方式。经典的答案是为每个业务事件定义抽象方法(例如,Confirm(),Acknowledge(),ShipDateChanged())。如果您使用的是C#,您可能决定使用Order对象中的传入和传出事件。或者您可以尝试一些混合或组合。重点是SetOrderState()接口的描述性不是很好,并且可能导致笨拙的实现(每个OrderState类中的大型方法)。
然后再次,如果您没有很多关于不同状态更改的代码,则可以使用类内部的SetState()方法(从每个特定方法或事件中调用):作为外部接口。缺点是内部IOrderState接口的方法与外部公开的Order接口的方法可能会有些重叠。
这是一个判断电话,但是如果我是您,我会本着您的直觉不向客户公开您实施State的细节。使用您的Order类的代码应该是可读和可理解的。
例如,您可以减少将方法打包为私有方法的可见性。但是在您的情况下,我认为这是唯一的方法,另一种方法是拥有一个实现状态机的父抽象类,并且只有一组nextState(inputParameter)方法,这些方法会将订单的状态转换为相应的状态。
我认为对此没有“正确”的答案;它实际上取决于您为状态机选择的体系结构。如果所有用于更改状态的逻辑都封装在您的Order类中,那么我会说公开SetState方法是不好的做法。但是,由于您已经将某些状态确定逻辑放置在Order类之外,因此公开(公共)SetState方法或类似方法似乎是适当的(并且是必要的)。
当然,您的另一种选择是将状态确定逻辑移到Order类中,尽管根据您发布的内容,似乎好像会创建一个非常复杂的类。
总之,模式确实可以帮助您构建代码,而无需设置严格的规则。您应该以最有效的方式将模式应用于架构,而不是模式的架构师。
我认为,状态之间的转换应该在类中。