Frage

Für meinen Java -Spielserver sende ich die Aktions -ID des Pakets, die dem Server im Grunde mitteilt, wofür das Paket ist. Ich möchte jede Aktions -ID (eine Ganzzahl) einer Funktion zuordnen. Gibt es eine Möglichkeit, dies zu tun, ohne einen Schalter zu verwenden?

War es hilfreich?

Lösung

Was ist mit diesem?

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();

(Wenn Sie einige Argumente übergeben müssen, definieren Sie Ihre eigene Schnittstelle mit einer Funktion mit einem geeigneten Parameter und verwenden Sie diese anstelle von Runnable).

Andere Tipps

Java hat keine wirklich Funktionszeiger (wir haben stattdessen anonyme innere Klassen). Es ist jedoch wirklich nichts Falsches daran, einen Switch zu verwenden, solange Sie den Wert und nicht auf dem Typ einschalten. Gibt es einen Grund, warum Sie keinen Switch verwenden möchten? Es scheint, als müssten Sie irgendwo in Ihrem Code eine Zuordnung zwischen Aktions -IDs und Aktionen durchführen. Warum also nicht einfach halten?

Java hat keine erstklassigen Funktionszeiger. Um eine ähnliche Funktionalität zu erzielen, müssen Sie eine Schnittstelle definieren und implementieren. Sie können es einfacher machen, anonyme innere Klassen zu verwenden, aber es ist immer noch nicht sehr hübsch. Hier ist ein Beispiel:

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

Haben Sie jemals Swing/AWT verwendet? Ihre Ereignishierarchie löst ein ähnliches Problem. Die Art und Weise, wie Java funktioniert, ist zum Beispiel mit einer Schnittstelle

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

Wenn Sie dann Ganzzahlen auf diese Objekte abbilden möchten, können Sie so etwas wie a verwenden java.util.HashMap<Integer,ActionHandler> Um das zu verwalten. Die tatsächlichen Implementierungen können entweder in anonymen Klassen (Javas beste Annäherung an "Lambda") oder in den richtigen Klassen irgendwo eingehen. Hier ist die anonyme Klassenstraße:

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);

(Bearbeiten) Wenn Sie noch missbräuchlicher sein möchten, können Sie die HashMap wie so initialisieren:

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

Ja, aber mithilfe einer Schnittstelle müssen Sie für jeden Rückruf eine Schnittstelle erstellen, was bedeutet, dass jede Funktion, die Sie sie übergeben möchten. Erstellen einer Delegiertenklasse, um dies zu verarbeiten, gibt Ihnen (kein echter Funktion Zeiger), aber die zu übergebene Funktion. Wenn Sie einen Generikum als Rückgabetyp missbrauchen, müssen Sie nicht gießen, was den Engpass auf fast nichts reduziert.

Das C# -Delegate (MulticastDelegate to Corprey) wird von der Verwendung von Methoden -Methoden mithilfe von Methoden -Methoden verwendet, was Sie für die Delegate -Klasse unter Verwendung von java.lang.reflect.method tun müssen. Ich habe meinen Code für die Delegate (T) -Klasse in einer anderen Form dieser Site gepostet, die sich mit diesem ausgefeilten Problem befasst. Ich mache es, weil (ja) aus C ++ eine bessere Möglichkeit zum Übergeben von Funktionen (insbesondere für void) und dann eine Schnittstelle für die Funktion oder mehr erstellen muss. Jetzt kann ich die Funktionsfüllung in den Parameterinformationen dafür auswählen. Voila`! Schön und nutzbar ohne merkliche Verlust an Geschwindigkeit von JIT oder JVM. Und wenn ich nur eine Woche lang Java -Programmierung gelernt habe, kann es ein Java -Programmierer tun.

Außerdem dient es sehr gut beim Erstellen eines Basishörers und einer Basisoberfläche, die Sie in den Hörer übergeben können. Sie müssen nicht mehr einen anderen Hörer schreiben, da sich der Name der Funktion geändert hat. Das Erstellen einer Delegiertenklasse hat große Vorteile, da es sehr verwendbar und passable ist.

Sie können statische Methoden interfasieren. Mit dieser Methode können Sie auch Parameter angeben. Deklarieren Sie Ihre Schnittstelle ...

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

Und deine Karte ...

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

Implementieren Sie dann statische Methoden, die mit der Schnittstelle/Params übereinstimmen ...

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();
}

Sie können diese Methoden dann in Ihre Karte hinzufügen ...

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

und ruf sie wie folgt an ...

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

Ein weiterer ähnlicher Aproach könnte die Lieferanten von Java 8 verwenden:

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();

Sie können dies durch die Verwendung der Kette des Verantwortungsmusters tun.

Es ist ein Muster, das verschiedene Objekte mit einer Art verknüpften Liste zusammen verknüpft. IE Jedes Objekt hat einen Hinweis auf als nächstes in der Kette. Die Objekte in der Kette behandeln normalerweise ein spezifisches Verhalten. Der Fluss zwischen den Objekten ist der Switch-Case-Anweisung sehr ähnlich.

Es gibt einige Gotchas, wie z. B. Ihre Logik ausbreitet, eine übermäßig lange Kette kann Leistungsprobleme verursachen. Aber zusammen mit diesen Gotchas haben Sie den Vorteil einer erhöhten Prüfbarkeit und einem stärkeren Zusammenhalt. Sie sind auch nicht auf die Verwendung von Enum-, Byte-, INT -Short- und SHAR -Ausdrücken als Auslöser für die Verzweigung beschränkt.

Überprüfen Sie die Schließungen, wie sie in der Lambdaj -Bibliothek implementiert wurden. Sie haben tatsächlich ein Verhalten, das C# Delegierten sehr ähnlich ist:

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

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top