【问题标题】:Generalizing actions for a binary tree traversal?泛化二叉树遍历的动作?
【发布时间】:2013-02-24 18:20:16
【问题描述】:

我正在尝试找到一种方法,我可以采用二叉树类并遍历其节点,
在每个节点上执行 X 数量的内联操作,而无需重写相同的操作一遍又一遍的遍历代码。
我想如果 Java 允许函数指针,这对我来说会更容易弄清楚...

基本上,我需要的是以下内容:

public class BinaryTreeNode {

    //...

    public void inOrderTraversalFrom(BinaryTreeNode node, /* ??? */ actions) {
        if(node.left != null)
            inOrderTraversalFrom(node.left);

        /* do something with "actions" here */

        if(node.right != null)
            inOrderTraversalFrom(node.right);
    }
}


... actions 可以允许执行不同的方法,采用不同的参数集,具体取决于要在每个节点上执行的操作类型.

一个很好的例子是可以传递一个旨在绘制这些节点的类,接受一个 Graphics 对象作为它的参数之一,而一个旨在执行一些其他一系列动作的类不会需要一个 Graphics 对象作为参数,而是一组完全不同的参数。

这怎么可能?执行此操作的最动态方式是什么?

【问题讨论】:

  • @MitchWheat 非常有趣,我有时会使用它...但它仍然不能解决必须重写相同的inOrderTraversalFrom 方法来接受不同接口的问题。也许可以在这里结合使用泛型?

标签: java function generics methods binary-tree


【解决方案1】:

我找到了一个解决方案,虽然它可能不是最好的...

BinaryTreeNode.java:

public void inOrderTraversalFrom(BinaryTreeNode rootNode, BinaryTreeActions actions)
{
    if(rootNode.left != null)
        inOrderTraversalFrom(rootNode.left, actions);

    if(actions != null)
        actions.Perform(rootNode);

    if(rootNode.right != null)
        inOrderTraversalFrom(rootNode.right, actions);
}


BinaryTreeActions.java:

public interface BinaryTreeActions {
    public void Perform(BinaryTreeNode node);
}


BinaryTreeGraphicsActions.java:

public interface BinaryTreeGraphicsActions extends BinaryTreeActions {
    void DrawNode(BinaryTreeNode node, Graphics g);
}


BinaryTreeView.java:

private void DrawNodes(final Graphics graphics)
{
    BinaryTreeNode node = root;
    root.inOrderTraversalFrom(node, new BinaryTreeGraphicsActions() {
        @Override
        public void DrawNode(BinaryTreeNode node, Graphics g) {
            // draw the node
        }

        @Override
        public void Perform(BinaryTreeNode node) {
            DrawNode(node, graphics);
        }
    });
}


...并且任何时候您需要一组新的操作,您都可以为它创建一个新界面,遵循BinaryTreeGraphicsActionsBinaryTreeView 中的相同想法。这允许您执行任何一组操作,具体取决于您为它们设计的界面。


#EDIT : 在发现不需要BinaryTreeGraphicsActions 后,我接受了类似的答案,因为您可以像这样简单地使用 BinaryTreeActions 来执行相同的内联代码:

private void DrawNodes(final Graphics graphics)
{
    BinaryTreeNode node = root;
    root.inOrderTraversalFrom(node, new BinaryTreeActions() {
        @Override
        public void Perform(BinaryTreeNode node) {
            /* draw the node, using any local vars (providing they are final) */
        }
    });
}

【讨论】:

    【解决方案2】:

    以下解决方案与此处建议的其他解决方案非常相似。但是,如果您喜欢这些东西,请使用一些设计模式糖霜。

    这个想法不仅要隔离动作,还要隔离遍历。这可以通过以下 2 个抽象来完成。

    • BinaryTreeNodeTraversalStrategy
    • BinaryTreeNodeVisitor


    public interface Visitable<T extends Visitor> {
    
      public void accept(T visitor);
    }
    
    public interface Visitor<T extends Visitable> {
    
      public void visit(T visitable);
    }
    
    public interface BinaryTreeNode implements Visitable<BinaryTreeNodeVisitor> {
    
      public void accept(BinaryTreeNodeVisitor visitor);
    }
    
    public interface BinaryTreeNodeVisitor implements Visitor<BinaryTreeNode> {
    
      public void visit(BinaryTreeNode visitable);
    }
    
    public interface BinaryTreeNodeTraversalStrategy {
    
      public void traverse(BinaryTreeNode root);
    }
    

    【讨论】:

      【解决方案3】:

      一种方法是:

      action 可以是接收Node 并对其应用Action 的类的实例:

      public interface Action{
        public void apply(Node node){
        // To be done in the classes that will implement 
        // this interface. If it's a graphic-display of the nodes, the apply 
        // method will call draw(node.value), for example
        }
      }
      

      行:

      /* do something with "actions" here */
      

      应该替换为

      action.apply(node);
      

      方法的签名应该改为:

      public void inOrderTraversalFrom(BinaryTreeNode node, Action action)
      

      递归调用应该是:

      inOrderTraversalFrom(node.left, action);
      

      【讨论】:

      • 这与我想要的非常接近,但不接受要执行的特定操作的不同参数集。
      • 您发布的答案与我的建议非常接近:Preform 是 Apply 的另一个名称。唯一的区别是您的解决方案提供了另一层抽象,当您想在解决方案中使用外部库时需要该抽象层(问题中没有提到的东西)。只要你实现了图形动作——你可以直接在你的类中实现apply()方法,你不需要这个额外的抽象级别。
      • 我离开了 Mitch Wheat 早些时候提供的关于 Java 中的函数指针的链接。我们的代码是巧合,因为我在刷新页面之前发布了我的代码。而且我确实理解您现在的意思,但它不允许执行内联的操作(我的问题确实问过)。例如,如果BinaryTreeView 要实现BinaryTreeActions,我将如何将Graphics 对象传递给DrawNode?在它已经传递给BinaryTreeView.DrawNodes 之前不会有一个可以传递,因为 DrawNodes 是从覆盖的 paint 方法调用的......
      • @RectangleEquals 如果我理解正确,您只是提供了第二个非常好的理由来添加另一层抽象:如果“内联”表示您想要级联方法(即g.set(node).color('red').setX(something).setY(somethingElse)...),那么无论如何,是的,添加另一层抽象是完全有意义的。干得好!
      • 是的,当我说你不需要另一个抽象级别时,这就是我的意思;)但是,如果你必须使用不“知道”如何使用的 external BinaryTreeActions要使用节点,那么您需要像您最初建议的那样扩展它(创建另一个知道如何连接图形和节点的抽象级别)
      猜你喜欢
      • 2012-01-01
      • 2022-11-11
      • 1970-01-01
      • 2021-11-20
      • 1970-01-01
      • 2021-03-08
      • 2020-01-15
      • 1970-01-01
      相关资源
      最近更新 更多