Pregunta

He estado tratando de encontrar una manera de etiquetar varios métodos de mi clase base, para que una clase cliente pueda llamarlos por etiqueta. El código de ejemplo es:

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

Una clase cliente de un método main () llamará a cada método de Base a través de una secuencia de instrucciones aleatoria:

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

    }

Ahora deseo deshacerme de la instrucción switch del objeto Client. Soy consciente de la técnica para reemplazar el cambio por polimorfismo, pero me gustaría evitar crear un conjunto de nuevas clases. Esperaba simplemente almacenar esos métodos en una estructura de datos adecuada y etiquetarlos de alguna manera con un carácter coincidente de la secuencia.

Un mapa podría almacenar fácilmente objetos con pares valor / clave que podrían hacer el trabajo, (como hice aquí ), o el patrón de comando, pero dado que no quiero reemplazar esos métodos con objetos, ¿existe una forma diferente de almacenar métodos y hacer que un cliente llame selectivamente ellos?

Cualquier consejo es apreciado

¿Fue útil?

Solución

¿Algo así?

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

}

y luego

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

Otros consejos

Yo usaría anotaciones en los métodos en cuestión, permitiendo que se marque como un "método etiquetado" y que proporcione la cadena de la etiqueta que se usará para ese método.

Desde ese punto, la implementación se vuelve más simple; puede usar la reflexión para iterar sobre los métodos de una clase e inspeccionar sus anotaciones; quizás haga esto de forma estática al inicio y rellene un mapeo desde la cadena de la etiqueta hasta java.lang.reflect.Method.

Luego, al procesar la cadena de comandos, invoque los métodos que corresponden a cada etiqueta.

Editar: algún código de ejemplo:

import java.lang.annotation.*; 

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

Luego en la clase 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
   }
}

... y en el cliente:

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

para que pueda acceder a esto como:

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

}

Este código no ha sido compilado ni probado, por cierto ... :-)

Puede usar Atributos para esto, en C #. Para Java, use anotaciones. Derive una clase de la clase Attribute, por ejemplo, TagAttribute, y aplique el atributo a los métodos.

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

Aplicar el atributo a los métodos:

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

Invoque los métodos usando la reflexión:

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.
}

Llame al método CallTaggedMethod desde su método 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();
}

Aquí está mi enfoque de anotaciones. Ni siquiera necesita un Mapa de etiquetas a métodos si está usando anotaciones, simplemente repita la secuencia y busque el método para esa etiqueta utilizando la reflexión.

import java.lang.annotation.*;

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

entonces:

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

entonces

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;
  }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top