以下是在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"));
}
}
Label createdTimestamp 将被显示为long值,因为这是 getCreatedTimestamp()方法返回的内容,而panel会抛出异常,因为没有 getDuration ()在类Item
中实现的方法。
一种可能的解决方案是为类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方法。到目前为止,我还没有找到如何强制模型使用它们。有没有办法做到这一点?
答案 0 :(得分:2)
当您在组件和属性之间进行直接映射时,CompoundPropertyModel非常有用。 但有时候使用AROM会更容易:
calendar.dateByAddingUnit(.Year, value: -1, toDate: NSDate(), options: [])
请注意,使用Wicket 8,你可以写得更短:
add(new Label("createdTimestamp", new AbstractReadOnlyModel<Date>() {
public Date getObject() {
return new Date(getModelObject().getCreatedTimestamp());
}
});
(因为我们实际上只是从Long转换为Date,所以最好使用转换器。)
项目已经包装,称为ItemModel。这就是我愿意的原因 喜欢在这个类中创建getter方法并强制使用模型 它们
除了在IModel中定义的模型之外,模型永远不应该有其他方法 - 你在那里添加的任何东西都不会被Wicket调用,但是你最终会在组件之间产生不必要的依赖关系(比如你的面板需要一个ItemModel作为参数)。
为所有实体创建和使用包装类可能非常耗时
我想知道你为什么要定义ItemModel。最好只接受构造函数中的任何模型:
add(new Label("createdTimestamp", () -> new Date(getModelObject().getCreatedTimestamp())));
为什么调用者应该知道您的面板内部使用了CompoundPropertyModel?
跟进:
是创建自己的模型实现的正当理由。看看与Wicket一起带来的那些:
他们都有技术理由存在。您将根据自己的技术要求创建自己的模型实现,例如
public ItemPanel(String id, IModel<Item> itemModel) {
super(id, new CompoundPropertyModel<Item>(itemModel);
}
我见过其他模型实现,我觉得有问题。以下两个通常有其他方法,可能是一种有问题的气味:
new EntityModel(uuid, clazz)
new PropertyInYourGeneratedLegacyObjectModel(order, R.id.ORDER_NUMBER)
人们一直主张创建与您的域名相关的可重用模型 - 例如OrderSumModel - 但恕我直言这失败了。模型中将包含迟早的域逻辑,您将看到以下模式:
new TransactionalModel(wrapped).commit()
new AsyncModel(wrapped).startAsync()
大多数时候,当我需要一些特别的东西时,我只是在编写匿名内部类:
BigDecimal sum = new OrderSumModel(order).getObject();
如上所述,使用Java 8和Wicket 8可以更容易地编写:
new Label("sum", new AbstractReadOnlyModel() {
public BigDecimal getObject() {
return service.calculateSum(getModelObject());
}
});
一如既往有例外:
当我需要具有复杂域逻辑的模型(例如OrderSumModel)时,我将其保留为嵌套的内部类。一旦我从多个地方需要它,我再想一想。
答案 1 :(得分:0)
您的模型不被强制使用这些方法的原因是您的ItemModel类不是ItemView的子类。在Item模型的类声明中,使它扩展ItemView。那时父类的getter方法将被覆盖。
<强>更新强>:
我刚刚意识到ItemModel已经扩展了另一个类。在Java中,类只能扩展1个父类。四处走走很棘手。一种方法是使ItemView成为CompoundPropertyModel的内部类,然后ItemModel扩展CompoundPropertyModel。这应该工作。如果你有麻烦谷歌“使java类扩展多个类”,并有很好的结果。