i создал конечный автомат и хотел бы, чтобы он использовал преимущества дженериков в java. в настоящее время я не вижу, как я могу сделать эту работу и получить довольно перспективный код. Я уверен, что проблема с дизайном уже неоднократно подходила, и я искал некоторые данные. heres грубая схема.
class State { ... }
только одна копия каждого отдельного объекта состояния (в основном анонимные классы, привязанные к статическим конечным переменным), у него есть пользовательские данные для каждого состояния. каждый объект состояния имеет родительский элемент состояния (имеется одно корневое состояние)
class Message { ... }
каждое сообщение создается отдельно, и каждый из них имеет пользовательские данные. они могут подклассифицировать друг друга. существует один класс сообщений root.
class Handler { ... }
каждый обработчик создается только один раз и обрабатывает конкретную комбинацию состояний/сообщений.
class StateMachine { ... }
в настоящее время отслеживает текущее состояние и список всех (State
, Message
) → Handler
отображений. он имеет и другие функции. Я пытаюсь сохранить этот класс generic и подклассировать его с параметрами типа, так как он использовал кучу раз в моей программе и каждый раз с другим набором Message
/State
/и Handler
. разные StateMachine
будут иметь разные параметры для своих обработчиков.
Подход A
имеет автоответчик для отслеживания всех сопоставлений.
class StateMachine<MH extends MessageHandler> {
static class Delivery {
final State state;
final Class<? extends Message> msg;
}
HashMap<Delivery, MH> delegateTable;
...
}
class ServerStateMachine extends StateMachine<ServerMessageHandler> {
...
}
позволяет мне иметь настраиваемые методы обработчика для этого конкретного конечного автомата. параметры метода handler.process могут быть перезаписаны. Однако обработчик не может быть параметризован типом сообщения.
Проблема: это включает в себя проверку работоспособности instanceof
для каждого обработчика сообщений (убедитесь, что он получает ожидаемое сообщение).
Подход B
позволяет сделать каждый обработчик сообщения параметризованным по типу сообщения
class MessageHandler<M extends Message> {
void process(M msg) { .... }
}
Проблема: стирание типа не позволит мне хранить их в хорошем хешмапе, так как все MessageHandler
будут введены по-разному. если я могу хранить их на карте, я не смогу их отыскать и называть их соответствующими аргументами.
Подход C
объект объекта обрабатывает все сообщения.
class State<M extends Message> { ... }
class ServerState<M extends ServerMessage> extends State<M> { ... }
У меня есть обработчики сообщений, привязанные к конкретным состояниям состояния машины (путем помещения их внутрь), (каждый экземпляр конечного автомата будет иметь свой собственный список допустимых состояний), что позволяет обработчикам быть определенного типа. (серверный конечный автомат → обработчик сообщений сервера).
Проблема: каждое состояние может обрабатывать только один тип сообщения. вы также теряете идею о том, что родительское состояние может обрабатывать разные сообщения, чем дочерние состояния. type erasure также предотвращает вызов StateMachine
методов текущих состояний.
Подход D
сам процесс сообщения основывается на состоянии.
Проблема: никогда не рассматривалась, так как каждое сообщение должно иметь другой обработчик, основанный на текущем состоянии конечного автомата. отправитель не будет знать текущее состояние StateMachine
.
Подход E
забыть о дженериках и состоянии жесткого кода/обработке сообщений с помощью оператора switch.
Проблема: здравомыслие
Небезопасное решение:
Спасибо за ваш вклад всех, я думаю, проблема в том, что я не уменьшил это до хорошей проблемы (слишком много обсуждения), что у меня есть сейчас.
public class State { }
public class Message { }
public class MessageHandler<T extends Message> { }
public class Delivery<T extends Message> {
final State state;
final Class<T> msgClass;
}
public class Container {
HashMap<Delivery<? extends Message>, MessageHandler<? extends Message>> table;
public <T extends Message> add(State state, Class<T> msgClass, MessageHandler<T> handler) {
table.put(new Delivery<T>(state, msgClass), handler);
}
public <T extends Message> MessageHandler<T> get(State state, T msg) {
// UNSAFE - i cannot cast this properly, but the hashmap should be good
MessageHandler<T> handler = (MessageHandler<T>)table.get(new Delivery<T>(state, msg.getClass()));
return handler;
}
}