これは、状態パターンの理想的なシナリオです。
状態パターンでは、状態クラスは、遷移の有効性をチェックするだけでなく、状態の遷移を担当する必要があります。さらに、注文クラスの外に状態遷移をプッシュすることは良い考えではなく、パターンに反しますが、それでも OrderProcessor クラスを操作できます。
setQuantity 操作を実装するには、各状態クラスを取得する必要があります。状態クラスは、状態の変更を伴うかどうかにかかわらず、一部の状態では有効であるが他の状態では有効でない可能性があるすべてのメソッドを実装する必要があります。
canChangeQuantity() や isValid() などのメソッドは必要ありません。現在の状態に対して無効な操作を試行するとスローされるため、状態クラスにより、注文インスタンスが常に有効な状態にあることが保証されます。
Order クラスのプロパティは、状態ではなく注文と共に保存されます。.Net では、状態クラスを Order クラス内にネストし、呼び出し時に注文への参照を提供することでこれを機能させます。これにより、状態クラスは注文のプライベート メンバーにアクセスできるようになります。.Net で作業していない場合は、言語用の同様のメカニズムを見つける必要があります。たとえば、C++ のフレンド クラスです。
状態と遷移に関するいくつかのコメント:
状態 A は、注文が新規であり、数量が 0 を超えており、製品 ID があることを示しています。私にとって、これは、コンストラクターでこれらの値の両方を提供していることを意味します (インスタンスが有効な状態で開始されるようにするためですが、setQuantity メソッドは必要ありません)、または assignProduct を持つ初期状態が必要です。初期状態から状態 A に遷移する (Int32 quantity, Int32 productId) メソッド。
同様に、サプライヤが価格を記入した後、状態 C から移行する最終状態を検討することもできます。
状態遷移で 2 つのプロパティの割り当てが必要な場合は、遷移を明示的にするために、(setQuantity の後に setsetProductId が続くのではなく) パラメータによって両方のプロパティを受け入れる単一のメソッドの使用を検討することをお勧めします。
また、よりわかりやすい状態名をお勧めします。たとえば、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 に String Status プロパティを追加し、それぞれの具体的な状態クラスにその名前を返すようにします。