Это идеальный сценарий для паттерна состояний.
В шаблоне состояния ваши классы состояния должны нести ответственность за переходное состояние, а не только за проверку действительности перехода. Кроме того, перемещение переходов между состояниями за пределы класса порядка не является хорошей идеей и противоречит шаблону, но вы все равно можете работать с классом OrderProcessor.
Вы должны заставить каждый класс состояния реализовать операцию setQuantity. Класс состояния должен реализовывать все методы, которые могут быть действительными в одних состояниях, но не в других, независимо от того, включает ли это изменение состояния.
Нет необходимости в таких методах, как canChangeQuantity () и isValid () - классы состояния гарантируют, что экземпляры вашего заказа всегда находятся в допустимом состоянии, потому что любая операция, недопустимая для текущего состояния, сработает, если вы ее попробуете.
Свойства вашего класса Order хранятся вместе с порядком, а не с состоянием. В .Net вы могли бы выполнить эту работу, вложив свои классы состояния в класс Order и предоставив ссылку на порядок при выполнении вызовов - тогда класс состояния будет иметь доступ к закрытым членам заказа. Если вы не работаете в .Net, вам необходимо найти аналогичный механизм для вашего языка - например, классы друзей в C ++.
Несколько комментариев по поводу ваших состояний и переходов:
Состояние A отмечает, что заказ новый, количество> 0 и у него есть идентификатор продукта. Для меня это означает, что вы либо предоставляете оба этих значения в конструкторе (чтобы гарантировать, что ваш экземпляр запускается в допустимом состоянии, но вам не понадобится метод setQuantity), либо вам нужно начальное состояние, которое имеет assignProduct (Количество Int32, идентификатор продукта Int32), который перейдет из начального состояния в состояние A.
Точно так же вы можете рассмотреть конечное состояние для перехода из состояния C после того, как поставщик указал цену.
Если для перехода между состояниями требуется присвоение двух свойств, вы можете рассмотреть возможность использования одного метода, который принимает оба свойства по параметру (а не setQuantity, за которым следует 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
{
}
}
Вы можете работать со своими классами обработчика заказов, но они работают с общедоступными методами класса заказа и позволяют классам состояния заказа нести всю ответственность за переходное состояние. Если вам нужно знать, в каком состоянии вы находитесь в данный момент (чтобы обработчик заказов мог определять, что делать), вы можете добавить свойство String Status в класс заказа и в BaseState, чтобы каждый конкретный класс состояния возвращал свое имя.
person
Remi Despres-Smyth
schedule
11.12.2009