2008-12-28 7 views
10

Para mi servidor de juegos Java, envío el Action ID del paquete que básicamente le dice al servidor para qué es el paquete. Quiero asignar cada ID de acción (un entero) a una función. ¿Hay alguna forma de hacerlo sin usar un interruptor?punteros de función/delegados en Java?

+0

En general, un interruptor va a ser mejor. – TheSoftwareJedi

+0

Sí, es casi seguro que un interruptor sea mucho más rápido, más pequeño, más limpio. –

+1

posible duplicado de [¿Cuál es el sustituto más cercano para un puntero de función en Java?] (Http://stackoverflow.com/questions/122407/whats-the-nearest-substitute-for-a-function-pointer-in-java) – Raedwald

Respuesta

21

¿Qué hay de este?

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

(Si tiene que pasar algunos argumentos, definir su propia interfaz con una función que tiene un parámetro adecuado, y el uso que en lugar de Ejecutable).

+3

Creo que un modificador al que se llamarán functionA y functonB sería un código mucho más claro. Esto simplemente mueve el mapeo de ID a funciones en la sección de código donde está cargando el mapa, en lugar de donde está llamando a la función. –

+0

sí, acepto que un cambio sería más limpio. Yo usaría un interruptor también. pero creo que deberías contarle el cuestionario original (en la sección de comentarios de OP) :) –

+0

Lo puse en mi respuesta. –

1

Java no tiene punteros de función de primera clase. Para lograr una funcionalidad similar, debe definir e implementar una interfaz. Puedes hacerlo más fácil usando clases internas anónimas, pero aún no es muy bonito. Aquí hay un ejemplo:

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 
+0

Para obtener más información sobre los "indicadores de función" en Java, consulte: http://stackoverflow.com/q/122407/545127 – Raedwald

1

¿Alguna vez ha utilizado Swing/AWT? Su jerarquía de eventos resuelve un problema similar. La forma en que Java pasa alrededor de funciones es una interfaz, por ejemplo

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

Entonces, si desea asignar números enteros en estos objetos, se puede usar algo como un java.util.HashMap<Integer,ActionHandler> para manejar eso. Las implementaciones reales pueden ir en clases anónimas (la mejor aproximación de Java de "lambda") o en clases apropiadas en alguna parte. Esta es la forma en clase anónima:

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

(editar) Si quieres ser aún más abusiva, puede inicializar el HashMap así:

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

Java no tiene realmente punteros de función (que obtuve clases internas anónimas en su lugar). Sin embargo, no hay nada de malo con el uso de un conmutador, siempre que esté activando el valor y no el tipo. ¿Hay alguna razón por la que no quieres usar un interruptor? Parece que tendrás que hacer un mapeo entre los identificadores de acción y las acciones en algún lugar de tu código, entonces ¿por qué no mantenerlo simple?

+0

Sí, mi pensamiento era el mismo. Yo usaría el interruptor. Creo que detrás de las cámaras los conmutadores se manejan de forma aleatoria en lugar de controlar secuencialmente los casos como si tuviera que hacer con if/else. Lo único es que los índices de casos deben ser consecutivos, creo ... –

+0

No tienen que ser consecutivos. Puede poner los casos más probables primero si necesita optimizarlos. (Mida siempre antes de optimizar.) –

+0

Una instrucción de conmutación no permite que las clases de varias partes de su código carguen dinámicamente las devoluciones de llamadas. Esto evita características avanzadas tales como la carga de devoluciones de llamadas desde archivos de configuración en tiempo de ejecución y tal.Recomiendo contra declaraciones de cambio en favor de una solución más potente por esta razón. –

0

Puede hacerlo mediante el uso del patrón de cadena de responsabilidad.

Es un patrón que vincula diferentes objetos para formar una especie de lista vinculada. es decir, cada objeto tiene una referencia al siguiente en la cadena. Los objetos en la cadena generalmente manejan un comportamiento específico. El flujo entre los objetos es muy similar al de la sentencia switch-case.

Hay algunos errores, como, extiende su lógica, una cadena excesivamente larga puede causar problemas de rendimiento. Pero junto con estas trampas, usted tiene el beneficio de una mayor capacidad de prueba y una mayor cohesión. Además, no está limitado a usar expresiones enum, byte, int short y char como el desencadenante de la bifurcación.

+0

Dulce. Me votaron sin explicación. Supongo que a la persona no le gustó la forma en que escribí mi respuesta. Quizás lo que dije estuvo mal. Supongo que la persona tiene un problema con el CDR. Pero solo estoy adivinando porque no hubo ningún comentario sobre por qué. –

+0

¿Qué es este patrón? ¿Cómo resuelve/elude el problema de usar un código para cambiar varias funciones? Un ejemplo, o al menos un enlace, habría guardado esta respuesta. Pero sí, no me gustó la forma en que escribiste tu respuesta. –

+0

Gracias. Eso es todo lo que pido. Si tienes las agallas para vengar, alguien tiene las agallas para dar una razón. –

1

Sí, pero utilizando una interfaz significa que tenga que crear una interfaz para cada una devolución de llamada que significa que cada función que desea pasarlo establecido. Crear una clase de delegado para manejar esto le da (no un verdadero puntero de función) sino la función que se va a pasar y si usa un genérico para ser el tipo de devolución, no tiene que lanzar, lo que reduce el cuello de botella a casi nada.

El delegado C# (MultiCastDelegate para ser correcto) obtiene la información del método MethodInfo, que es lo mismo que tendría que hacer para la clase Delegate utilizando java.lang.reflect.method. Publiqué mi código para la clase Delegado (T) en otra forma de este sitio que trata este tema de contacto. Lo hago porque (sí) desde C++ necesito una mejor manera para pasar funciones (especialmente Void) luego tener que crear una interfaz para la función o más. Ahora puedo elegir la función que completa la información del parámetro. Voila`! Agradable y utilizable, sin perder velocidad en JIT o JVM. Y si solo tuve que aprender programación de Java por solo una semana, cualquier programador de Java puede hacerlo.

Además, sirve muy bien al crear un oyente base y una interfaz base para pasar al oyente. Ya no tendrá que escribir otro oyente porque el nombre de la función ha cambiado. Crear una clase de delegado tiene grandes ventajas, ya que es muy útil y aceptable.

0

Puede interconectar métodos estáticos. Este método le permite especificar parámetros también. Declarar su interfaz ...

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

Y su mapa ...

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

luego implementar métodos estáticos que coinciden con los de interfaz/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(); 
} 

A continuación, puede añadir esos métodos en su mapa ...

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

y llámelos de la siguiente manera ...

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