2017-02-02 159 views
4

假設我們有一個固定的類型層次結構,例如如下所示。它是一個定義良好的樹,每個節點都有一個父節點(除了根節點)。匹配類型層次結構

Human taxonomy

每種類型都有一個與其相關聯的動作,這應該在成功匹配來執行。這不是而不是意味着動作對應於所述類型的方法。這只是任意的關聯。

將對象與類型層次結構進行匹配的智能方法是什麼? 每個對象應與可能的最具體類型進行匹配。對象已經創建。

+0

的解決方案,我已經記穿越類型和相關的行動的元組的一棵樹。 – mike

+0

你想檢查給定對象的類型嗎?你能給我們一個代碼示例嗎? –

+2

不知道我明白。如果您有屬於上述類/類型層次結構的對象以及表示動作的方法,那麼在這些對象上調用這些方法將自動調用最具體的動作。 – user152468

回答

1

使用來自根的遞歸搜索。

一旦在兒童中找不到匹配,請記住匹配的對象,如果它的等級比最後一匹配更深。

僞代碼:

class MatchContext { 
     public int level; 
     public Node result; 
    } 

    public boolean match(Node object, int level, MatchContext ctx) { 
     if (no match) 
      return false; 
     boolean found = false; 
     for (all children in object) { 
      if (match(child, level + 1, ctx)) 
       found = true; 
     } 
     if (!found && level > ctx.level) { 
      ctx.level = level; 
      ctx.result = this; 
     } 
     return found; 
    } 

調用它像這樣:

MatchContext ctx; 
    if (match(root, 0, ctx)) 
     myAction(ctx.result); 
+0

我的直覺也是一棵樹。這產生O(log n)匹配最大值。 – mike

+0

最後一個條件存在錯誤。爲了在根級別獲得匹配,您需要將'ctx.level == 0 && level == 0'和'level mike

0

樹狀分級結構已經隱含在類的聲明中定義。你只需要通過鏈接getSuperclass()調用你想要查找的類型遍歷一個樹分支。然後可以使用簡單的哈希映射來組織樹節點(類類型)。

鑑於該類型層次結構是靜態的,你可以把它定義爲枚舉

public enum ClassType{ 
    HOMINOIDEA(HOMINOIDEA.class), 
    HOMINIDAE(HOMINIDAE.class), 
    HOMININAE(HOMININAE.class), 
    //and so on 
    UNKNOWN(null);  

    private static final Map<Class<?>, ClassType> typesMap = new HashMap<>(); 
    public final Class<?> type; 

    static{ 
     for (ClassType classType : EnumSet.allOf(ClassType.class)){    
      if(classType.type != null){ 
       typesMap.put(classType.type, classType); 
      } 
     } 
    } 

    private ClassType(Class<?> type){ 
     this.type = type;   
    } 

    public static ClassType getClassTypeOf(Class<?> type){ 
     for(Class<?> lookupType = type; lookupType != null; lookupType = lookupType.getSuperclass()){ 
      ClassType classType = typesMap.get(lookupType); 
      if(classType != null){ 
       return classType; 
      } 
     }  
     return UNKNOWN; 
    } 
} 

,然後映射類類型的行動:

public static void main(String[] args){ 
    EnumMap<ClassType, Action> actionMap = new EnumMap<>(ClassType.class); 
    actionMap.put(ClassType.HOMININAE, new HomininaeAction()); 
    Homininae h = new Homininae(); 
    actionMap.get(ClassType.getClassTypeOf(h)); //action associated with homininaes  
} 

這裏是一個其他在某些方面更動態版本

public class ActionDispatcher { 
    private final Map<Class<?>, Consumer<?>> actionMap = new HashMap<>(); 

    public <T> void registerAction(Class<T> type, Consumer<? super T> action){ 
     actionMap.put(type, action); 
    }  

    @SuppressWarnings("unchecked") 
    public void dispatchActionFor(Object object){ 
     Consumer<Object> action = ((Consumer<Object>)getActionFor(object.getClass())); 
     if(action != null){ 
      action.accept(object); 
     } 
    } 

    private Consumer<?> getActionFor(Class<?> type){   
     for(Class<?> lookupType = type; lookupType != null; lookupType = lookupType.getSuperclass()){ 
      Consumer<?> action = actionMap.get(lookupType); 
      if(action != null){ 
       return action; 
      } 
     }  
     return null; 
    } 

    //demo 
    public static void main(String[] args){ 
     ActionDispatcher dispatcher = new ActionDispatcher(); 
     dispatcher.registerAction(Number.class, n -> System.out.println("number: " + n)); 
     dispatcher.registerAction(Double.class, d -> System.out.println("double: " + d)); 
     dispatcher.registerAction(String.class, s -> System.out.println("first char: " + s.charAt(0))); 
     dispatcher.registerAction(Object.class, o -> System.out.println("object: " + o)); 

     dispatcher.dispatchActionFor(new Integer(3)); 
     dispatcher.dispatchActionFor(new Double(3.0)); 
     dispatcher.dispatchActionFor("string"); 
     dispatcher.dispatchActionFor(new Thread()); 
    } 

} 

的這個輸出是:

number: 3 
double: 3.0 
first char: s 
object: Thread[Thread-0,5,main] 
+0

也想過哈希,但是有一個缺點,你嘗試在第二種方法中解決。哈希失去了類型之間的層次關係。你的第二種方法也有一個小流程,因爲它不區分類型(或接口)和實際類。一個類可以實現不同的接口,這會破壞樹,因爲節點的父節點不再被定義好。 – mike

+0

@mike第一種方法在'getClassTypeOf()'中發生超類查找。我認爲有一個定義良好的類型層次結構,你想匹配這個給定的層次結構。一棵明確定義的樹如何被「摧毀」?但是,兩種方法都應該適用於在查找函數中使用'getInterfaces()'的接口。 – Calculator

+0

啊,對不起我的錯誤。與類型相關:類型和類之間存在差異。或者用Java來表示接口和類。是的,你的解決方案可以工作,但是** @RustyX的解決方案更穩定,因爲它對於實際的實現類是不變的。 – mike