Question

J'ai essayé de trouver un moyen de baliser plusieurs méthodes de ma classe de base, afin qu'une classe client puisse les appeler par balise. Le code exemple est:

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 d'une méthode main () appellera chaque méthode de Base via une séquence d'instructions aléatoires:

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

    }

Je souhaite maintenant supprimer l’instruction switch de l’objet Client. Je suis conscient de la technique permettant de remplacer switch par polymorphisme, mais je voudrais éviter de 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 étiqueter avec un caractère correspondant de la séquence.

Une carte pourrait facilement stocker des objets avec des paires valeur / clé pouvant faire le travail (comme je le faisais ici ), ou le modèle de commande, mais comme je ne veux pas remplacer ces méthodes par des objets, existe-t-il un moyen différent de stocker des méthodes et de faire appeler sélectivement un client eux?

Tous les conseils sont appréciés

Était-ce utile?

La solution

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() {}

}

et ensuite

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

Autres conseils

Je voudrais utiliser des annotations sur les méthodes en question, ce qui permet de le marquer comme "méthode étiquetée" et de fournir la chaîne de balises à utiliser pour cette méthode.

À partir de ce moment, la mise en œuvre 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 manière statique au démarrage et renseigner un mappage d'une 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 balise.

Modifier: un exemple de code:

import java.lang.annotation.*; 

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

Puis 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 dans 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 y accéder en tant que:

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é ni testé, soit dit en passant ...: -)

Vous pouvez utiliser des attributs pour cela, en C #. Pour Java, utilisez des annotations. Dérivez une classe de la classe Attribute, par exemple, TagAttribute, et appliquez cet 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; }
    }
}

Appliquez 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 CallTaggedMethod à partir de votre méthode Main:

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

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

    // The rest.

    Console.ReadLine();
}

Voici mon approche des annotations. Si vous utilisez des annotations, vous n'avez même pas besoin de mapper les balises sur les méthodes. Il vous suffit de parcourir la séquence et de rechercher la méthode pour cette balise à l'aide de 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;
  }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top