2011-01-12 5 views
1

Je me demande si j'utilise trop la réflexion java.
J'ai une classe qui est un détenteur de données pour un couple de cartes. J'ai des méthodes get (...) publiques qui, en donnant une clé en entrée, renvoient la valeur associée à celle-ci dans la carte correspondante.
Étant donné que les cartes sont volumineuses, je les charge uniquement lorsque je veux réellement y accéder. Donc, dans chaque méthode get (...), je vérifie si la carte est nulle. Si c'est le cas, j'appelle la méthode loadMap (..) correspondante.
Voici un exemple de code snippetJava: surutilisation de la réflexion?

public getId(String name) 
{ 
    try 
    { 
    if(nameMap1 == null) 
     loadNameMap1(); 
    } catch(...) {....} 

    return nameMap1.getId(name); 
} 

Le problème est que je dispose de plusieurs cartes. Donc, pour charger chaque carte, j'ai une méthode loadMap (..) différente et le bloc try catch dans les méthodes get (...). Donc, au lieu de cela, j'ai écrit une méthode appelée loadMap (Object map, String methodName) qui utilise la réflexion pour appeler la méthode appropriée et gère toutes les exceptions.

private synchronized void loadMap(Object map, String methodName) 
{ 
if (map == null) 
    try 
    { 
    Method method = this.getClass().getDeclaredMethod(methodName, new Class[0]); 
    method.invoke(this, new Object[0]); 
    } 
    catch (..) 
} 

Est-ce que j'utilise trop la réflexion ici? Y a-t-il une meilleure manière de faire cela? Est-ce que qualifie comme une « utilisation limitée de la réflexion », comme écrit dans Effective Java by Joshua Bloch
(Note: Je ne peux pas factoriser la classe en plusieurs classes)

+0

"Je ne peux pas refactoriser la classe en plusieurs classes". Et pourquoi est-ce que? Vous pouvez garder l'interface courante à l'extérieur, mais l'implémentation peut être refactorisée (en utilisant une interface MapLoader), non? – Thilo

+0

@Thilo: Je n'ai aucun contrôle sur la conception. – athena

+0

Si vous n'avez aucun contrôle sur la conception, comment pouvez-vous décider d'utiliser la réflexion? Il est certain que l'ajout de classes internes privées (c'est-à-dire les détails d'implémentation) n'entrent pas en conflit avec la conception des choses. – Thilo

Répondre

3
// could also be static 
private Map<String, Callable<Map>> myLoaders; 

private synchronized void loadMap(Object map, String mapName) 
{ 
if (map == null) 
    try 
    { 
     Callable<Map> mapLoader = myLoaders.get(mapName); 
     map = mapLoader.call(); 
    } 
    catch (..) 
} 

// and in the constructor or other init code 
myLoaders.put("map1", new Callable<Map>(){ 
    Map call(){ 
     // load map 1 
    }}); 

Je pense, bien que si tout ce que vous faites est de déplacer une commune essayer/attraper la logique d'un couple de méthodes où il doit être répété à un seul endroit, c'est la mauvaise approche. Vous perdez beaucoup de prise en charge de la vérification des erreurs du compilateur de cette façon. Certaines personnes utiliseraient un outil comme Aspect/J pour cela, mais je pense que vous avez juste à vivre avec le fait que Java n'a pas vraiment de facilité pour cela, réduire le fouillis au minimum en utilisant des fonctions privées partagées, et accepter le couple de copier/coller des lignes. Tant qu'il n'y a pas de "vrai code" dans ces lignes, ce n'est pas vraiment une duplication de code nuisible.

Alors:

public getId(String name){ 
    try{ 
     if (nameMap1 == null) 
      loadNameMap1(); 
     } 
     catch (....){ 
      privateHelperFunctionThatCutsThisDownToOneLine(name, "id", "nameMap1"); 
     } 
    } 

    // you are left with the above repetitive three (or seven) lines, 
    // but that is Java for you... 
    // in return, you get nice, static compile-time error checking 


private void privateHelperFunctionThatCutsThisDownToOneLine(){ 
     // all the long repeated code in the exception handler 
     // goes here. 
} 
+0

Merci beaucoup. J'ai implémenté votre solution. Juste par curiosité, est-ce une sorte de modèle? – athena

+0

En Perl, ils l'appellent "table d'expédition". En Java, il s'agit probablement d'un mauvais design (à moins que vous ne le souhaitiez configurable/extensible à l'exécution, alors cela a du sens). – Thilo

+0

Pourquoi est-ce mauvais design? Comment peut-il être amélioré? (Je ne serai probablement pas en mesure de changer le design mais ce sera toujours bon à savoir) – athena

2

Je dirais que oui, vous êtes un usage excessif des réflexions.

Peut-être que vous devriez adopter une approche plus orientée objet

public interface MapMaker <K,V> { 
public Map<K,V> create(); 
} 

public class LazyMap<K,V> implements Map<K,V> { 

private MapMaker<K,V> creation; 
private Map<K,V> theMap = null; 

public LazyMap(MapMaker<K,V> creation) { 
    this.creation=creation; 
} 

protected Map<K,V> getMap() { 
    if(theMap == null) { 
    synchronized(this) { 
     if(theMap == null) { 
     theMap = creation.create(); 
     } 
    } 
    } 
    return theMap; 
} 
//Map interface 
public V get(Object key) { return getMap().get(key); } 
//repeat for all 
} 
+1

Bonne réponse, mais vous implémentez un pattern connu sous le nom de * double check checking * dans 'getMap()' et ce modèle est connu pour échouer comme décrit dans [developerworks] (http://www.ibm.com/developerworks/java/ library/j-dcl.html) – gabuzo

+0

Je sais. Il n'y a pas AFAIK, un moyen décent de le faire, même avec volatile, en Java. – KitsuneYMG

+0

Je viens de faire un peu de navigation sur le sujet et puisque Java 5 a un nouveau [modèle de mémoire] (http://xlct.it/faz2fl), le verrou à double vérification devrait fonctionner sur une JVM Java 5 ou supérieure. Voir aussi à la fin de ce [article] (http://xlct.it/ekzRnO) – gabuzo

2

Vous ne voulez pas charger toutes les cartes, car ils sont trop grandes. Mais en utilisant votre méthode, vous finirez avec tout ce qui sera chargé en mémoire. Vous pouvez jeter un oeil à ehcache qui peut être configuré un système de carte paresseux avec l'expulsion d'élément lorsqu'il n'est plus nécessaire.

+0

+1 pour mentionner qu'une stratégie d'éviction peut également être nécessaire. – Thilo