2017-02-02 8 views
4

Supposons que nous ayons une hiérarchie de type fixe, par ex. comme représenté ci-dessous. C'est un arbre bien défini, où chaque nœud a un parent (à l'exception de la racine).Correspondance par rapport à la hiérarchie de type

Human taxonomy

Chaque type a une action qui lui est associée, qui doit être effectuée lors de l'appariement avec succès. Cela ne pas signifie qu'une action correspond à une méthode sur ledit type. C'est juste une association arbitraire.

Quels sont les moyens intelligents de faire correspondre les objets à une hiérarchie de types? Chaque objet doit être comparé au type le plus spécifique possible. Les objets sont déjà créés.

+0

La solution que j'ai à l'esprit est un arbre qui traverse de tuples de type et de l'action associée. – mike

+0

Voulez-vous vérifier le type d'un objet donné? Pouvez-vous nous donner un exemple de code? –

+2

Je ne suis pas certain de comprendre. Si vous avez des objets appartenant à la hiérarchie classe/type ci-dessus et des méthodes représentant des actions, l'appel de ces méthodes sur les objets appellera automatiquement l'action la plus spécifique. – user152468

Répondre

1

Utilisez la recherche récursive depuis la racine. Une fois qu'aucune correspondance ne peut être trouvée chez les enfants, rappelez-vous l'objet correspondant si son niveau est plus profond que la dernière correspondance.

pseudocode:

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

invoquons comme ceci:

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

Mon intuition était aussi un arbre. Cela donne O (log n) correspond à max. – mike

+0

Il y a une erreur dans la dernière condition if. Afin d'obtenir des correspondances au niveau racine, vous devez combiner 'ctx.level == 0 && level == 0' et' level mike

0

La hiérarchie de l'arborescence est déjà implicitement défini par la déclaration des classes. Vous avez juste besoin de traverser une branche d'arbre via les appels chaînés getSuperclass() au type que vous voulez rechercher. Les nœuds d'arbre (types de classe) peuvent alors être organisés en utilisant une simple carte de hachage.

Étant donné que la hiérarchie de type est statique, vous pouvez le définir comme ENUM

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

puis cartographier les types de classes aux actions:

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  
} 

Voici un autre dans certains termes version plus dynamique

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

} 

La sortie de c'est:

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

Également pensé au hachage, mais il a un défaut que vous avez essayé de résoudre dans votre deuxième approche. Hashing perd la relation hiérarchique entre les types. Votre deuxième approche a également un flux mineur, car elle ne fait pas la distinction entre les types (ou interfaces) et les classes réelles. Une classe pourrait implémenter différentes interfaces, cela détruirait l'arbre, puisque le parent d'un nœud ne serait plus bien défini. – mike

+0

@mike Aussi dans la première approche arrive superclass-lookup - dans 'getClassTypeOf()'. Je pensais qu'il y avait une hiérarchie de types bien définie et que vous voulez faire correspondre cette hiérarchie. Comment un arbre bien défini peut-il être "détruit"? Cependant, les deux approches devraient être adaptables à la présence d'interfaces utilisant 'getInterfaces()' dans les fonctions de recherche. – Calculator

+0

Ah, désolé mon erreur. Relié aux types: il y a une différence entre les types et les classes. Ou en Java termes interfaces et classes. Oui, votre solution fonctionnerait **, mais ** la solution de @RustyX est plus stable, puisqu'elle est invariante aux classes d'implémentation actuelles. – mike