2009-09-09 7 views
5

J'ai essayé de trouver un moyen d'étiqueter plusieurs méthodes de ma classe de base, de sorte qu'une classe de client peut les appeler par balise. Le code exemple est:les méthodes de marquage et les appeler à partir d'un objet client par tag

public class Base { 
     public void method1(){  
     ..change state of base class 
    } 

    public void method2(){  
     ..change state of base class 
    } 

    public void method3(){  
     ..change state of base class 
    } 
} 

Une classe de client à partir d'une méthode principale() appellera chaque méthode de base à travers une séquence d'instructions au hasard:

public static void main(String[] args) { 
String sequence = "ABCAABBBABACCACC" 
Base aBase = new Base(); 
for (int i = 0; i < sequence.length(); i++){ 
      char temp = sequence.charAt(i); 
      switch(temp){ 
      case 'A':{aBase.method1(); break;} 
      case 'B':{aBase.method2(); break;} 
      case 'C':{aBase.method3(); break;}   } 
     } 

     System.out.println(aBase.getState()); 

    } 

Maintenant, je tiens à vous débarrasser de l'instruction switch tout à fait de l'objet Client. Je suis conscient de la technique pour remplacer le commutateur par le polymorphisme, mais souhaitez éviter créer un ensemble de nouvelles classes. J'espérais simplement stocker ces méthodes dans une structure de données appropriée et les marquer d'une manière ou d'une autre avec un caractère correspondant de la séquence. Une carte pourrait facilement stocker des objets avec des paires valeur/clé qui pourraient faire le travail, (comme je l'ai fait here), ou le modèle de commande, mais puisque je ne veux pas remplacer ces méthodes par des objets, y a-t-il un peut-être de façon différente, pour stocker les méthodes et demander à un client de les appeler de façon sélective?

Tout conseil est apprécié

Répondre

6

Quelque chose comme ça?

public class Base { 

    private final Map<Character, Method> methods = new HashMap<Character, Method>(); 

    public Base() throws SecurityException, NoSuchMethodException { 
     methods.put('A', getClass().getMethod("method1")); 
     methods.put('B', getClass().getMethod("method2")); 
     methods.put('C', getClass().getMethod("method3")); 
    } 

    public Method getMethod(char c) { 
     return methods.get(c); 
    } 

    public void method1() {} 

    public void method2() {} 

    public void method3() {} 

} 

puis

public static void main(String[] args) throws Exception { 
     String sequence = "ABCAABBBABACCACC"; 
     Base aBase = new Base(); 

     for (int i = 0; i < sequence.length(); i++) { 
      char temp = sequence.charAt(i); 
      aBase.getMethod(temp).invoke(aBase); 
     } 
    } 
+0

@skaffman: Merci pour l'élaboration, son apparence est plutôt nette et droite – denchr

+0

+ 1 pour la solution la plus simple, bien que je pense toujours que le fait d'abandonner le reflet et d'utiliser le motif de commande * serait préférable. –

+0

Peut-être, mais j'en doute. La surcharge de réflexion est assez faible ces temps-ci, et elle est certainement assez rapide pour pratiquement tous les frameworks Java. – skaffman

5

j'utiliser annotations sur les méthodes en question, lui permettant d'être marqué comme une « méthode marquée » et fournir la chaîne de balise à utiliser cette méthode.

De ce point, l'implémentation devient plus simple; vous pouvez utiliser la réflexion pour parcourir les méthodes d'une classe et inspecter leurs annotations; peut-être le faire de façon statique au démarrage et remplir un mappage de la chaîne de balise vers java.lang.reflect.Method.

Ensuite, lors du traitement de la chaîne de commande, appelez les méthodes correspondant à chaque variable.

Edit: quelques exemples de code:

import java.lang.annotation.*; 

@Retention(RetentionPolicy.RUNTIME) 
@interface TaggedMethod { 
    String tag(); 
} 

Ensuite, dans la classe de base:

public class Base { 

    @TaggedMethod(tag = "A") 
    public void method1(){   
    ..change state of base class 
    } 

    @TaggedMethod(tag = "B") 
    public void method2(){    
    ..change state of base class 
    } 

    @TaggedMethod(tag = "C") 
    public void method3(){    
    ..change state of base class 
    } 
} 

... et le client:

private static final Map<String, Method> taggedMethods = new HashMap<String, Method>(); 

// Set up the tag mapping 
static 
{ 
    for (Method m : Base.class.getDeclaredMethods()) 
    { 
     TaggedMethod annotation = m.getAnnotation(TaggedMethod.class) 
     if (annotation != null) 
     { 
     taggedMethods.put(annotation.tag(), m); 
     } 
    } 
} 

afin que vous puissiez accéder à ceci comme:

public static void main(String[] args) throws Exception 
{ 
    String sequence = "ABCAABBBABACCACC" 
    Base aBase = new Base(); 
    for (int i = 0; i < sequence.length(); i++) 
    { 
      String temp = sequence.substring(i,1); 
      Method method = taggedMethods.get(temp); 
      if (method != null) 
      { 
       // Error handling of invocation exceptions not included 
       method.invoke(aBase); 
      } 
      else 
      { 
       // Unrecognised tag - handle however 
      } 
    } 

    System.out.println(aBase.getState()); 

} 

