2010-04-30 26 views
10

Attualmente, ho un sacco di classi Java che implementano un'interfaccia Processor, il che significa che hanno tutti un metodo processRequest(String key). L'idea è che ogni classe ha un membro di alcuni (per esempio, < 10) Strings, e ciascuna di queste mappe ad un metodo di quella classe attraverso il metodo processRequest, in questo modo:Come posso mappare una stringa a una funzione in 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... 
} 

Si ottiene l'idea.

Questo funzionava bene per me, ma ora ho bisogno di un mapping accessibile in runtime da chiave a funzione; non tutte le funzioni in realtà restituiscono una stringa (alcuni return void) e ho bisogno di accedere dinamicamente al tipo restituito (usando reflection) di ogni funzione in ogni classe per cui esiste una chiave. Ho già un manager che conosce tutte le chiavi, ma non la mappatura da chiave a funzione.

Il mio primo istinto è stato quello di sostituire questa mappatura utilizzando le dichiarazioni if-else con un Map<String, Function>, come avrei potuto fare in Javascript. Ma Java non supporta le funzioni di prima classe, quindi sono sfortunato. Probabilmente potrei trovare una libreria di terze parti che mi permetta di lavorare con funzioni di prima classe, ma non ne ho ancora viste, e dubito che abbia bisogno di un'intera nuova libreria.

Ho anche pensato di mettere questi String chiavi in ​​un array e l'utilizzo di riflessione per richiamare i metodi per nome, ma vedo due aspetti negativi a questo metodo:

  • mie chiavi dovrebbero essere chiamato lo stesso il metodo - o essere nominato in un modo particolare, coerente in modo che sia facile mapparli al nome del metodo.
  • Questo sembra più lento delle dichiarazioni if-else che ho adesso. L'efficienza è una preoccupazione, perché questi metodi tendono a essere chiamati abbastanza frequentemente, e voglio ridurre al minimo i costi non necessari.

TL; DR: Sto cercando un modo pulito, minimale per mappare uno String in una sorta di oggetto Function che posso richiamare e chiamare (qualcosa di simile) getReturnType() su. Non mi interessa in particolare l'utilizzo di una libreria di terze parti se è in realtà adatta alle mie esigenze. Inoltre, non mi dispiace usare il reflection, anche se preferirei fortemente evitare di usare reflection ogni volta che eseguo una ricerca di metodo - magari usando una strategia di caching che combini lo Map con la riflessione.

Pensieri su un buon modo per ottenere quello che voglio? Saluti!

+2

Non si può eseguire il comando String to Method (http://java.sun.com/j2se/1.4.2/docs/api/java/lang/reflect/Method.html)? Quindi puoi memorizzare nella cache i metodi che devi eseguire. –

+0

@Chris - sì, questo è praticamente il punto in cui stavo andando alla fine della mia domanda. –

+0

Fatto :) :) :) –

risposta

1

Non potresti fare String a Method? Quindi puoi memorizzare nella cache i metodi che devi eseguire.

0

Come hai notato, puoi fare quello che vuoi usando lo Reflection API, ma perdi alcuni benefici del compilatore Java, oltre ai problemi che hai già creato. Avvolgere le stringhe in un oggetto e utilizzare lo Visitor pattern risolve il problema? Ogni StringWrapper accetta solo uno Visitor con il metodo giusto o qualcosa del genere.

+0

Non penso che il pattern Visitor sia il giusto approccio qui - cioè, non sono venduto su di esso. Su cosa funzionerebbe il 'Visitor'? Quale sarebbe l'operazione? –

+0

@Bears ti mangerà - Mentre stavo pensando a questa idea, ho immaginato il Visitatore che visita un wrapper per ogni Stringa, ma poi di nuovo sembra che tu non sappia quale Stringa apparirà quando. Penso che l'idea dell'interfaccia sia molto più semplice. – justkt

+3

Visitatore? Usa uno schema di comando o strategia. Le altre risposte dimostrano lo schema di comando. Puoi trovare esempi di modelli di vita reale [qui] (http://stackoverflow.com/questions/1673841/examples-of-gof-design-patterns/2707195#2707195) in modo che tu possa imparare un po 'di più su di loro. – BalusC

7

Non ci sono funzioni stand-alone di prima classe, ma è possibile eseguire ciò che si desidera tramite un'interfaccia. Crea un'interfaccia che rappresenti la tua funzione.Ad esempio, si potrebbe avere la seguente:

public interface ComputeString 
{ 
    public String invoke(); 
} 

Quindi è possibile creare un oggetto Map<String,ComputeString> come si vuole, in primo luogo. Usare una mappa sarà molto più veloce del riflesso e darà anche più sicurezza sul tipo, quindi consiglierei quanto sopra.

+3

Ciò significherebbe avere una classe di implementazione per ogni singola funzione che desidero richiamare, il che sembra complicato e richiede un po 'di codice per ogni singola funzione. Non sembra fantastico per me. –

+1

È totalmente goffo, ma è colpa di Java non supportare direttamente le funzioni di ordine superiore. –

+0

@Bears, sì è goffo, ma puoi costruire classi anonime, che dovrebbero renderlo un po 'meno goffo, come map.put (str, new ComputeString() {public String invoke() {return "blah";}}) ; –

4

Anche se non si può avere funzioni di prima classe, ci sono classi anonime, che può essere basata su un'interfaccia:

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

Oppure si potrebbe usare Scala :-)

0

utilizzare una mappa dove la key è una stringa e il valore è un oggetto che implementa un'interfaccia contenente method(). In questo modo puoi ottenere l'oggetto contenente il metodo che desideri dalla mappa. Quindi chiama quel metodo sull'oggetto. Ad esempio:

class FooProcessor implements Processor{ 

    Map<String, FooMethod> myMap; 

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

Questa example utilizza un enum di funzioni con nome ed un estratto FunctionAdapter per richiamare le funzioni con un numero variabile di parametri omogenei senza riflessione. La funzione lookup() utilizza semplicemente Enum.valueOf, ma uno Map potrebbe valerne la pena per un numero elevato di funzioni.

0

E la classe Method dall'API di riflessione? Puoi trovare i metodi di una classe in base a nome, parametri o tipo di reso. Quindi chiama Method.invoke (questo, parametri).

È praticamente la stessa di una mappa di JavaScript di cui si sta parlando.

Problemi correlati