属性的属性有意义吗?
Do properties of properties make sense?
因为这是一个关于设计的问题,所以我先说说我有什么,我想要什么。
我有一个使用组合的设计。一个 Cell
对象包含一个 Shape
和一个 Background
对象(本例中是定制对象)。这 2 个中的每一个都有自己的数据来定义它们。这是代码中的示例:
class Cell {
Shape shape;
Background background;
class Shape {
int size;
Color color;
Point location;
//...
}
class Background {
Color color;
String name;
CoverType type;
//...
}
}
我还有一个 GUI 需要表示许多单元格,我已经写了如何做(如何使用颜色、大小等在屏幕上创建我想要的内容)。它包括 class 类,例如 CellRepresentation、ShapeRepresentation 和 BackgroundRepresentation,它们的显示属性绑定到数据属性(我认为这称为模型和视图)。
我希望能够通过更改以上数据来表示GUI中的变化:
- 用户可以(例如)右键单击形状并设置其颜色。所以上面的数据发生变化,变化需要反映在GUI中。
- 用户还可以更改整个形状(例如从另一个单元格复制粘贴)。甚至整个细胞。这些更改也需要反映在 GUI 中。
我的问题 class 成员中的哪个需要是我绑定到的 JavaFX 属性。
这就是我的想法:"leaf" 属性(大小、颜色、位置...)必须是属性,以便我可以将它们绑定到 GUI 属性。但是我是否也需要制作形状和背景对象属性?只有它们的属性在屏幕上有 "Actual" 表示。理想情况下,我会喜欢它,如果 Shape 发生变化,那么它的所有属性都会告诉它们的绑定它们可能已经改变了(也许颜色没有但大小改变了)。但它不是这样工作的 - 即使形状的颜色可以在形状改变时改变颜色 属性 也不会告诉绑定到它的任何东西它改变了.
这同样适用于使单元格成为 属性 在更大的图片中有很多单元格等等:属性委托更改的属性。
所以我想使 Shape 和 Background 也具有属性并向它们注册 InvalidationListener
更新它们的属性。这似乎不对,因为我认为有了对属性的所有支持,就有办法做我想做的事。
有人可以建议一种方法吗?
仅使用标准 JavaFX API,您可以利用 Bindings.selectXXX
方法来观察 "property of a property".
例如:
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.paint.Color;
public class Cell {
private final ObjectProperty<Shape> shape = new SimpleObjectProperty<>(new Shape());
public final ObjectProperty<Shape> shapeProperty() {
return this.shape;
}
public final Cell.Shape getShape() {
return this.shapeProperty().get();
}
public final void setShape(final Cell.Shape shape) {
this.shapeProperty().set(shape);
}
public static class Shape {
private final IntegerProperty size = new SimpleIntegerProperty(0);
private final ObjectProperty<Color> color = new SimpleObjectProperty<>(Color.BLACK);
public final IntegerProperty sizeProperty() {
return this.size;
}
public final int getSize() {
return this.sizeProperty().get();
}
public final void setSize(final int size) {
this.sizeProperty().set(size);
}
public final ObjectProperty<Color> colorProperty() {
return this.color;
}
public final javafx.scene.paint.Color getColor() {
return this.colorProperty().get();
}
public final void setColor(final javafx.scene.paint.Color color) {
this.colorProperty().set(color);
}
}
public static void main(String[] args) {
Cell cell = new Cell();
Bindings.selectInteger(cell.shapeProperty(), "size").addListener(
(obs, oldSize, newSize) -> System.out.println("Size changed from "+oldSize+" to "+newSize));
cell.getShape().setSize(10);
cell.setShape(new Shape());
Shape s = new Shape();
s.setSize(20);
cell.setShape(s);
}
}
将产生(期望的)输出
Size changed from 0 to 10
Size changed from 10 to 0
Size changed from 0 to 20
这个 API 有一点遗留的感觉,因为它依赖于将 属性 名称作为字符串传递,因此不是类型安全的,无法在编译时检查.此外,如果任何中间属性为空(例如,如果 cel.getShape()
returns 在本例中为空),绑定会生成烦人且冗长的警告消息(即使这应该是受支持的用例) .
Tomas Mikula 在他的 ReactFX library, see this post 中描述了一个更现代的实现。使用 ReactFX,您可以:
public static void main(String[] args) {
Cell cell = new Cell();
Var<Number> size = Val.selectVar(cell.shapeProperty(), Shape::sizeProperty);
size.addListener(
(obs, oldSize, newSize) -> System.out.println("Size changed from "+oldSize+" to "+newSize));
cell.getShape().setSize(10);
cell.setShape(new Shape());
Shape s = new Shape();
s.setSize(20);
cell.setShape(s);
}
最后,如果您要创建单元格列表,则可以创建一个 ObservableList
并指定一个 extractor
。提取器是一个函数,将列表中的每个元素(每个 Cell
)映射到 Observable
的数组。如果这些 Observable
中的任何一个发生变化,列表就会触发一个更新事件。所以你可以做
ObservableList<Cell> cellList =
FXCollections.observableArrayList(cell -> new Observable[] {Bindings.selectInteger(cell.shapeProperty(), "size")});
使用标准 API,或
ObservableList<Cell> cellList =
FXCollections.observableArrayList(cell -> new Observable[] {Val.selectVar(cell.shapeProperty(), Shape::sizeProperty)});
使用 ReactFX。然后只需在列表中添加一个 ListChangeListener
,如果大小更改(或者形状更改为具有不同大小的新形状),它将收到通知。您可以根据需要在返回的数组中添加任意数量的作为单元格属性(或属性的属性)的可观察对象。
因为这是一个关于设计的问题,所以我先说说我有什么,我想要什么。
我有一个使用组合的设计。一个 Cell
对象包含一个 Shape
和一个 Background
对象(本例中是定制对象)。这 2 个中的每一个都有自己的数据来定义它们。这是代码中的示例:
class Cell {
Shape shape;
Background background;
class Shape {
int size;
Color color;
Point location;
//...
}
class Background {
Color color;
String name;
CoverType type;
//...
}
}
我还有一个 GUI 需要表示许多单元格,我已经写了如何做(如何使用颜色、大小等在屏幕上创建我想要的内容)。它包括 class 类,例如 CellRepresentation、ShapeRepresentation 和 BackgroundRepresentation,它们的显示属性绑定到数据属性(我认为这称为模型和视图)。
我希望能够通过更改以上数据来表示GUI中的变化:
- 用户可以(例如)右键单击形状并设置其颜色。所以上面的数据发生变化,变化需要反映在GUI中。
- 用户还可以更改整个形状(例如从另一个单元格复制粘贴)。甚至整个细胞。这些更改也需要反映在 GUI 中。
我的问题 class 成员中的哪个需要是我绑定到的 JavaFX 属性。
这就是我的想法:"leaf" 属性(大小、颜色、位置...)必须是属性,以便我可以将它们绑定到 GUI 属性。但是我是否也需要制作形状和背景对象属性?只有它们的属性在屏幕上有 "Actual" 表示。理想情况下,我会喜欢它,如果 Shape 发生变化,那么它的所有属性都会告诉它们的绑定它们可能已经改变了(也许颜色没有但大小改变了)。但它不是这样工作的 - 即使形状的颜色可以在形状改变时改变颜色 属性 也不会告诉绑定到它的任何东西它改变了.
这同样适用于使单元格成为 属性 在更大的图片中有很多单元格等等:属性委托更改的属性。
所以我想使 Shape 和 Background 也具有属性并向它们注册 InvalidationListener
更新它们的属性。这似乎不对,因为我认为有了对属性的所有支持,就有办法做我想做的事。
有人可以建议一种方法吗?
仅使用标准 JavaFX API,您可以利用 Bindings.selectXXX
方法来观察 "property of a property".
例如:
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.paint.Color;
public class Cell {
private final ObjectProperty<Shape> shape = new SimpleObjectProperty<>(new Shape());
public final ObjectProperty<Shape> shapeProperty() {
return this.shape;
}
public final Cell.Shape getShape() {
return this.shapeProperty().get();
}
public final void setShape(final Cell.Shape shape) {
this.shapeProperty().set(shape);
}
public static class Shape {
private final IntegerProperty size = new SimpleIntegerProperty(0);
private final ObjectProperty<Color> color = new SimpleObjectProperty<>(Color.BLACK);
public final IntegerProperty sizeProperty() {
return this.size;
}
public final int getSize() {
return this.sizeProperty().get();
}
public final void setSize(final int size) {
this.sizeProperty().set(size);
}
public final ObjectProperty<Color> colorProperty() {
return this.color;
}
public final javafx.scene.paint.Color getColor() {
return this.colorProperty().get();
}
public final void setColor(final javafx.scene.paint.Color color) {
this.colorProperty().set(color);
}
}
public static void main(String[] args) {
Cell cell = new Cell();
Bindings.selectInteger(cell.shapeProperty(), "size").addListener(
(obs, oldSize, newSize) -> System.out.println("Size changed from "+oldSize+" to "+newSize));
cell.getShape().setSize(10);
cell.setShape(new Shape());
Shape s = new Shape();
s.setSize(20);
cell.setShape(s);
}
}
将产生(期望的)输出
Size changed from 0 to 10
Size changed from 10 to 0
Size changed from 0 to 20
这个 API 有一点遗留的感觉,因为它依赖于将 属性 名称作为字符串传递,因此不是类型安全的,无法在编译时检查.此外,如果任何中间属性为空(例如,如果 cel.getShape()
returns 在本例中为空),绑定会生成烦人且冗长的警告消息(即使这应该是受支持的用例) .
Tomas Mikula 在他的 ReactFX library, see this post 中描述了一个更现代的实现。使用 ReactFX,您可以:
public static void main(String[] args) {
Cell cell = new Cell();
Var<Number> size = Val.selectVar(cell.shapeProperty(), Shape::sizeProperty);
size.addListener(
(obs, oldSize, newSize) -> System.out.println("Size changed from "+oldSize+" to "+newSize));
cell.getShape().setSize(10);
cell.setShape(new Shape());
Shape s = new Shape();
s.setSize(20);
cell.setShape(s);
}
最后,如果您要创建单元格列表,则可以创建一个 ObservableList
并指定一个 extractor
。提取器是一个函数,将列表中的每个元素(每个 Cell
)映射到 Observable
的数组。如果这些 Observable
中的任何一个发生变化,列表就会触发一个更新事件。所以你可以做
ObservableList<Cell> cellList =
FXCollections.observableArrayList(cell -> new Observable[] {Bindings.selectInteger(cell.shapeProperty(), "size")});
使用标准 API,或
ObservableList<Cell> cellList =
FXCollections.observableArrayList(cell -> new Observable[] {Val.selectVar(cell.shapeProperty(), Shape::sizeProperty)});
使用 ReactFX。然后只需在列表中添加一个 ListChangeListener
,如果大小更改(或者形状更改为具有不同大小的新形状),它将收到通知。您可以根据需要在返回的数组中添加任意数量的作为单元格属性(或属性的属性)的可观察对象。