与类型层次结构匹配

5
假设我们有一个固定的类型层次结构,如下所示。它是一棵明确定义的树,每个节点都有一个父节点(除了根节点)。

Human taxonomy

每种类型都与一个动作相关联,该动作应在成功匹配后执行。这并不意味着一个动作对应于该类型上的方法。这只是任意的关联。
智能地匹配对象与类型层次结构的方法是什么?每个对象应尽可能地与最具体的类型匹配。这些对象已经被创建。

我心目中的解决方案是遍历一个由类型和相关操作组成的元组树。 - mike
2
不确定我是否理解。如果您拥有属于上述类/类型层次结构的对象和表示操作的方法,则在对象上调用这些方法将自动调用最具体的操作。 - user152468
1
你是否可以控制对象的实例化?如果是这样,你可以使用适配器。 - Jorn Vernee
看起来,如果action()是由超类定义的指定方法,那么在对每个对象引用调用该方法时,action()的预期实现将被调用。不是吗? - Kedar Mhaswade
没有方法。不要关注这个动作。这只是我随意联想的一种无关紧要的联系,与这个问题毫无关系。 - mike
显示剩余7条评论
2个回答

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

我的直觉也是一棵树。这将产生O(log n)个最大匹配。 - mike
最后一个if条件有错误。为了在根级别上获得匹配,您需要将ctx.level == 0 && level == 0level < ctx.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]

也考虑过哈希,但它有一个缺陷,你在第二种方法中试图解决。哈希会丢失类型之间的层次关系。你的第二种方法也有一个小缺陷,因为它不能区分类型(或接口)和实际类。一个类可以实现不同的接口,这将破坏树形结构,因为节点的父节点将不再被定义清楚。 - mike
@mike 在第一种方法中,也会发生超类查找 - 在 getClassTypeOf() 中。我认为已经给定了一个明确定义的类型层次结构,并且您想要与此给定的层次结构进行匹配。如何破坏给定的明确定义的树呢?但是,两种方法都应该能够适应使用查找函数中的 getInterfaces() 的接口的存在。 - Calculator
啊,抱歉我的错误。关于类型:类型和类之间存在差异。或者用Java术语来说,就是接口和类。是的,你的解决方案可以工作,但是@RustyX的解决方案更加稳定,因为它不受实际实现类的影响。 - mike

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接