Реализация шаблона состояния

Я реализую шаблон состояния в Java для своего приложения и нуждаюсь в нескольких разъяснениях.

Конечный автомат имеет 5 состояний от состояния 1 до состояния 5. Всего 5 событий (от события 1 до события 5), которые вызывают переход состояния. Не все события применимы во всех штатах. Если событие неприменимо в этом конкретном состоянии, приложение выдаст исключение.

Когда конечный автомат инициализируется, он начинается с state1.

Ниже приведен интерфейс и класс контекста.

/*
 Interface defining the possible events in each state.
 Each Implementer will handle event in a different manner. 
*/
public interface State {
 /*
  Handlers for each event. Each Implementer will handle the vent in a different manner.
 */
 public void handleEvent1(StateContext context);
 public void handleEvent2(StateContext context);
 public void handleEvent3(StateContext context);
 public void handleEvent4(StateContext context);
 public void handleEvent5(StateContext context);
 // Method to enter state and do some action.
 public void enter(StateContext context);
 // Method to exit state and do some clean-up activity on exit .
 public void exit(StateContext context);
}

/*
  Context class which will handle the state change and delegate event to appropriate event handler of current state
*/
Class StateContext {

   private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

   private State currentState = null;

   StateContext() {
        currentState = new State1();
   }

   //Handle event1 and pass it to the appropriate event handler for the current state.
   public void handleEvent1() {
      currentState.handleEvent1(); 
   }
       .
       .
       .
   //Handle event5 and pass it to the appropriate event handler for the current state.
   public void handleEvent5() {
      currentState.handleEvent5(); 
   }

   // Method to change the state. 
   // This method will be called by each state when it needs to transit to a new state.
   public void changeState(State newState) {
          accquireLock();
          currentState.exit();
          currentState = newState;
          currentState.enter();           
   }

   // Release read lock and accquire write lock
   public void accquireLock() {
        lock.readLock().unlock()
        lock.writeLock().lock();
   }

   // accquire readlock and release write lock
   public void releaseLock() {
        lock.readLock().lock()
        lock.writeLock().unlock();
   }
}

Для простоты я предоставил реализацию только для одного состояния.

public class State1 implements State {
       public void handleEvent1(StateContext context) {
          //Hand1e Event 1
       }
              .
              .
              .
      public void handleEvent5(StateContext context) {
          //Handle Event 5
       }


       public void enter(StateContext context) {
           //Release the lock here
           context.releaseLock();
           /*Here is my question. Is it a  good java practice to expose accquire and release lock in Context object. And use the exposed method here to release lock. 
           */

           // Do some action on entering the state. This may take few seconds to finish

       }  
}

Я хочу снять блокировку только после входа в состояние. Также я не хочу удерживать блокировку до завершения ввода(). Если я удерживаю блокировку до завершения ввода, я не могу обрабатывать другие события, и это может быть тайм-аут. Для некоторых событий (которые на самом деле не меняют состояние) нам нужно прочитать состояние, и на основе состояния мы можем их обработать или игнорируй их. Если я не снимаю блокировку, они не могут быть обработаны. Также в некоторых других случаях, если событие завершается (это событие меняет состояние), конечный автомат во время ввода () не может его обработать. Я должен немедленно выключить конечный автомат, поскольку продолжение ввода() после того, как наступило событие выключения, нецелесообразно.

Мой вопрос: является ли хорошей практикой программирования java выставлять accquireLock и releaseLock как API в классе Context и использовать их в каждом классе состояния.

Спасибо, Арун


