这是状态模式的理想场景。
在状态模式中,您的状态类应该负责转换状态,而不仅仅是检查转换的有效性。此外,将状态转换推送到订单类之外不是一个好主意,并且违反了模式,但您仍然可以使用 OrderProcessor 类。
您应该让每个状态类实现 setQuantity 操作。状态类应该实现所有可能在某些状态下有效但在其他状态下无效的方法,无论它是否涉及状态更改。
不需要诸如 canChangeQuantity() 和 isValid() 之类的方法 - 状态类确保您的订单实例始终处于有效状态,因为如果您尝试,任何对当前状态无效的操作都会抛出。
您的 Order 类的属性与订单一起存储,而不是与状态一起存储。在 .Net 中,您可以通过将状态类嵌套在 Order 类中并在进行调用时提供对订单的引用来完成这项工作 - 然后状态类将有权访问订单的私有成员。如果您不在.Net 中工作,则需要为您的语言找到类似的机制——例如,C++ 中的友元类。
关于您的状态和转换的一些评论:
状态 A 指出该订单是新订单,数量 >0 并且具有产品 ID。对我来说,这意味着您要么在构造函数中提供这两个值(以确保您的实例以有效状态开始,但您不需要 setQuantity 方法),或者您需要一个具有 assignProduct 的初始状态(Int32 quantity, Int32 productId) 方法,将从初始状态转换到状态 A。
同样,您可能需要考虑在供应商填写价格后从状态 C 转换到的最终状态。
如果您的状态转换需要分配两个属性,您可能需要考虑使用通过参数接受两个属性(而不是 setQuantity 后跟 set setProductId)的单一方法,以使转换显式。
我还建议使用更具描述性的状态名称 - 例如,将 StateD 称为 CanceledOrder,而不是。
这是我如何在 C# 中实现此模式的示例,而不添加任何新状态:
public class Order
{
private BaseState _currentState;
public Order(
Int32 quantity,
Int32 prodId)
{
Quantity = quantity;
ProductId = prodId;
_currentState = new StateA();
}
public Int32 Quantity
{
get; private set;
}
public Int32 ProductId
{
get; private set;
}
public String Supplier
{
get; private set;
}
public Decimal Price
{
get; private set;
}
public void CancelOrder()
{
_currentState.CancelOrder(this);
}
public void AssignSupplier(
String supplier)
{
_currentState.AssignSupplier(this, supplier);
}
public virtual void AssignPrice(
Decimal price)
{
_currentState.AssignPrice(this, price);
}
abstract class BaseState
{
public virtual void CancelOrder(
Order o)
{
throw new NotSupportedException(
"Invalid operation for order state");
}
public virtual void AssignSupplier(
Order o,
String supplier)
{
throw new NotSupportedException(
"Invalid operation for order state");
}
public virtual void AssignPrice(
Order o,
Decimal price)
{
throw new NotSupportedException(
"Invalid operation for order state");
}
}
class StateA : BaseState
{
public override void CancelOrder(
Order o)
{
o._currentState = new StateD();
}
public override void AssignSupplier(
Order o,
String supplier)
{
o.Supplier = supplier;
o._currentState = new StateB();
}
}
class StateB : BaseState
{
public virtual void AssignPrice(
Order o,
Decimal price)
{
o.Price = price;
o._currentState = new StateC();
}
}
class StateC : BaseState
{
}
class StateD : BaseState
{
}
}
您可以使用订单处理器类,但它们使用订单类上的公共方法,并让订单的状态类负责转换状态。如果您需要知道您当前处于什么状态(以允许订单处理器确定要做什么),您可以在订单类和 BaseState 上添加一个字符串状态属性,并让每个具体的状态类返回其名称。