这是一个在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),我将其保留为嵌套的内部类。只要我从多个位置需要它,我就会再次考虑。

10-07 18:56
查看更多