person Arun    schedule 07.08.2013    source источник
comment
Почему бы вам не вызвать releaseLock() после выполнения кода в вашем методе changeState()? Поскольку вы получаете блокировку в начале этого метода, казалось бы, логично разблокировать блокировку в конце, не так ли?   -  person Deactivator2    schedule 07.08.2013
comment
Для моего приложения, если я получаю событие выключения для выключения машины во время выполнения ввода(), я не должен продолжать ввод(). Продолжение может привести к неблагоприятным последствиям или может не привести к чему-либо полезному, кроме пустой траты ресурсов, пока не завершится enter(). В некоторых случаях ввод может занять более 5 минут и отправить много запросов. Если я продолжу, запросы могут не достичь получателя или получатель может отменить мой запрос, в этом случае я должен немедленно остановиться и перейти в исходное состояние (для чего мне нужна блокировка).   -  person Arun    schedule 07.08.2013
comment
Тогда вам нужен метод прерывания метода enter состояния, который превосходит любую имеющуюся у вас блокировку. Рассматривайте объект блокировки как блок для изменения состояния, но событие выключения важнее любого события изменения состояния и, следовательно, должно иметь возможность вступить в силу в любое время.   -  person Deactivator2    schedule 07.08.2013
comment
Да. Кажется, это хороший вариант. Постараюсь вписаться в мое приложение. Спасибо Деактиватор2 :)   -  person Arun    schedule 07.08.2013
comment
Извините, пропустил, чтобы сделать точку. Есть некоторые события, для которых мы просто считываем состояние и обрабатываем их или игнорируем соответственно. Если я использую механизм прерывания, я не могу прочитать состояние, так как блокировка записи удерживается обработкой потока enter(). Я отредактировал сообщение.   -  person Arun    schedule 07.08.2013
comment
Хм. Я понимаю, к чему вы клоните, но опять же, если вы перебиваете, имеет ли значение, в каком состоянии вы находитесь? Смысл прерывания в том, чтобы остановить происходящее как можно быстрее, верно?   -  person Deactivator2    schedule 07.08.2013
comment
Да, точно. Остановите то, что делает конечный автомат, и выключите конечный автомат. Однако на данный момент я не знаю, как обрабатывать события (для чего требуется только чтение состояния).   -  person Arun    schedule 07.08.2013
comment
Создайте в своем классе StateContext метод, который будет получать события выключения. Создайте метод в базовом классе State, который будет вызываться из первого метода, который установит логическое значение interrupted в каждом классе State в значение true. Затем, после каждого функционального блока кода в каждом State enter(), выполнить проверку логического значения interrupted и выйти из метода, если оно истинно.   -  person Deactivator2    schedule 07.08.2013


Ответы (1)


Чтобы ответить на вопрос, если блокировка удерживается классом «менеджера» состояния, то этот класс должен управлять доступом к блокировке, а не каким-либо из классов фактического состояния.

Что касается вашего заявления об удержании блокировки до тех пор, пока enter не завершится, именно в этом и заключается смысл блокировки: вы не хотите, чтобы другие методы вмешивались или прерывали. Вместо этого вы должны разработать своего рода очередь событий, чтобы перехватывать все события и ждать их распределения, если принимающий объект занят. Либо так, либо сделайте конкретное исключение для любых событий, которые, как вы знаете, будут прерваны, чтобы вы могли обойти блокировку при запуске указанных событий.

Если вы решите использовать метод прерывания, вам придется реализовать ряд проверок в методе enter каждого класса State, чтобы проверить, было ли инициировано событие выключения. Единственный другой способ, который я вижу, это работающий, чтобы каждый State расширял Thread так, чтобы вы могли прервать/остановить его по желанию, хотя это не похоже на допустимый вариант в данном случае.

person Deactivator2    schedule 07.08.2013
comment
Я также согласен с вашей точкой зрения, что если менеджер держит блокировку, только он должен снять блокировку. Хорошей практикой является раскрытие блокировки получения и освобождения в качестве API в Менеджере, а также получение и освобождение ее в обработчике состояния. например, в классе State1 обработчик событий будет выглядеть как public void handleEvent1() { context.accquireLock(); //Сделай что-нибудь; контекст.releaseLock(); } - person Arun; 08.08.2013
comment
@Arun Да, это хороший шаблон дизайна. Другим было бы иметь код блокировки в обработчике событий контекста вместо состояния, но функциональность та же. - person Deactivator2; 08.08.2013
comment
хорошо, спасибо ... Тогда мой дизайн будет заключаться в том, чтобы после входа в метод enter() порождать новый поток для выполнения операции, которая должна выполняться после входа в каждое состояние. При этом блокировки не будут удерживаться до тех пор, пока операция не завершится, и я смогу обрабатывать события, которым нужно просто прочитать текущее состояние. После завершения операции, т.е. в конце вновь порожденного потока, приобретите состояние изменения блокировки (при необходимости). - person Arun; 08.08.2013