Шаблон состояния: Как состояния объекта должны перейти, когда они вовлечены в сложные процессы?

10
задан nick2083 28 August 2009 в 04:27
поделиться

5 ответов

Изменение текущего объекта состояния может быть выполнено непосредственно из объекта состояния, из Порядка и даже ОК из внешнего источника (процессора), хотя и необычно.

В соответствии с шаблоном состояния, Объект Order делегирует все запросы текущему объекту OrderState. Если setQuantity () - это операция, зависящая от состояния (она есть в вашем примере), то каждый объект OrderState должен реализовать ее.

0
ответ дан 4 December 2019 в 03:39
поделиться

У вас есть несколько разных классов, по одному на состояние.

BaseOrder {
    //  common getters
    // persistence capabilities
}

NewOrder extends BaseOrder {
    // setters
    CheckingOrder placeOrder();
} 

CheckingOrder extends BaseOrder {
     CancelledOrder cancel();
     PricingOrder assignSupplier();
}

и так далее. Идея состоит в том, что код, которому нужны заказы в определенном состоянии, получает только объекты нужного класса, поэтому проверки состояния не требуются. Код, который просто хочет работать с заказами в любом случае состояния, использует BaseClass.

-1
ответ дан 4 December 2019 в 03:39
поделиться

In order for the state pattern to work, the context object has to expose an interface that the state classes can use. At a bare minimum this will have to include a changeState(State) method. This I'm afraid is just one of the limitations of the pattern and is a possible reason why it is not always useful. The secret to using the state pattern is to keep the interface required by the states as small as possible and restricted to a tight scope.

(1) Having a canChangeQuantity method is probably better than having all states implement a setQuantity. If some states are doing something more complex than throwing an exception, this advice may not follow.

(2) The setState method is unavoidable. It should, however, be kept as tightly scoped as possible. In Java this would probably be Package scope, in .Net it would be Assembly (internal) scope.

(3) The point about validation raises the question of when you do validation. In some cases, it is sensible to allow the client to set properties to invalid values and only validate them when you do some processing. In this case each state having an 'isValid()' method that validates the whole context makes sense. In other cases you want a more immediate error in which case I would create an isQuantityValid(qty) and isPriceValid(price) that would be called by the set methods before changing the values, if they return false throw an exception. I've always called these two Early and Late Validation and it is not easy to say which you need without knowing more about what you are up to.

0
ответ дан 4 December 2019 в 03:39
поделиться

Это идеальный сценарий для шаблона состояния.

В шаблоне состояния ваши классы состояний должны отвечать за переходное состояние, не просто проверка действительности перехода. Кроме того, перемещение переходов между состояниями за пределы класса порядка не является хорошей идеей и противоречит шаблону, но вы все равно можете работать с классом OrderProcessor.

Вы должны заставить каждый класс состояний реализовать операцию setQuantity. Класс состояния должен реализовывать все методы, которые могут быть действительными в одних состояниях, но не в других, независимо от того, включает ли это изменение состояния.

Нет необходимости в таких методах, как canChangeQuantity () и isValid () - классы состояния гарантируют, что экземпляры вашего заказа всегда находятся в допустимом состоянии, потому что любая операция, недопустимая для текущего состояния, будет срабатывать, если вы попробуете ее .

Свойства вашего класса Order хранятся вместе с порядком, а не с состоянием. В .Net вы бы сделали эту работу, вложив свои классы состояния в класс Order и предоставив ссылку на порядок при выполнении вызовов - тогда класс состояния будет иметь доступ к закрытым членам заказа. Если вы не работаете в .Net, вам необходимо найти аналогичный механизм для вашего языка - например, классы друзей в C ++.

Несколько комментариев по вашим состояниям и переходам:

  • Состояние A отмечает, что порядок новый, количество> 0 и идентификатор продукта. Для меня это означает, что ты » d реализовать этот шаблон на 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, чтобы каждый конкретный класс состояния возвращал свое имя.

6
ответ дан 4 December 2019 в 03:39
поделиться

Я бы сохранил информацию в классе Order и передал бы указатель на экземпляр Order в состояние. Примерно так:


class Order {
  setQuantity(q) {
    _state.setQuantity(q);
  } 
}

StateA {
  setQuantity(q) {
    _order.q = q;
  }
}

StateB {
  setQuantity(q) {
    throw exception;
  }
}

0
ответ дан 4 December 2019 в 03:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: