Я использую несколько конечных автоматов на основе перечисления в своем приложении для Android. Хотя они работают очень хорошо, я ищу предложение о том, как элегантно получать события, обычно из зарегистрированных обратных вызовов или из сообщений шины событий, в текущее активное состояние. Из множества блогов и руководств, касающихся автоматов на основе перечисления, в большинстве из них приводятся примеры конечных автоматов, которые потребляют данные (например, парсеры), а не показано, как эти конечные автоматы могут управляться событиями.
Типичный конечный автомат, который я использую, имеет такую форму:
private State mState;
public enum State {
SOME_STATE {
init() {
...
}
process() {
...
}
},
ANOTHER_STATE {
init() {
...
}
process() {
...
}
}
}
...
В моей ситуации некоторые состояния запускают часть работы, которая должна быть выполнена над определенным объектом, регистрируя слушателя. Этот объект асинхронно перезванивает, когда работа сделана. Другими словами, простой интерфейс обратного вызова.
Точно так же у меня EventBus. Классы, желающие получать уведомления о событиях, снова реализуют интерфейс обратного вызова и listen()
для этих типов событий на EventBus.
Таким образом, основная проблема заключается в том, что конечный автомат или его отдельные состояния, или класс, содержащий перечисление FSM, или что-то должны реализовывать эти интерфейсы обратного вызова, чтобы они могли представлять события в текущем состоянии.
Один из подходов, который я использовал, - это реализация интерфейса (ов) обратного вызова для всего enum
. Само перечисление имеет стандартные реализации методов обратного вызова внизу, и отдельные состояния могут затем переопределить эти методы обратного вызова для событий, которые им интересны. Чтобы это работало, каждое состояние должно регистрироваться и отменять регистрацию при входе и выходе, в противном случае существует риск того, что обратный вызов произойдет в состоянии, которое не является текущим состоянием. Я, вероятно, буду придерживаться этого, если не найду ничего лучше.
Другой способ - реализовать обратные вызовы в содержащем классе. Затем он должен делегировать эти события конечному автомату, вызвав mState.process( event )
. Это означает, что мне нужно будет перечислить типы событий. Например:
enum Events {
SOMETHING_HAPPENED,
...
}
...
onSometingHappened() {
mState.process( SOMETHING_HAPPENED );
}
Однако мне это не нравится, потому что (а) у меня было бы уродство необходимости switch
для типов событий в пределах process(event)
каждого состояния, и (б) передача дополнительных параметров выглядит неудобно.
Я хотел бы предложить элегантное решение для этого, не прибегая к использованию библиотеки.
enum
s могли бы реализовать интерфейсы слушателя таким образом, чтобы избежать необходимости прослушивать и отправлять события из внешнего контекста в текущее состояниеenum
. Другими словами, есть ли способ избежать эффективного определения и прослушивания событий дважды. Я понимаю, что это нереалистичный или невозможный вопрос. Хотя были выдвинуты некоторые умные идеи (например, dudeprgm), я склоняюсь к тому, чтобы придерживаться текущего кода, основанного на вашем ответе. - person Trevor   schedule 05.01.2015