Domanda

Per il mio server di gioco Java invio l'ID azione del pacchetto che in pratica dice al server a cosa serve il pacchetto. Voglio mappare ogni ID di azione (un numero intero) su una funzione. C'è un modo per farlo senza usare uno switch?

È stato utile?

Soluzione

Che dire di questo?

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

(Se è necessario passare alcuni argomenti, definire la propria interfaccia con una funzione con un parametro adatto e usarlo anziché eseguire).

Altri suggerimenti

Java non ha davvero puntatori di funzioni (invece abbiamo ottenuto lezioni interne anonime). Tuttavia, non c'è davvero nulla di sbagliato nell'uso di un interruttore, purché tu stia cambiando il valore e non sul tipo. C'è qualche motivo per cui non vuoi usare uno switch? Sembra che dovrai fare una mappatura tra ID azione e azioni da qualche parte nel tuo codice, quindi perché non mantenerlo semplice?

Java non ha puntatori di funzionalità di prima classe. Per ottenere funzionalità simili, è necessario definire e implementare un'interfaccia. Puoi semplificare l'utilizzo di classi interne anonime, ma non è ancora molto carino. Ecco un esempio:

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

Hai mai usato swing/awt? La loro gerarchia degli eventi risolve un problema simile. Il modo in cui Java passa funziona con un'interfaccia, ad esempio

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

Quindi, se vuoi mappare i numeri interi su questi oggetti, potresti usare qualcosa come un java.util.HashMap<Integer,ActionHandler> per gestirlo. Le attuali implementazioni possono andare in classi anonime (la migliore approssimazione di Java di "Lambda") o in classi adeguate da qualche parte. Ecco il modo di classe anonima:

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

(Modifica) Se vuoi essere ancora più offensivo, puoi inizializzare l'hashmap in questo modo:

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

Sì, ma usare un'interfaccia significa che devi creare un'interfaccia per ogni callback, il che significa che ogni funzione che si desidera superarlo. La creazione di una classe delegata per gestire questo ti dà (non un vero puntatore di funzioni) ma la funzione da passare e se si abusi di un generico per essere il tipo di ritorno, non devi lanciare che riduce il collo di bottiglia a quasi nulla.

Il delegato C# (multicastDelegate per essere corretto) ottiene le informazioni dall'uso del metodo metodo che è la stessa cosa che dovresti fare per la classe delegata usando java.lang.reflect.method. Ho pubblicato il mio codice per la classe Delegate (T) su un'altra forma di questo sito che si occupa di questo problema di $ Lo faccio perché (sì) da C ++ ho bisogno di un modo migliore per passare le funzioni (specialmente vuoto) quindi dover creare un'interfaccia per la funzione o più. Ora posso scegliere la funzione compilando le informazioni sui parametri. Voilà`! Bello e utilizzabile senza perdite notevoli da JIT o JVM. E se ho avuto solo la programmazione di Java per solo una settimana, qualsiasi programmatore Java può farlo.

Inoltre, serve molto bene quando si crea un ascoltatore di base e un'interfaccia di base per passare l'ascoltatore. Non dover più scrivere un altro ascoltatore perché il nome della funzione è cambiato. La creazione di una classe delegata ha grandi vantaggi in quanto è molto utilizzabile e passabile.

Potresti interfacciarsi metodi statici. Questo metodo consente di specificare anche i parametri. Dichiara la tua interfaccia ...

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

E la tua mappa ...

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

Quindi implementa metodi statici che corrispondono all'interfaccia/params ...

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

Puoi quindi aggiungere quei metodi nella tua mappa ...

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

E chiamali come segue ...

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

Un altro approccio simile potrebbe essere l'uso dei fornitori di Java 8:

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

Puoi farlo attraverso l'uso della catena del modello di responsabilità.

È un modello che collega oggetti diversi a un po 'come un elenco collegato. cioè ogni oggetto ha un riferimento al prossimo nella catena. Gli oggetti nella catena di solito gestiscono un comportamento specifico. Il flusso tra gli oggetti è molto simile all'istruzione Switch-Case.

Ci sono alcuni gotchas, come ad esempio, diffonde la tua logica, una catena eccessivamente lunga può causare problemi di prestazione. Ma insieme a questi gotchas hai il vantaggio di una maggiore testabilità e una coesione più forte. Inoltre, non sei limitato alle espressioni ENUM, Byte, Int Short e Char come innesco per la ramificazione.

Controlla le chiusure come sono state implementate nella Biblioteca Lambdaj. In realtà hanno un comportamento molto simile ai delegati C#:

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top