2010-04-30 18 views
10

Actualmente, tengo un montón de clases de Java que implementan una interfaz Processor, lo que significa que todas tienen un método processRequest(String key). La idea es que cada clase tiene un miembro de pocos (por ejemplo, < 10) Strings, y cada uno de esos mapas a un método en el que la clase a través del método processRequest, así:¿Cómo puedo asignar una cadena a una función en Java?

class FooProcessor implements Processor 
{ 
    String key1 = "abc"; 
    String key2 = "def"; 
    String key3 = "ghi"; 
    // and so on... 

    String processRequest(String key) 
    { 
     String toReturn = null; 
     if (key1.equals(key)) toReturn = method1(); 
     else if (key2.equals(key)) toReturn = method2(); 
     else if (key3.equals(key)) toReturn = method3(); 
     // and so on... 

     return toReturn; 
    } 

    String method1() { // do stuff } 
    String method2() { // do other stuff } 
    String method3() { // do other other stuff } 
    // and so on... 
} 

Usted consigue la idea.

Esto funcionaba bien para mí, pero ahora necesito un mapeo accesible desde el tiempo de ejecución de la clave a la función; no todas las funciones en realidad devuelven una Cadena (algunas vuelven vacías) y necesito acceder dinámicamente al tipo de retorno (usando reflexión) de cada función en cada clase para la que hay una clave. Ya tengo un administrador que conoce todas las claves, pero no la asignación de la clave a la función.

Mi primer instinto fue reemplazar este mapeo usando if-else declaraciones con un Map<String, Function>, como podría hacerlo en Javascript. Pero Java no admite funciones de primera clase, así que no tengo suerte allí. Probablemente podría desenterrar una biblioteca de terceros que me permita trabajar con funciones de primera clase, pero aún no he visto ninguna, y dudo que necesite una biblioteca completamente nueva.

También pensé en poner estos String teclas en una matriz y utilizando la reflexión para invocar los métodos por su nombre, pero veo dos inconvenientes de este método:

  • Mis llaves tendrían que ser el mismo nombre que el método - o ser nombrado de una manera particular y consistente para que sea fácil asignarlos al nombre del método.
  • Esto parece mucho más lento que las declaraciones if-else que tengo ahora. La eficiencia es algo preocupante porque estos métodos tienden a recibir llamadas con bastante frecuencia, y quiero minimizar la sobrecarga innecesaria.

TL; DR: Estoy buscando una manera limpia y mínima para mapear un String a algún tipo de objeto Function que pueda invocar y llamar (algo así como) getReturnType() en. No me importa especialmente utilizar una biblioteca de terceros si realmente se ajusta a mis necesidades. Tampoco me molesta usar la reflexión, aunque preferiría evitar el uso de la reflexión cada vez que realizo una búsqueda de métodos, tal vez usando alguna estrategia de almacenamiento en caché que combine el Map con la reflexión.

¿Piensas en una buena forma de obtener lo que quiero? ¡Aclamaciones!

+2

¿No podría hacer Cadena al método (http://java.sun.com/j2se/1.4.2/docs/api/java/lang/reflect/Method.html)? Luego puede almacenar en caché los métodos que necesita para ejecutar. –

+0

@Chris - Sí, básicamente es a donde me dirigía al final de mi pregunta. –

+0

Hecho :) :) :) –

Respuesta

1

¿No pudo hacer String a Method? Luego puede almacenar en caché los métodos que necesita para ejecutar.

0

Como habrás notado, puedes hacer lo que quieras usando el Reflection API, pero pierdes algunos beneficios del compilador de Java, además de los problemas que ya has presentado. ¿Va a envolver sus cadenas en un objeto y usar el Visitor pattern para resolver su problema? Cada StringWrapper solo aceptará un Visitor que tenga el método correcto o algo similar.

+0

No creo que el patrón Visitor sea el enfoque correcto aquí, es decir, no me venden. ¿En qué operaría el 'Visitor'? ¿Cuál sería la operación? –

+0

@Bears te comerá. Cuando estaba pensando en esta idea, me imaginé al visitante visitando un contenedor para cada cadena, pero de nuevo parece que no sabes qué cadena aparecerá cuando. Estoy pensando que la idea de la interfaz es mucho más simple. – justkt

+3

Visitante? Usa un patrón de comando o estrategia. Las otras respuestas demuestran el patrón de comando. Puede encontrar ejemplos de patrones de vida real [aquí] (http://stackoverflow.com/questions/1673841/examples-of-gof-design-patterns/2707195#2707195) para que pueda conocer un poco más sobre ellos. – BalusC

7

No hay funciones independientes de primera clase, pero puede hacer lo que quiera con una interfaz. Crea una interfaz que represente tu función.Por ejemplo, es posible que tenga la siguiente:

public interface ComputeString 
{ 
    public String invoke(); 
} 

continuación, puede crear un objeto Map<String,ComputeString> como usted desea en primer lugar. Usar un mapa será mucho más rápido que la reflexión y también proporcionará más seguridad de tipo, por lo que aconsejaría lo anterior.

+3

Eso significaría tener una clase de implementación para cada función que quiero invocar, lo que parece torpe y requiere un poco de repetición para cada función. No me suena tan genial. –

+1

Es totalmente torpe, pero eso es culpa de Java por no apoyar directamente las funciones de orden superior. –

+0

@Bears, sí, es torpe, pero puedes construir clases anónimas, lo que debería hacerlo algo menos torpe, como map.put (str, new ComputeString() {public String invoke() {return "blah";}}) ; –

4

Si bien no se puede tener funciones de primera clase, hay clases anónimas que pueden basarse en una interfaz:

interface ProcessingMethod { 
    String method(); 
} 

Map<String, ProcessingMethod> methodMap = new HashMap<String, ProcessingMethod>(); 
methodMap.put("abc", new ProcessingMethod() { 
    String method() { return "xyz" } 
}); 
methodMap.put("def", new ProcessingMethod() { 
    String method() { return "uvw" } 
}); 

methodMap.get("abc").method(); 

O usted podría utilizar Scala :-)

0

usar un mapa en el que el key es una cadena y el valor es un objeto que implementa una interfaz que contiene el método(). De esta forma, puede sacar del mapa el objeto que contiene el método que desea. Entonces simplemente llame a ese método en el objeto. Por ejemplo:

class FooProcessor implements Processor{ 

    Map<String, FooMethod> myMap; 

    String processRequest(String key){ 
     FooMethod aMethod = myMap.getValue(key); 
     return aMethod.method(); 
    }   
} 
0

Este example utiliza un enum de funciones con nombre y un resumen FunctionAdapter para invocar funciones con un número variable de parámetros homogéneos y sin reflexión. La función lookup() simplemente usa Enum.valueOf, pero una Map puede valer la pena para un gran número de funciones.

0

¿Qué pasa con Method clase de la API de reflexión? Puede encontrar métodos de una clase basados ​​en el nombre, los parámetros o el tipo de devolución. Entonces simplemente llama a Method.invoke (esto, parámetros).

Eso es más o menos lo mismo que un Mapa de JavaScript del que está hablando.

Cuestiones relacionadas