я сделал конечный автомат и хотел бы, чтобы он использовал преимущества дженериков в java. в настоящее время я не вижу способа заставить это работать и получить красивый код. Я уверен, что эта проблема дизайна уже обсуждалась много раз, и я ищу какой-то вклад. Вот грубый набросок.
class State { ... }
только одна копия каждого отдельного объекта состояния (в основном анонимные классы, привязанные к статическим конечным переменным), у него есть пользовательские данные для каждого состояния. каждый объект состояния имеет родителя состояния (существует одно корневое состояние)
class Message { ... }
каждое сообщение создается отдельно, и каждое имеет пользовательские данные. они могут быть подклассами друг друга. существует один корневой класс сообщений.
class Handler { ... }
каждый обработчик создается только один раз и имеет дело с определенной комбинацией состояния/сообщения.
class StateMachine { ... }
в настоящее время отслеживает текущее состояние и список всех сопоставлений (State
,Message
) -> Handler
. у него есть и другие функции. я пытаюсь сохранить этот класс общим и подклассировать его с параметрами типа, поскольку он использовался в моей программе несколько раз, и каждый раз с другим набором Message
/ State
/ и Handler
. разные StateMachine
будут иметь разные параметры для своих обработчиков.
Подход А
пусть конечный автомат отслеживает все сопоставления.
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
проверки работоспособности для каждого обработчика сообщений (чтобы убедиться, что он получает ожидаемое сообщение).
Подход Б
давайте сделаем каждый обработчик сообщений параметризованным по типу сообщения
class MessageHandler<M extends Message> {
void process(M msg) { .... }
}
Проблема: стирание типов не позволит мне сохранить их в красивой хэш-карте, поскольку все MessageHandler
будут напечатаны по-разному. если я смогу сохранить их на карте, я не смогу их восстановить и вызвать с правильными аргументами.
Подход С
пусть объект состояния обрабатывает все сообщения.
class State<M extends Message> { ... }
class ServerState<M extends ServerMessage> extends State<M> { ... }
у меня есть обработчики сообщений, привязанные к определенным состояниям конечного автомата (поместив их внутрь) (каждый экземпляр конечного автомата будет иметь свой собственный список допустимых состояний), что позволяет обработчикам быть определенного типа. (конечный автомат сервера -> обработчик сообщений сервера).
Проблема: каждое состояние может обрабатывать только один тип сообщений. вы также теряете представление о том, что родительское состояние может обрабатывать сообщения, отличные от дочерних состояний. стирание типа также не позволяет StateMachine
вызывать методы обработки текущих состояний.
Подход D
имеют процесс сообщения, основанный на состоянии.
Проблема: никогда не рассматривалась, так как каждое сообщение должно иметь отдельный обработчик в зависимости от текущего состояния конечного автомата. отправитель не будет знать текущее состояние StateMachine
.
Подход Е
забудьте о дженериках и обработке состояний/сообщений жесткого кода с помощью оператора 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;
}
}