2009-09-09 9 views
5

Tôi đã cố gắng tìm ra cách để gắn thẻ một số phương thức từ lớp cơ sở của mình để lớp khách hàng có thể gọi chúng theo thẻ. Đoạn mã ví dụ là:phương pháp gắn thẻ và gọi chúng từ đối tượng khách hàng theo thẻ

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

Một lớp khách hàng từ một phương pháp main() sẽ gọi mỗi phương pháp của cơ sở thông qua một chuỗi lệnh ngẫu nhiên:

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

    } 

Bây giờ tôi muốn thoát khỏi câu lệnh switch hoàn toàn từ đối tượng Client. Tôi biết kỹ thuật này để thay thế chuyển đổi bằng đa hình, nhưng muốn tránh tạo một tập hợp các lớp mới. Tôi đã hy vọng chỉ đơn giản là lưu trữ những phương pháp đó trong một cấu trúc dữ liệu thích hợp và bằng cách nào đó gắn thẻ chúng với một nhân vật phù hợp từ trình tự.

Bản đồ có thể dễ dàng lưu trữ các đối tượng với cặp giá trị/khóa có thể thực hiện công việc, (như tôi đã làm here) hoặc mẫu lệnh, nhưng vì tôi không muốn thay thế các phương thức đó bằng các đối tượng. cách khác nhau có lẽ, để lưu trữ các phương pháp và có một khách hàng chọn lọc gọi cho họ?

Bất cứ lời khuyên được đánh giá cao

Trả lời

6

Một cái gì đó như thế này?

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

} 

và sau đó

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: Cảm ơn bạn đã xây dựng, vẻ ngoài của nó khá gọn gàng và thẳng thắn – denchr

+0

+1 cho giải pháp đơn giản nhất, mặc dù tôi vẫn nghĩ rằng bỏ qua chi phí phản chiếu và sử dụng mẫu lệnh * sẽ thích hợp hơn. –

+0

Có thể, nhưng tôi nghi ngờ điều đó. Reflection overhead là khá nhỏ những ngày này, và chắc chắn là đủ nhanh cho khá nhiều mọi khuôn khổ java ra khỏi đó. – skaffman

5

Tôi sẽ sử dụng annotations về các phương pháp trong câu hỏi, cho phép nó được đánh dấu là một "phương pháp gắn thẻ", và cung cấp các chuỗi thẻ để sử dụng cho phương pháp đó.

Từ thời điểm đó việc triển khai trở nên đơn giản hơn; bạn có thể sử dụng sự phản chiếu để lặp qua các phương thức của một lớp và kiểm tra các chú thích của chúng; có lẽ làm điều này một cách tĩnh lặng khi khởi động và điền một ánh xạ từ chuỗi thẻ vào java.lang.reflect.Method.

Sau đó, khi xử lý chuỗi lệnh, hãy gọi các phương thức tương ứng với từng thẻ.

Chỉnh sửa: một số mã ví dụ:

import java.lang.annotation.*; 

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

Sau đó, trong lớp cơ sở:

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

... và trong ứng dụng:

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

để bạn có thể truy cập này là:

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

} 

Mã này chưa được biên soạn hoặc thử nghiệm, bằng cách này ... :-)

+0

@dtsazza: Tôi đã không quá quen thuộc với các chú thích rất cảm ơn bạn vì đã mang đến sự chú ý của tôi. Tôi sẵn sàng thử nó chỉ để tìm hiểu thêm về chú thích – denchr

+1

@denchr - bạn được chào đón. Nhìn vào câu trả lời của skaffman, điều này về cơ bản là tương tự (tạo một bản đồ tag cho các đối tượng Method, sau đó thực hiện tra cứu và gọi chúng). Sự khác biệt là ở đây các ánh xạ được khai báo là thuộc tính của chính các phương thức, chứ không phải trong một ánh xạ rõ ràng. Cách tiếp cận của skaffman có lẽ tốt hơn cho các dự án nhỏ hơn với không nhiều phương pháp (vì nó đơn giản hơn để đọc), tôi có thể quy mô tốt hơn rất nhiều. –

+0

Vấn đề với giải pháp này là bạn không thể tách các phương thức khỏi các thẻ của chúng, đó là điểm mạnh hoặc điểm yếu tùy thuộc vào tình huống. – skaffman

1

Bạn có thể sử dụng thuộc tính cho điều này, trong C#. Đối với Java, sử dụng chú thích. Lấy được một lớp từ lớp Attribute, nói, TagAttribute, và áp dụng thuộc tính cho các phương thức.

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

Áp dụng các thuộc tính đến các phương pháp:

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

Gọi các phương pháp sử dụng phản ánh:

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

Gọi CallTaggedMethod từ phương thức Main của bạn:

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

đây là chú thích của tôi Ap proach. Bạn thậm chí không cần một Bản đồ các thẻ cho các phương thức nếu bạn đang sử dụng các chú thích, chỉ cần lặp qua chuỗi và tra cứu phương thức cho thẻ đó bằng cách sử dụng sự phản chiếu.

import java.lang.annotation.*; 

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

thì:

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

sau đó

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

Thực ra, tôi có lẽ nên sửa đổi getMethodForCharacter để sử dụng một số hình thức lưu bộ nhớ đệm – Tarski