且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

在javaFx中,为什么我们需要调用“getChildren()”当我们向窗口添加元素时?

更新时间:2023-12-01 17:42:10

简短的回答就是你必须这样做,因为这就是API的编写方式。当然,您可能真正要问的是为什么API就是这样编写的。我认为这真的相当于两个(相关的)问题。一个是关于方法名称,以及该方法的作用,一个是关于实际的API设计。

The short answer is simply "you have to do it that way, because that's how the API was written". Of course, what you are probably really asking is why the API was written like that. I think that really amounts to two (related) questions. One is about the method name, and what that method does, and one is about the actual API design.

通常,在计算中,重用现有的通常是有益的。问题的解决方案。 (知道何时执行此操作是有益的,但在这两种情况下,这显然都是一种好处。)这种API设计以两种不同的方式重用现有解决方案来解决众所周知的问题,从而使其成为现实的解决方案。更容易理解和使用之前遇到过这些解决方案的程序员。

In general, in computing, it is often beneficial to reuse existing solutions to problems. (Knowing when it is beneficial to do this is something of an art, but in both the cases here it's pretty clearly a benefit.) In two somewhat different ways, this API design reuses existing solutions to well-known problems, and consequently makes it easier to understand and use for programmers who have encountered those solutions before.

从整体来看,考虑一般用户界面的整体结构。有一个大容器(想想JavaFX中的场景的根),它包含各种UI组件。其中一些可能是简单的控件,如按钮和标签等,其中一些可能是其他容器,而其他容器又包含各种UI组件。当然,其中一些也可能是容器,等等。在不强加某种结构的情况下,这可能变得复杂且难以使用。

For the big picture, think about the overall structure of a general user interface. There is a large container of some kind (think of the root of the scene in JavaFX), which contains various UI components. Some of those might be simple controls, such as buttons and labels, etc, and some of them might be other containers, which in turn contain various UI components. And some of those, of course, may also be containers, and so on. Without imposing some kind of structure, this could get complex and difficult to use.

为了理解结构,我们将其抽象化。有一个根节点,其中包含零个或多个其他节点的集合。其中每个都有零个或多个其他节点的集合,依此类推。这是一个众所周知的计算抽象结构,称为树,基本上每个计算机程序员(无论他们编程的语言)都熟悉。因此,如果我们将其视为树结构,因为我们已经熟悉它,我们可以使复杂性更容易使用。为了将其视为树结构,我们对树状方面使用标准名称。每个节点(根节点除外)只有一个父(它所属的容器):所以你会在节点类(节点中找到一个方法是树结构的另一个术语,名为 getParent() ,它允许访问父节点(包含当前节点的容器)。类似地,包含其他节点的UI元素(节点)具有一个名为 getChildren()的方法,该方法返回当前节点中包含的所有节点的集合。 Oracle JavaFX教程有一节介绍场景描述这个的图表,包含许多漂亮的图表和相关代码。

To understand the structure, we make it abstract. There is a root node, which has a collection of zero or more other nodes. Each of those has a collection of zero or more other nodes, and so on. This is a very well known abstract structure in computing, called a "tree", which basically every computer programmer (no matter which language they program in) is familiar with. So if we think of it as a tree structure, because we are already familiar with that, we can make the complexity easier to work with. In order to think of it as a tree structure, we use standard names for the tree-like aspects. Each node (except the root node) has exactly one "parent" (the container to which it belongs): so you'll find a method in the Node class ("node" is another piece of terminology from tree structures) called getParent(), which gives access to the parent node (the container containing the current node). Similarly, UI elements (nodes) that contain other nodes have a method called getChildren() that returns a collection of all the nodes contained in the current node. The Oracle JavaFX tutorial has a section on the Scene graph describing this, with lots of nice diagrams and related code.

因此,简而言之,有一个名为的方法的原因getChildren()是因为我们将UI中所有内容的集合视为树结构,并且此方法名称准确描述了该方法在所有UI元素集合的上下文中的作用是一棵树。术语节点,父和子对于具有一点经验的程序员来说是即刻可识别的,并帮助他们理解整体结构并使用它。他们基本上可以立即推断 getChildren()返回该容器中包含的所有UI元素的列表(容器是 StackPane 在你的例子中。)

So, in short, the reason there is a method called getChildren() is because we think of the collection of all the things in the UI as a tree structure, and this method name describes exactly what that method does in the context of the collection of all the UI elements being a tree. The terms "node", "parent" and "child" are instantly recognizable to programmers with a little bit of experience, and help them to understand the overall structure and to work with it. They can basically instantly infer that getChildren() returns a list of all the UI elements immediately contained in that container (the container is the StackPane in your example).

要考虑API设计,请考虑所有可能的事情想要操纵 StackPane 中包含的内容。如您所见,您可能想要将新的UI元素(节点)添加到 StackPane ,因此您可能需要一个名为 add的方法( ...)接受节点。但是你可能还需要或想要做其他一些事情:

To think about the API design, think about all the things you might want to do in terms of manipulating what is contained in your StackPane. As you observe, you might want to add a new UI element ("node") to the StackPane, so you might want a method called add(...) that accepts a Node. But there are a whole bunch of other things you might need or want to do too:


  • StackPane

  • 将一整套节点添加到 StackPane

  • StackPane中移除整个节点集合

  • 从(清除) StackPane

  • 由于堆栈窗格中节点的顺序很重要(它定义了z顺序,即如果节点重叠,则定义绘制的节点你可能想在一个特定的位置插入一个节点(在一些东西之前但在其他东西之后)

  • 删除后面的节点,或者在前面,或者在某个指定位置

  • 以及许多其他人

  • Remove a node from the StackPane
  • Add a whole collection of nodes to the StackPane
  • Remove a whole collection of nodes from the StackPane
  • Remove all the nodes from (clear) the StackPane
  • Since the order of nodes in the stack pane is important (it defines the z-order, i.e. if the nodes overlap it defines which are drawn in front and which behind), you might want to insert a node at a specific position (in front of something but behind something else)
  • Remove the node at the "back", or at the front, or at some specified position
  • and many, many others

所以设计者 StackPane 类可能刚刚编写了所有这些方法,但有更好的方法。

So the designers of the StackPane class could have just written all these methods, but there's a better way.

如果你考虑实现 StackPane ,您需要一些方法来跟踪它包含的节点(UI元素)。这些节点的顺序很重要,因此我们必须跟踪它,我们需要有一个很好的方法来定义上面列出的所有功能。 StackPane 类(或其超类, Pane )的作者可以构建一个数据结构来从头开始,但标准库中已存在一个。包含某些特定类型的对象集合并跟踪其顺序的数据结构称为 列表 1 列表 interface是每个Java程序员都熟悉的

If you think about implementing StackPane, you need some way to keep track of the nodes (UI elements) it contains. The order of these nodes is important, so we must keep track of that, and we need to have a nice way to define all the functionality listed above. The authors of the StackPane class (or its superclass, Pane) could have built a data structure to do this from scratch, but one already exists in the standard libraries. A data structure that holds a collection of objects of some specific type, and keeps track of their order, is called a List1, and the List interface is one that every single Java programmer is familiar with.

因此,如果您考虑 StackPane 的实际实现,您可以为上面列出的所有功能定义方法在堆栈窗格本身。这最终会找到一些东西(实际上它会比这更复杂,我只想在这里提出足够的意见),例如

So if you think of the actual implementation of the StackPane, you could define methods for all the functionality listed above in the stack pane itself. This would end up looking something (really it would be more complex than this, I just want enough here to make the point) like

public class StackPane {

    private final List<Node> children = new ArrayList<>(); // or some other list implementation...

    public void add(Node node) {
        children.add(node);
    }

    public boolean remove(Node node) {
        return children.remove(node);
    }

    public void add(int index, Node node) {
        children.add(index, node);
    }

    public boolean remove(int index) {
        return children.remove(index);
    }

    public void addAll(Collection<Node> nodes) {
        children.addAll(nodes);
    }

    // lots more methods like this...

    // lots of layout code omitted...
}

我认为你明白了。所有这些代码都没有真正做任何事情;它只是调用已经定义的行为。因此,只需提供对列表本身 2 的访问权限,就可以为 StackPane 的用户提供完全相同的功能,而不是这个膨胀的API。

I think you get the point. All this code is not really doing anything; it is simply invoking behavior that is already defined. So instead of this bloated API, it's possible to provide exactly the same functionality to the user of StackPane simply by providing access to the list itself2:

public class StackPane {
    private final List<Node> children = new ArrayList<>();

    public List<Node> getChildren() {
        return children ;
    }

    // layout code omitted...
}

现在这个类不那么臃肿了,API不那么臃肿了,另外,用户收到了一个 List ,正如前面所指出的那样非常好已知类型的对象。 JavaFX的新程序员,假设他们有一些(任何)Java经验,已经熟悉 List 的API,并且不需要学习很多新的顺序使用它。所以程序员现在可以这样做(代码是非常规的,并插入了程序员的想法):

Now the class is much less bloated, the API is less bloated, and additionally, the user receives a List, which as pointed out earlier is a very well known type of object. The new programmer to JavaFX, assuming they have some (any) Java experience, will already be familiar with the API for List and will not need to learn much that is new in order to use it. So the programmer can now do (with code laid out unconventionally and the thoughts of the programmer inserted):

StackPane pane = new StackPane();

Button button = new Button("OK");

pane.getChildren()
    // oooh, a List, I know how those work
    .add(button);

List<Label> lotsOfLabels = Arrays.asList(new Label("One"), new Label("Two"), new Label("Three"));

pane.getChildren()
    // still a list, I know more things I can do here:
    .addAll(lotsOfLabels);

pane.getChildren().remove(button);

pane.getChildren().clear();

我们有一个快乐的程序员,他们立刻就知道这个API,并且在第一次见到时立刻就会很有效率管理JavaFX中的场景图,因此是一个快乐的老板,他从编程团队那里看到了很高的工作效率。

and we have a happy programmer who immediately knows the API, and is instantly productive on a first encounter with managing the scene graph in JavaFX, and consequently a happy boss who sees great productivity from the programming team.

总而言之,只需暴露列出子节点,API可以公开操作 StackPane 内容所需的所有功能,而无需添加大量方法 StackPane 以一种利用每个Java程序员的现有知识的方式完成它。

So in summary, by simply exposing the List of child nodes, the API can expose all the functionality it needs for manipulating the contents of the StackPane, without adding a huge list of methods to the StackPane class and do it in a way that leverages the existing knowledge of every Java programmer.

脚注


  1. 实际上, Pane 需要比 List 中定义的功能更多的功能:他们需要通过节点知道列表何时更改的便捷方式被添加或删除(因为窗格需要在发生这种情况时重新计算其布局)。因此,JavaFX团队定义了 List 的子接口,名为 ObservableList ,其具有列出并添加观察列表的功能。

  2. JavaFX团队有一件事需要在这里仔细思考:是所有适用于子节点集合的 List 定义的功能?如果 List 接口定义了一些在这里没有意义的方法,那么使用这个实现可能是一个坏主意,并且第一个提出的API更加臃肿代码块实际上可能是更好的选择。

  1. Actually, Panes require a little more functionality than is defined in List: they need a convenient way of knowing when the list changes, by nodes being added or removed (because the pane needs to recompute its layout when this happens). So the JavaFX team defined a subinterface of List, called ObservableList, which has all the functionality of List and adds functionality for "observing" the list.
  2. There is one thing that the JavaFX team will have had to think hard about here: is all the functionality defined by List appropriate for the collection of child nodes? If the List interface defined some methods that really didn't make sense here, it would probably be a bad idea to use this implementation, and the more bloated API suggested in the first code block might actually be a better choice.