例如,当我们向窗格添加新按钮时,我们需要编写以下代码:
StackPane pane = new StackPane();
pane.getChildren().add(new Button("OK"));
为什么我们需要调用“ getChildren()”?它甚至做什么?我们为什么不能说:
StackPane pane = new StackPane();
pane.add(new Button("OK"));
我们将按钮添加到窗格中。我们不会将其添加到其子项中,
简短的答案就是“您必须那样做,因为这就是API的编写方式”。当然,您可能真正要问的是为什么要这样编写API。我认为这实际上是两个(相关的)问题。一种与方法名称有关,以及该方法的作用,另一种与实际的API设计有关。
通常,在计算中,重用现有的问题解决方案通常是有益的。(知道何时进行此操作是一门艺术,但是在这两种情况下,显然都是有益的。)以两种不同的方式,此API设计重用了现有的解决方案来解决众所周知的问题,因此使之成为现实。对于以前遇到过这些解决方案的程序员来说,更容易理解和使用。
从总体上看,考虑一下通用用户界面的整体结构。有一个大容器(包含JavaFX场景的根源),其中包含各种UI组件。其中一些可能是简单的控件,例如按钮和标签等,其中一些可能是其他容器,这些容器又包含各种UI组件。当然,其中一些也可能是容器,依此类推。如果不强加某种结构,这可能变得复杂且难以使用。
为了理解结构,我们将其抽象化。有一个根节点,它有零个或多个其他节点的集合。每个节点都有零个或多个其他节点的集合,依此类推。这是计算中非常著名的抽象结构,称为“树”,基本上每个计算机程序员(无论他们使用哪种语言)都熟悉。因此,如果我们将其视为树结构,因为我们已经很熟悉它,那么我们可以使复杂性更易于使用。为了将其视为树结构,我们对树状方面使用标准名称。每个节点(根节点除外)都只有一个“父”(它所属的容器):因此您将在Node
该类中找到一个方法(“节点”是树结构中的另一种术语),称为getParent()
,它可以访问父节点(包含当前节点的容器)。同样,包含其他节点的UI元素(节点)具有一种称为的方法getChildren()
,该方法返回当前节点中包含的所有节点的集合。Oracle
JavaFX教程在“ 场景”图上有一节对此进行了描述,其中包含许多精美的图表和相关代码。
简而言之,之所以有一个方法被称为,getChildren()
是因为我们认为UI中所有事物的集合都是树形结构,并且此方法名称准确地描述了该方法在所有对象集合的上下文中的作用。
UI元素是一棵树。具有一点经验的程序员可以立即识别术语“节点”,“父”和“子”,并帮助他们理解整体结构并与之合作。他们基本上可以立即推断出getChildren()
返回立即包含在该容器中的所有UI元素的列表(该容器是StackPane
您的示例中的)。
Pane
(例如StackPane
)要考虑API设计,请考虑操作中可能包含的所有内容StackPane
。如您所见,您可能想向中添加一个新的UI元素(“节点”)StackPane
,因此您可能想要一个名为的方法add(...)
,该方法接受一个Node
。但是,您可能还需要或想要做很多其他事情:
StackPane
StackPane
StackPane
StackPane
因此,StackPane
该类的设计师可以编写所有这些方法,但是有更好的方法。
如果考虑实现StackPane
,则需要某种方式来跟踪其中包含的节点(UI元素)。这些节点的顺序很重要,因此我们必须对其进行跟踪,并且需要有一种很好的方法来定义上面列出的所有功能。StackPane
该类(或其父类Pane
)的作者本可以构建一个数据结构来从头开始执行此操作,但是标准库中已经存在一个。包含某种特定类型的对象的集合并跟踪其顺序的数据结构称为1,并且该接口是
每个Java程序员都熟悉
的接口。List
List
__
因此,如果考虑到的实际实现,则StackPane
可以在堆栈窗格本身中为上面列出的所有功能定义方法。最终看起来有些东西(真的会比这更复杂,我只想在这里指出这一点)就像
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...
}
我认为你说对了。所有这些代码实际上并没有做什么。只是调用已定义的行为。因此,可以StackPane
通过仅提供对列表本身的访问权限2来向用户提供完全相同的功能,而不用使用这种API肿的API
:
public class StackPane {
private final List<Node> children = new ArrayList<>();
public List<Node> getChildren() {
return children ;
}
// layout code omitted...
}
现在,该类不再那么blo肿,API也不再那么blo肿,此外,用户会收到List
,如前所述,这是一种非常众所周知的对象类型。假设他们具有一定的Java经验,那么新的JavaFX程序员将已经熟悉该API,List
并且无需学习太多新知识就可以使用它。因此,程序员现在可以执行此操作(使用非常规方式放置代码,并插入程序员的思想):
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中的场景图时立即产生了成果,因此,一个高兴的老板也看到了编程团队的高生产力。
因此,总而言之,通过简单地公开List
子节点,API即可公开处理的内容所需的所有功能StackPane
,而无需在StackPane
类中添加大量方法
并 以利用现有知识的方式来实现每个Java程序员。
脚注
Pane
s需要的功能比以下中定义的要多List
:他们需要一种方便的方式来了解列表何时更改(通过添加或删除节点)(因为发生这种情况时窗格需要重新计算其布局)。因此,JavaFX团队定义了一个List
名为的子接口,该子接口ObservableList
具有的所有功能,List
并添加了“观察”列表的功能。List
适合于子节点的集合?如果该List
接口定义了一些在这里实际上没有用的方法,那么使用此实现可能是一个坏主意,并且在第一个代码块中建议使用的更肿的API实际上可能是一个更好的选择。问题内容: 在Golang中,我们将结构体与接收器方法结合使用。到这里为止一切都很完美。 我不确定什么是接口。我们在结构中定义方法,如果要在结构上实现方法,则无论如何都要在另一个结构下再次编写该方法。 这意味着接口似乎只是方法定义,仅占用了页面上多余的空间。 有没有解释我为什么需要接口的示例? 问题答案: 接口太大了,不能在这里给出全面的答案,但是有些事情需要弄清楚它们的用途。 接口是一种 工具
问题内容: 我可以将数据发送到服务器,但是只有在使用FromBody-Attribute时才可以。 为什么无法使用Post从主体自动读取json数据? 后端Web API 前端angularjs 问题答案: 仅因为某事是POST请求,所以没有明确的规则如何传递参数。POST请求仍可以包含URL中编码的查询参数。方法参数应该是“简单”类型(字符串,整数等)的查询参数。 通常,复杂类型应该是POST表
问题内容: 训练期间需要调用该方法。但是文档不是很有帮助 为什么我们需要调用此方法? 问题答案: 在中,我们需要在开始进行反向传播之前将梯度设置为零,因为PyTorch 会 在随后的向后传递中 累积梯度 。在训练RNN时这很方便。因此,默认操作是在每次调用时累积(即求和)梯度。 因此,理想情况下,当您开始训练循环时,应该正确进行参数更新。否则,梯度将指向预期方向以外的其他方向,即朝向 最小值 (或
当我阅读mapstruct文档时,他们说:mapstruct是一个Java注释处理器,用于生成类型安全的bean映射类。 https://mapstruct.org/documentation/stable/reference/html/#introduction 这就剩下我的任务了。为什么我需要mapstruct?Jhipster使用它,我不知道他们为什么首先需要它?为什么你需要Jhipster
问题内容: 我们需要大量的序列化工作,并且必须在使用的每个对象上指定标签,这是一种负担。特别是当它是第三方类时,我们不能真正更改。 问题是:由于是一个空接口,并且Java一旦添加便提供了可靠的序列化 为什么它们不使所有内容都可序列化,仅此而已? 问题答案: 序列化充满陷阱。这种形式的自动序列化支持使类内部成为公共API的一部分(这就是javadoc为你提供类的持久化形式的原因)。 为了实现长期持久
问题内容: 我知道String [] args是作为参数传递给main的字符串数组。 但是,当您不将其包含为参数时(即使您不使用它),它也会引发异常。那么为什么需要它呢?另外,为什么不能是int []或boolean []? 问题答案: 这是一个字符串,因为命令行以文本形式表示。如果要将文本转换为整数或布尔值,则必须自己做- 操作系统或Java引导程序如何确切知道您希望如何解析所有内容?我想Jav