ステートマシンを作成し、Javaのジェネリックを利用したいと考えています。現在、私はこれを機能させて見栄えの良いコードを取得する方法がわかりません。この設計の問題は以前にも何度もアプローチされていると確信しており、いくつかの入力を探しています。ここに大まかな概要があります。
class State { ... }
各個別の状態オブジェクト(ほとんどの場合、静的最終変数に関連付けられた匿名クラス)のコピーが1つだけあり、各状態のカスタムデータがあります。各状態オブジェクトには状態の親があります(ルート状態は1つです)
class Message { ... }
各メッセージは個別に作成され、各メッセージにはカスタムデータがあります。それらは互いにサブクラス化する場合があります。ルートメッセージクラスが1つあります。
class Handler { ... }
各ハンドラーは1回だけ作成され、特定の状態/メッセージの組み合わせを処理します。
class StateMachine { ... }
State
現在、現在の状態と、すべての( 、Message
)->Handler
マッピングのリストを追跡します。他の機能もあります。私はこのクラスをジェネリックに保ち、プログラムで何度も使用され、毎回異なるセットのMessage
's / State
's/とHandler
'sを使用して、型パラメーターでサブクラス化しようとしています。が異なる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> { ... }
私はメッセージハンドラーを特定のステートマシンの状態に(それらを内部に置くことによって)結び付けています(ステートマシンの各インスタンスには有効な状態の独自のリストがあります)。これにより、ハンドラーを特定のタイプにすることができます。(サーバーステートマシン->サーバーメッセージハンドラー)。
問題:各状態は1つのメッセージタイプしか処理できません。また、親の状態が子の状態とは異なるメッセージを処理できるという考えも失われます。型消去は、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;
}
}