簡體   English   中英

如何為類似的Ruby功能編寫最佳的Java代碼?

[英]How to write the best possible Java code for a similar Ruby functionality?

我有一些Java經驗,正在學習Ruby。 我遇到了如下的Ruby程序:

class Tree
  attr_accessor :children, :node_name
  def initialize(name, children=[])
    @children = children
    @node_name = name
  end
  def visit_all(&block)
    visit &block
    children.each {|c| c.visit_all &block}
  end
  def visit(&block)
    block.call self
  end
end
ruby_tree = Tree.new( "Ruby" ,
                      [Tree.new("Reia" ),
                       Tree.new("MacRuby" )] )
puts "Visiting a node"
ruby_tree.visit {|node| puts node.node_name}
puts
puts "visiting entire tree"
ruby_tree.visit_all {|node| puts node.node_name}

當我查看ruby語言的強大功能時,我想到了用Java編寫類似的代碼,如下所示:

public class Tree {

    private String name;
    private Tree[] children;

    Tree(String name, Tree[] children) {
        this.name = name;
        this.children = children;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Tree[] getChildren() {
        return children;
    }

    public void setChildren(Tree[] children) {
        this.children = children;
    }

    public static void main(String[] args) {
        Tree myTree = new Tree("Ruby", new Tree[] {
                new Tree("Reia", new Tree[] {}),
                new Tree("MacRuby", new Tree[] {}) });
        myTree.visit();
        myTree.visit_all();
    }

    public void visit() {
        System.out.println(getName());
    }

    public void visit_all() {
        visit();
        for (Tree tree : children) {
            tree.visit();
        }
    }
}

問題:我知道這里的Java版本不如Ruby靈活,是否可以像ruby一樣提供Java類似的功能來達到靈活性呢?

首先,請注意:該代碼絕對可怕。 它幾乎不提供封裝,它左右泄漏實現細節, Tree對象無法維護其自己的不變式或狀態。 其次,它不會在所有 Ruby的集合框架集成。

結果,我的Java翻譯同樣令人恐懼,並且沒有與Java的收集框架集成。

與Ruby相比,您的Java代碼的兩個最大缺點是

  • 在Java版本中,元素類型被硬編碼為String ,而在Ruby版本中,它可以是任何對象,甚至可以是同一棵樹中對象的混合,並且
  • 在Java版本中,迭代器被硬編碼以打印名稱,而在Ruby版本中,迭代器采用帶有代碼的block參數來執行。

第一個問題在Java中無法輕易解決。 您可以使集合具有通用性,以便它可以容納任何類型的元素,但是使其具有異構性(即能夠在同一集合中容納不同類型的元素)將需要很多工作。 因此,我堅持使用部分解決方案:使Tree通用。

第二個問題可以通過使迭代器采用包含代碼的對象來解決。 畢竟,一類子例程與僅使用一個方法的對象基本相同。 (Java 8將減輕一些痛苦,我在代碼中包含了示例。)

import java.util.Collection;
import java.util.ArrayList;

interface Consumer<T> {
    void accept(T e);
}
// In Java 8, this interface is already part of the JRE.
// Just replace the 3 lines above with this import:
//import java.util.function.Consumer;

class Tree<T> {
    private String nodeName;
    private Collection<Tree<T>> children = new ArrayList<>();

    Tree(String name, Collection<Tree<T>> children) {
        nodeName = name;
        this.children = children;
    }

    Tree(String name) {
        nodeName = name;
    }

    public String getNodeName() { return nodeName; }
    public void setNodeName(String name) { nodeName = name; }

    public Collection<Tree<T>> getChildren() { return children; }
    public void setChildren(Collection<Tree<T>> children) { this.children = children; }

    void visitAll(Consumer<Tree<T>> block) {
        visit(block);
        for (Tree<T> tree : children) tree.visitAll(block);
    }

    void visit(Consumer<Tree<T>> block) {
        block.accept(this);
    }

    public static void main(String... args) {
        ArrayList<Tree<String>> children = new ArrayList<>();
        children.add(new Tree<String>("Reia"));
        children.add(new Tree<String>("MacRuby"));
        Tree<String> rubyTree = new Tree<>("Ruby", children);

        System.out.println("Visiting a node");
        rubyTree.visit(new Consumer<Tree<String>>() {
            public void accept(Tree<String> node) {
                System.out.println(node.getNodeName());
            }
        });
        // In Java 8, you can use a lambda.
        // Just replace the 5 lines above with this line:
        //rubyTree.visit(node -> System.out.println(node.getNodeName()));

        System.out.println();

        System.out.println("Visiting entire tree");
        rubyTree.visitAll(new Consumer<Tree<String>>() {
            public void accept(Tree<String> node) {
                System.out.println(node.getNodeName());
            }
        });
        // In Java 8, you can use a lambda.
        // Just replace the 5 lines above with this line:
        //rubyTree.visitAll(node -> System.out.println(node.getNodeName()));
    }
}
def visit(&block)
  block.call self
end

更好地寫為

def visit
  yield self
end

而且, visit_allvisit會更習慣地寫為符合Enumerable模塊:

class Tree
  include Enumerable

  # ...

  def each(&cb)
    cb.call(@element)
    children.each end |child|
      child.each(&cb) if child.respond_to?(:each)
    end
  end
end

這樣,您可以免費獲得各種其他東西,例如max ...,而且,每個人都知道each元素都會對所有元素應用一個塊,而他們將不得不仔細閱讀您的API文檔或代碼以查看該函數是否被調用visit_all


編輯 :因為我顯然是個白痴,所以刪除了一大塊。 感謝steenslag使我正確。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM