質問

Java ゲーム サーバーの場合、パケットのアクション ID を送信します。これにより、基本的にパケットの目的がサーバーにわかります。各アクション ID (整数) を関数にマップしたいと考えています。スイッチを使わずにこれを行う方法はありますか?

役に立ちましたか?

解決

これはどうですか?

HashMap<Integer, Runnable> map = new HashMap<Integer, Runnable>();
map.put(Register.ID, new Runnable() { 
    public void run() { functionA(); }
});
map.put(NotifyMessage.ID, new Runnable() { 
    public void run() { functionB(); }
});
// ...
map.get(id).run();

(いくつかの引数を渡す必要がある場合は、適切なパラメーターを持つ関数で独自のインターフェイスを定義し、実行可能ではなく使用します)。

他のヒント

Javaには実際には機能ポインターがありません(代わりに匿名の内部クラスがありました)。ただし、タイプではなく値をオンにしている限り、スイッチを使用することには何の問題もありません。スイッチを使用したくない理由はありますか?コードのどこかでアクションIDとアクションの間のマッピングを行う必要があるように思われるので、シンプルにしてみませんか?

Javaには、ファーストクラスの機能ポインターがありません。同様の機能を達成するには、インターフェイスを定義および実装する必要があります。匿名のインナークラスを使用して簡単にすることができますが、それでもあまりきれいではありません。これが例です:

public interface PacketProcessor
{
    public void processPacket(Packet packet);
}

...

PacketProcessor doThing1 = new PacketProcessor()
{
    public void processPacket(Packet packet)
    {
        // do thing 1
    }
};
// etc.

// Now doThing1, doThing2 can be used like function pointers for a function taking a
// Packet and returning void

Swing/AWTを使用したことがありますか?彼らのイベント階層は同様の問題を解決します。 Javaが機能を渡す方法は、たとえばインターフェイスを使用することです。

public interface ActionHandler {
    public void actionPerformed(ActionArgs e);
}

次に、整数をこれらのオブジェクトにマッピングする場合は、次のようなものを使用できます。 java.util.HashMap<Integer,ActionHandler> それを管理するために。実際の実装は、匿名のクラス(Javaの「Lambda」の最良の近似)または適切なクラスのどこかで行くことができます。これが匿名のクラスの方法です:

HashMap<Integer,ActionHandler> handlers;
handlers.put(ACTION_FROB, new ActionHandler() {
    public void actionPerformed(ActionArgs e) {
        // Do stuff
        // Note that any outer variables you intend to close over must be final.
    }
});
handlers.get(ACTION_FROB).actionPerformed(foo);

(編集)さらに虐待したい場合は、Hashmapのように初期化できます。

HashMap<Integer,String> m = new HashMap<Integer,String>() {{
    put(0,"hello");
    put(1,"world");
}};

はい、しかし、インターフェイスを使用するということは、コールバックごとにインターフェイスを作成する必要があることを意味します。つまり、セットを渡すすべての関数を意味します。これを処理するデリゲート クラスを作成すると、(真の関数ポインターではありませんが) 渡される関数が得られ、戻り値の型としてジェネリックを悪用した場合は、キャストする必要がなく、ボトルネックがほとんどゼロになります。

C# デリゲート (正しくは MultiCastDelegate) は、MethodInfo メソッドを使用して情報を取得します。これは、java.lang.reflect.method を使用して Delegate クラスに対して行う必要があることと同じです。私は、この正確な問題を扱うこのサイトの別のフォームに Delegate(T) クラスのコードを投稿しました。(はい) C++ 出身なので、関数 (特に Void) を渡すためのより良い方法が必要なので、関数以上のインターフェイスを作成する必要があります。これで、関数を選択してパラメータ情報を入力できるようになりました。ほら!JIT や JVM による顕著な速度低下もなく、快適に使用できます。そして、Java プログラミングをたった 1 週間学習しただけなら、Java プログラマーなら誰でもそれを行うことができます。

また、ベース リスナーとそのリスナーに渡す基本インターフェイスを作成するときにも非常に役立ちます。関数の名前が変更されたため、別のリスナーを作成する必要はなくなりました。デリゲート クラスの作成には、非常に使いやすく、合格できるため、大きな利点があります。

静的メソッドをインターフェースできます。この方法では、パラメーターも指定できます。インターフェイスを宣言してください...

public interface RouteHandler {
    void handleRequest(HttpExchange t) throws IOException;
}

そしてあなたの地図...

private Map<String, RouteHandler> routes = new HashMap<>();

次に、インターフェイス/パラメーションに一致する静的メソッドを実装してください...

public static void notFound(HttpExchange t) throws IOException {
    String response = "Not Found";

    t.sendResponseHeaders(404, response.length());
    OutputStream os = t.getResponseBody();
    os.write(response.getBytes());
    os.close();
}

その後、これらのメソッドをマップに追加できます...

routes.put("/foo", CoreRoutes::notFound);

そして、次のように呼び出します...

RouteHandler handler = routes.get("/foo");
handler.handleRequest(exchange);

Another similar aproach could be using Java 8's Suppliers:

Map<Integer, Supplier<T> suppliers = new HashMap();
suppliers.put(1, () -> methodOne());
suppliers.put(2, () -> methodTwo());

// ...

public T methodOne() { ... }
public T methodTwo() { ... }

// ...

T obj = suppliers.get(id).run();

You can do this through the use of the chain of responsibility pattern.

It is a pattern that links different objects to together kind of like a linked list. i.e. Each object has a reference to next in the chain. The objects in the chain usually handles one specific behavior. The flow between the objects is very similar to the switch-case statement.

There are some gotchas, such as, it spreads your logic out, an excessively long chain can cause performance problems. But along with these gotchas you have the benefit of increased testability, and stronger cohesion. Also you are not limited to the using enum, byte, int short, and char expressions as the trigger for branching.

Check the closures how they have been implemented in the lambdaj library. They actually have a behavior very similar to C# delegates:

http://code.google.com/p/lambdaj/wiki/Closures

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top