JavaFX中如何将只读数据模型的方法转为Property对象?
JavaFX TableView 只读数据适配方案选择
我是资深Java程序员,从Java初始版本就开始使用Java。上一次在Java生态中开发UI还是给Eclipse富客户端做SWT,现在不想再用这套方案,且具备GUI、监听器、Observable属性等相关知识。
目前正在开发一款费用与信用卡交易匹配的应用,需要用TableView展示已对账的费用,现有如下数据类(省略构造方法和成员变量):
public class ReconciledExpense { public String getStore() { return transactionData.getStore(); } public BankName getBankName() { return transactionData.getBankName(); } public LocalDate getDate() { return transactionData.getDate(); } public BigDecimal getAmount() { return expenseData.getAmount(); } public String getCategory() { return expenseData.getCategory(); } }
数据为只读,当前ReconciledExpense实例存在ArrayList中,打算改成ObservableList来感知新增的已对账费用。但现在纠结如何将getStore这类普通getter转为JavaFX的StringProperty,有两种可选方案:
- 创建
ReconciledExpenseWrapper包装类封装ReconciledExpense - 将底层数据模型的
String/BigDecimal等类型改为StringProperty/ObjectProperty
对于第二种方案,我疑问是否需要每次返回相同的StringProperty/ObjectProperty?虽然数据只读,感觉这无关紧要,但我不喜欢该方案,因为UI决策会影响底层代码,尤其是目前还没完全认可JavaFX。
方案分析与建议
优先选择包装类方案
这是最符合关注点分离原则的做法,完全隔离UI层和核心业务模型:
- 核心业务代码不依赖JavaFX API,就算之后更换UI框架(比如换回SWT或其他),底层模型无需任何修改;
- 包装类只需为每个只读属性创建对应的
ReadOnlyXXXProperty,实现简单,示例代码如下:
public class ReconciledExpenseWrapper { private final ReconciledExpense expense; // 可选:缓存Property实例,避免重复创建 private final ReadOnlyStringProperty store; private final ReadOnlyObjectProperty<BankName> bankName; private final ReadOnlyObjectProperty<LocalDate> date; private final ReadOnlyObjectProperty<BigDecimal> amount; private final ReadOnlyStringProperty category; public ReconciledExpenseWrapper(ReconciledExpense expense) { this.expense = expense; // 初始化缓存的Property this.store = new ReadOnlyStringWrapper(expense.getStore()).getReadOnlyProperty(); this.bankName = new ReadOnlyObjectWrapper<>(expense.getBankName()).getReadOnlyProperty(); this.date = new ReadOnlyObjectWrapper<>(expense.getDate()).getReadOnlyProperty(); this.amount = new ReadOnlyObjectWrapper<>(expense.getAmount()).getReadOnlyProperty(); this.category = new ReadOnlyStringWrapper(expense.getCategory()).getReadOnlyProperty(); } public ReadOnlyStringProperty storeProperty() { return store; } public ReadOnlyObjectProperty<BankName> bankNameProperty() { return bankName; } public ReadOnlyObjectProperty<LocalDate> dateProperty() { return date; } public ReadOnlyObjectProperty<BigDecimal> amountProperty() { return amount; } public ReadOnlyStringProperty categoryProperty() { return category; } // 可选:提供原始对象的访问方法 public ReconciledExpense getReconciledExpense() { return expense; } }
如果不想缓存,也可以每次调用xxxProperty()时返回新的ReadOnlyXXXProperty实例——因为数据是只读的,TableView绑定后不会有任何问题,只是重复创建实例的性能开销可以忽略不计。
不推荐修改底层模型方案
虽然只读场景下,每次返回同一个Property实例或新实例都能正常工作,但这种方案的致命问题是业务模型与JavaFX框架强耦合:
- 核心业务代码依赖JavaFX的Property类,一旦后续更换UI技术栈,整个模型都需要重构;
- UI层的决策侵入了核心业务代码,破坏了代码的职责边界,对于资深开发者来说,这种设计是应该尽量避免的。
内容的提问来源于stack exchange,提问作者Mark Levison
相关产品推荐
相关产品推荐

