marcação métodos e chamá-los a partir de um objeto cliente por tag
-
05-07-2019 - |
Pergunta
I foram tentando descobrir uma maneira de tag vários métodos de minha classe base, de modo que uma classe cliente pode chamá-los pelo tag. O código de exemplo é:
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
}
}
Uma classe de cliente a partir de um método principal () vai chamar cada um método de base através de uma sequência de instruções aleatória:
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());
}
Agora eu gostaria de se livrar da instrução switch completamente do objeto cliente. Estou ciente da técnica para substituir um detector de polimorfismo, mas gostaria de evitar a criação de um conjunto de novas classes. Eu estava esperando para simplesmente armazenar os mesmos métodos com uma estrutura de dados apropriada e de alguma forma marcá-las com um caractere correspondente da sequência.
Um mapa poderia facilmente armazenar objetos com valor de pares /-chave que poderiam fazer o trabalho, (como eu fiz aqui ), ou o padrão de comando, mas desde que eu não quero substituir os métodos com objetos, há uma maneira diferente, talvez, aos métodos de loja e ter um cliente chamada seletivamente -los?
Qualquer conselho é apreciada
Solução
Algo como isso?
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() {}
}
e
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);
}
}
Outras dicas
Gostaria de usar anotações sobre os métodos em questão, permitindo que ele seja marcado como um "método com a tag", e fornecendo a string tag para utilização para esse método.
A partir desse ponto a implementação fica mais simples; você pode usar a reflexão para iterar sobre uma classe de métodos e inspecionar suas anotações; talvez fazer isso estaticamente na inicialização e preencher um mapeamento de cadeia de tag para java.lang.reflect.Method.
Em seguida, ao processar a seqüência de comandos, chamar os métodos que correspondem a cada tag.
Edit: alguns códigos de exemplo:
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@interface TaggedMethod {
String tag();
}
Em seguida, na classe 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
}
}
... e no 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);
}
}
}
de modo que você pode acessar esta 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 não foi compilado ou testado, pelo caminho ...: -)
Você pode usar atributos para isso, em C #. Para Java, usar anotações. Derivar uma classe de classe de atributo, dizer, TagAttribute, e aplicar o atributo para os 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 o atributo para os 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"); }
}
chamar os métodos que utilizam reflexão:
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.
}
Chamar o CallTaggedMethod do seu método principal:
static void Main(string[] args)
{
String sequence = "ABCAABBBABACCACC";
MyClass inst = new MyClass();
foreach(char c in sequence)
CallTaggedMethod(inst, c);
// The rest.
Console.ReadLine();
}
Aqui está o meu anotações Approach. Você não precisa sequer de um Mapa de etiquetas para os métodos se você estiver usando anotações, apenas iterar sobre a sequência e procurar o método para que tag usando a reflexão.
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Tag {
char value();
}
então:
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();
}
}
então
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;
}
}