Ce code n'a pas été compilé ou testé, au fait ... :-)

+0

@dtsazza: Je n'étais pas trop familier avec annotations afin que vous remercier d'avoir attiré mon attention. Je serais heureux de l'essayer juste pour en savoir plus sur les annotations – denchr

+1

@denchr - de rien. En regardant la réponse de Skaffman, ceci est essentiellement similaire (créer une carte de tag pour les objets Method, puis faire la recherche et les invoquer). La différence est qu'ici les mappages sont déclarés comme propriétés des méthodes elles-mêmes, plutôt que dans un mappage explicite. L'approche de skaffman est probablement meilleure pour les petits projets avec peu de méthodes (car c'est plus simple à lire), la mienne est probablement bien meilleure. –

+0

Le problème avec cette solution est que vous ne pouvez pas découpler les méthodes de leurs balises, ce qui est soit une force ou une faiblesse en fonction de la situation. – skaffman

1

Vous pouvez utiliser des attributs pour cela, en C#. Pour Java, utilisez les annotations. Dérivez une classe de la classe Attribute, par exemple, TagAttribute, et appliquez l'attribut aux méthodes.

[global::System.AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)] 
public sealed class TagAttribute : Attribute 
{ 
    public TagAttribute(char value) 
    { 
     this.value = value; 
    } 

    private char value; 
    public char Value 
    { 
     get { return value; } 
    } 
} 

Appliquer l'attribut aux méthodes:

public class MyClass 
{ 
    [Tag('A')] 
    public void Method1() 
    { Console.Write("a"); } 

    [Tag('B')] 
    public void Method2() 
    { Console.Write("b"); } 

    [Tag('C')] 
    public void Method3() 
    { Console.Write("c"); } 
} 

Invoquer les méthodes utilisant la réflexion:

private static void CallTaggedMethod(MyClass instance, char value) 
{ 
    MethodInfo methodToCall = null; 

    // From the MyClass type... 
    Type t = typeof(MyClass); 
    // ...get all methods. 
    MethodInfo[] methods = t.GetMethods(); 
    // For each method... 
    foreach (MethodInfo mi in methods) 
    { 
     // ...find all TagAttributes applied to it. 
     TagAttribute[] attributes = (TagAttribute[])mi.GetCustomAttributes(typeof(TagAttribute), true); 
     if (attributes.Length == 0) 
      // No attributes, continue. 
      continue; 
     // We assume that at most one attribute is applied to each method. 
     TagAttribute attr = attributes[0]; 
     if (attr.Value == value) 
     { 
      // The values match, so we call this method. 
      methodToCall = mi; 
      break; 
     } 
    } 

    if (methodToCall == null) 
     throw new InvalidOperationException("No method to call."); 

    object result = methodToCall.Invoke(
     // Instance object 
     instance, 
     // Arguments 
     new object[0]); 

    // 'result' now contains the return value. 
    // It is ignored here. 
} 

Appelez le CallTaggedMethod de votre méthode principale:

static void Main(string[] args) 
{ 
    String sequence = "ABCAABBBABACCACC"; 
    MyClass inst = new MyClass(); 

    foreach(char c in sequence) 
     CallTaggedMethod(inst, c); 

    // The rest. 

    Console.ReadLine(); 
} 
0

ici est mes annotations Ap proach. Vous n'avez même pas besoin d'une carte des étiquettes pour les méthodes si vous utilisez des annotations, il suffit de parcourir la séquence et de rechercher la méthode pour cette étiquette en utilisant la réflexion.

import java.lang.annotation.*; 

@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.METHOD) 
public @interface Tag { 
    char value(); 
} 

alors:

public class Base { 

    StringBuilder state = new StringBuilder(); 

    @Tag('A') 
    public void method1(){   
     state.append("1"); 
    } 

    @Tag('B') 
    public void method2(){    
    state.append("2"); 
    } 

    @Tag('C') 
    public void method3(){    
    state.append("3"); 
    } 

    public String getState() { 
    return state.toString(); 
    } 
} 

puis

public final class TagRunner { 

    private TagRunner() { 
     super(); 
    } 

    public static void main(String[] args) throws IllegalArgumentException, 
    IllegalAccessException, InvocationTargetException { 
     Base b = new Base(); 
     run(b, "ABCAABBBABACCACC"); 
     System.out.println(b.getState()); 
    } 

    private static <T> void run(T type, String sequence) throws 
    IllegalArgumentException, IllegalAccessException, InvocationTargetException { 
     CharacterIterator it = new StringCharacterIterator(sequence); 
     Class<?> taggedClass = type.getClass(); 

     for (char c = it.first(); c != CharacterIterator.DONE; c = it.next()) { 
     getMethodForCharacter(taggedClass, c).invoke(type);  
     } 
    } 

    private static Method getMethodForCharacter(Class<?> taggedClass, char c) { 
     for (Method m : taggedClass.getDeclaredMethods()) { 
     if (m.isAnnotationPresent(Tag.class)){ 
      char value = m.getAnnotation(Tag.class).value(); 
      if (c == value) { 
       return m; 
      } 
     }  
     } 

    //If we get here, there are no methods tagged with this character 
    return null; 
    } 
} 
+0

En fait, je devrais probablement modifier getMethodForCharacter pour utiliser une forme de cache – Tarski

Questions connexes