覆盖CompoundPropertyModel中对象的getter方法

时间:2016-07-06 15:55:06

标签: java wicket

以下是在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方法。到目前为止,我还没有找到如何强制模型使用它们。有没有办法做到这一点?

2 个答案:

答案 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一起带来的那些:

  • AbstractReadOnlyModel
  • LoadableDetachableModel
  • ResourceModel
  • StringResourceModel
  • ListItemModel
  • PropertyModel
  • CompoundPropertyModel
  • FeedbackMessagesModel
  • LambdaModel
  • ...

他们都有技术理由存在。您将根据自己的技术要求创建自己的模型实现,例如

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类扩展多个类”,并有很好的结果。