这是一个在Wicket中使用CompoundPropertyModel时经常遇到的问题的简单示例:
这是我的实体Item
,具有其模型ItemModel
和控制器ItemController
:
public class Item implements Serializable {
private static final long serialVersionUID = 1L;
private long createdTimestamp;
public Item(long createdTimestamp) {
super();
this.createdTimestamp = createdTimestamp;
}
public long getCreatedTimestamp() {
return createdTimestamp;
}
public void setCreatedTimestamp(long createdTimestamp) {
this.createdTimestamp = createdTimestamp;
}
}
public class ItemModel extends CompoundPropertyModel<Item> {
private static final long serialVersionUID = 1L;
public ItemModel(Item object) {
super(object);
}
}
public class ItemController {
public static int getDuration(Item item) {
return Days.daysBetween(new DateTime(item.getCreatedTimestamp()), new DateTime()).getDays();
}
}
假设我要在面板上显示createdTimestamp和持续时间。如果我做这样的事情:
public class ItemPanel extends GenericPanel<Item> {
private static final long serialVersionUID = 1L;
public ItemPanel(String id, ItemModel itemModel) {
super(id);
setModel(itemModel);
add(new Label("createdTimestamp"));
add(new Label("duration"));
}
}
标签createdTimestamp将被显示为一个长值,因为那是getCreatedTimestamp()方法返回的结果,面板会抛出异常,因为在类
Item
中没有实现getDuration()方法。一个可能的解决方案是为类
Item
创建一个包装器,可以将其称为ItemView
:public class ItemView implements Serializable {
private static final long serialVersionUID = 1L;
private Item item;
public ItemView(Item item) {
super();
this.item = item;
}
public Date getCreatedTimestamp() {
return new Date(item.getCreatedTimestamp());
}
public int duration() {
return ItemController.getDuration(item);
}
}
可以,但是我不喜欢这种解决方案,因为如果您有很多实体并且它们具有很多字段,那么为所有实体创建和使用包装器类可能会非常耗时。
Item
已经具有包装器,称为ItemModel
。这就是为什么我想在此类中创建getter方法并强制模型使用它们。这是我的意思的示例:
public class ItemModel extends CompoundPropertyModel<Item> {
private static final long serialVersionUID = 1L;
public ItemModel(Item object) {
super(object);
}
public Date getCreatedTimestamp() {
return new Date(getObject().getCreatedTimestamp());
}
public int duration() {
return ItemController.getDuration(getObject());
}
}
但是在这种情况下,模型将忽略getter方法。到目前为止,我还没有发现如何强制模型使用它们。有什么办法吗?
最佳答案
当您在组件和属性之间进行直接映射时,CompoundPropertyModel非常有用。
但是有时使用AROM更容易:
add(new Label("createdTimestamp", new AbstractReadOnlyModel<Date>() {
public Date getObject() {
return new Date(getModelObject().getCreatedTimestamp());
}
});
请注意,使用Wicket 8,您可以编写得更短:
add(new Label("createdTimestamp", () -> new Date(getModelObject().getCreatedTimestamp())));
(由于我们实际上只是从Long转换为Date,因此最好使用转换器。)
Item已经有包装器,称为ItemModel。这就是为什么我会
喜欢在此类中创建getter方法并强制模型使用
他们
除了在IModel中定义的方法外,模型永远不能有其他方法-Wicket不会调用其中添加的任何方法,但是最终会在组件之间产生不必要的依赖关系(例如,面板需要ItemModel作为参数)。
为所有实体创建和使用包装器类可能非常耗时
我想知道为什么要定义ItemModel。最好只接受构造函数中的任何模型:
public ItemPanel(String id, IModel<Item> itemModel) {
super(id, new CompoundPropertyModel<Item>(itemModel);
}
为什么调用者应该知道您的面板内部使用了CompositePropertyModel?
跟进:
有合理的理由创建您自己的模型实现。看看随Wicket一起带来的那些:
抽象只读模型
LoadableDetachableModel
资源模型
StringResourceModel
ListItemModel
属性模型
复合属性模型
反馈消息模型
Lambda模型
...
它们都有存在的技术原因。您将根据自己的技术要求创建自己的模型实现,例如
new EntityModel(uuid, clazz)
new PropertyInYourGeneratedLegacyObjectModel(order, R.id.ORDER_NUMBER)
我看过其他一些我认为值得怀疑的模型实现。以下两种方法通常具有其他方法,可能会发出某种错误的气味:
new TransactionalModel(wrapped).commit()
new AsyncModel(wrapped).startAsync()
人们提倡创建与您的域相关的可重用模型-例如OrderSumModel-但是恕我直言,这已失败。领域逻辑迟早会包含在模型中,您会看到以下模式出现:
BigDecimal sum = new OrderSumModel(order).getObject();
大多数时候,当我需要特殊的东西时,我只是在写匿名内部类:
new Label("sum", new AbstractReadOnlyModel() {
public BigDecimal getObject() {
return service.calculateSum(getModelObject());
}
});
如上所述,使用Java 8和Wicket 8,这变得更加容易编写:
new Label("sum", () -> service.calculateSum(getModelObject()));
与往常一样,有例外:
当我需要具有复杂域逻辑的模型时(例如OrderSumModel),我将其保留为嵌套的内部类。只要我从多个位置需要它,我就会再次考虑。