2017-02-02 13 views
4

固定型階層があるとします。以下に示すように。これは明確に定義されたツリーで、各ノードには親が1つあります(ルートを除く)。型階層との照合

Human taxonomy

各タイプは、成功したマッチングの際に行うべきであることに関連するアクションを有します。これはではありません。は、アクションが上記タイプのメソッドに対応することを意味します。それは単に任意の関連付けです。

オブジェクトを型階層と照合するインテリジェントな方法は何ですか? 各オブジェクトは、可能な最も具体的なタイプと照合する必要があります。オブジェクトはすでに作成されています。

+0

は私が考えているソリューションは、タイプと関連付けられたアクションのタプルのツリーをトラバースされます。 – mike

+0

指定されたオブジェクトのタイプをチェックしますか?コード例を教えてください。 –

+2

わかりません。上記のクラス/型階層に属するオブジェクトと、アクションを表すメソッドがある場合、オブジェクト上のメソッドを呼び出すと、最も具体的なアクションが自動的に呼び出されます。 – user152468

答えて

1

ルートからの再帰的検索を使用します。

子どもに一致するものが見つからない場合は、レベルが最後の一致よりも深い場合は、一致するオブジェクトを覚えておいてください。

擬似コード:

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

私の直感も木だった。これにより、O(log n)がmaxに一致します。 – mike

+0

最後のif条件にエラーがあります。ルートレベルでのマッチを得るためには、 'ctx.level == 0 && level == 0'と' level mike

0

ツリー階層がすでに暗黙的にクラスの宣言によって定義されます。

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

このようにそれを呼び出します。ルックアップしたいタイプへの呼び出しをgetSuperclass()で呼び出して、1つのツリーブランチをトラバースするだけです。ツリーノード(クラス型)は、単純なハッシュマップを使用して編成できます。

型の階層が静的であることを考えると、あなたは

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

ハッシュについても考えましたが、2番目の方法で解決しようとした欠陥があります。ハッシングは、型間の階層関係を失います。第2のアプローチでは、型(またはインタフェース)と実際のクラスを区別しないため、マイナーフローもあります。クラスは異なるインタフェースを実装することができます。これは、ノードの親がもはや明確に定義されていないため、ツリーを破壊します。 – mike

+0

@mikeまた、最初のアプローチでは、getClassTypeOf()でスーパークラス検索が行われます。私は十分に定義された型階層があり、この階層構造と照合したいと思っていました。与えられた明確に定義された木は、どのようにして「破壊」されますか?しかし、両方のアプローチは、ルックアップ関数で 'getInterfaces()'を使ってインタフェースの存在に適応可能でなければなりません。 – Calculator

+0

ああ、申し訳ありませんが私の間違い。タイプに関連する:タイプとクラスに違いがあります。または、Java用語のインターフェースとクラス。はい、あなたのソリューションは動作しますが、**実際の実装クラスに対して不変なので、@RustyXのソリューションはより安定しています。 – mike

関連する問題