2016-07-06 12 views
0

Hier ist ein einfaches Beispiel für ein Problem, das ich oft erhalten, wenn sie mit CompoundPropertyModel in Wicket arbeiten:Zwingende Getter-Methode eines Objekts in CompoundPropertyModel

Das ist mein Unternehmen ist Item mit seinem Modell ItemModel und Controller 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(); 
    } 
} 

Nehmen wir an, ich möchte createdTimestamp und Dauer auf einem Panel anzeigen. Wenn ich so etwas tun:

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 würde als Long-Wert diplayed werden, denn das ist, was getCreatedTimestamp() Methode zurückgibt und Panel würde eine Ausnahme auslösen, weil es keine getDuration () Methode implementiert in Klasse Item.

Eine mögliche Lösung hierfür ist ein Wrapper für die Klasse Item, zu schaffen, die ItemView genannt werden könnte:

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); 
    } 
} 

Es würde funktionieren, aber ich diese Lösung nicht gefällt, weil das Erstellen und Wrapper Klassen für all Ihre Entitäten können sehr zeitaufwendig sein, wenn Sie viele davon haben und viele Felder haben.

Item hat bereits Wrapper, der ItemModel heißt. Deshalb möchte ich Getter-Methoden in dieser Klasse erstellen und das Modell zwingen, sie zu verwenden. Hier

ist ein Beispiel dafür, was ich meine:

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()); 
    } 
} 

Aber in diesem Fall ist die Getter-Methoden würden durch das Modell außer Acht gelassen werden. Bisher habe ich noch nicht herausgefunden, wie man das Modell dazu zwingt, sie zu benutzen. Gibt es einen Weg, es zu tun?

Antwort

2

CompoundPropertyModel ist sehr nützlich, wenn Sie eine direkte Zuordnung zwischen Komponenten und Eigenschaften haben. Aber manchmal ist es nur einfacher, eine AROMATISCHE zu verwenden:

add(new Label("createdTimestamp", new AbstractReadOnlyModel<Date>() { 
    public Date getObject() { 
    return new Date(getModelObject().getCreatedTimestamp()); 
    } 
}); 

Beachten Sie, dass mit Wicket 8 Sie diese viel kürzer schreiben:

add(new Label("createdTimestamp",() -> new Date(getModelObject().getCreatedTimestamp()))); 

(Da sind wir eigentlich nur von Long Umwandlung in Datum könnte besser sein, stattdessen einen Konverter zu verwenden.)

Element hat bereits Wrapper, der ItemModel genannt wird. Deshalb würde ich mag Getter-Methoden in dieser Klasse erstellen und das Modell zwingen zu verwenden, um sie

Ein Modell sollte nie haben zusätzliche Methoden außer den in IModel definiert - alles, was Sie hinzufügen, es wird nicht von Wicket genannt werden , aber Sie werden mit unerwünschten Abhängigkeiten zwischen Ihren Komponenten enden (wie Ihr Panel erfordert ein ItemModel als Argument).

Erstellung und Verwendung von Wrapper-Klassen für alle Ihre Einheiten kann sehr zeitaufwendig sein

Ich frage mich, warum Sie ItemModel sind definiert, dann. Es wäre besser, nur jedes Modell in Ihrem Konstruktor zu akzeptieren:

public ItemPanel(String id, IModel<Item> itemModel) { 
    super(id, new CompoundPropertyModel<Item>(itemModel); 
} 

Warum sollte der Anrufer wissen, dass Ihre Platte eine CompoundPropertyModel innen verwendet?

Follow up:
Es sind legitimen Gründen Sie Ihr eigenes Modell Implementierungen zu erstellen. Werfen Sie einen Blick auf die zusammen mit Wicket gebracht:

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

Sie alle haben einen technischen Grund zu existieren. Sie erstellen Ihre eigene Modellimplementierung für Ihre eigenen technischen Anforderungen, z.

new EntityModel(uuid, clazz) 
new PropertyInYourGeneratedLegacyObjectModel(order, R.id.ORDER_NUMBER) 

Ich habe andere Modellimplementierungen gesehen, die ich fragwürdig finde. Die folgend beide haben oft zusätzliche Methoden, die ein Geruch sein könnte, dass etwas falsch ist:

new TransactionalModel(wrapped).commit() 
new AsyncModel(wrapped).startAsync() 

Die Menschen haben wieder verwendbare Modelle befürworten die Schaffung Bezug zu Ihrer Domain - z.B. OrderSumModel - aber IMHO ist dies fehlgeschlagen.Früher oder später Domain-Logik wird in Modellen enthalten sein, und Sie werden die folgenden Muster sehen auftauchen:

BigDecimal sum = new OrderSumModel(order).getObject(); 

Die meiste Zeit bin ich nur anonyme innere Klassen zu schreiben, wenn ich etwas brauche besondere:

new Label("sum", new AbstractReadOnlyModel() { 
    public BigDecimal getObject() { 
    return service.calculateSum(getModelObject()); 
    } 
}); 

Wie oben erwähnt, mit Java 8 und Wicket 8 diese viel leichter wird, zu schreiben:

new Label("sum",() -> service.calculateSum(getModelObject())); 

Wie immer gibt es Ausnahmen:
Wenn ich ein Modell mit komplizierter Domänenlogik (z. OrderSumModel), behalte ich es als eine verschachtelte innere Klasse. Sobald ich es von mehr als einem Ort brauche, denke ich nochmal.

+0

+1 Danke, das ist was ich brauchte. Sie haben recht, dass ich IModel als Parametertyp verwenden soll. Nach Ihrer Erklärung sehe ich, dass das Erstellen der Klasse ItemModel hier keinen Sinn ergibt, aber gibt es ein mögliches Szenario, in dem ich meine eigene Model-Klasse erstellen müsste? – mvantroba

+0

Ich habe eine Antwort zu meiner Antwort hinzugefügt. – svenmeier

+0

Schöne Erklärung, es hat mir geholfen, das Konzept der Modelle besser zu verstehen. – mvantroba

0

Der Grund, warum Ihr Modell diese Methoden nicht verwenden muss, ist, dass Ihre ItemModel-Klasse keine untergeordnete Klasse von ItemView ist. In der Klassendeklaration für Item-Modell soll ItemView erweitert werden. In diesem Fall werden die Getter-Methoden der Elternklasse überschrieben.

aktualisieren:

Ich habe erkannt, dass ItemModel bereits eine andere Klasse erweitert. In Java kann eine Klasse nur eine Elternklasse erweitern. Das ist schwierig zu umgehen. Eine Möglichkeit besteht darin, ItemView zu einer inneren Klasse von CompoundPropertyModel zu machen und dann ItemModel CompoundPropertyModel zu erweitern. Das sollte funktionieren. Wenn Sie Probleme mit Google haben "make Java Class mehrere Klassen erweitern" und es gibt gute Ergebnisse.

+0

Die ItemModel-Klasse kann die ItemView-Klasse nicht erweitern, da sie die CompoundPropertyModel-Klasse erweitern muss. Und wie gesagt, ich würde gerne eine Lösung finden, ohne ItemView zu verwenden. – mvantroba

+0

@mvantroba Sorry, ich habe das falsch gelesen. Ich habe diesen Link gefunden. http://stackoverflow.com/questions/16997747/java-override-method-from-another-class-without-inheritance – Michael

+0

Das Problem ist, wie das CompoundPropertyModel in Wicket funktioniert. Es ruft nicht seine eigenen Getter-Methoden auf, sondern die Getter-Methoden des Objekts, das es im Konstruktor bekommt. Wenn ich einem Panel Label mit dem Namen "duration" hinzufüge, versucht CompoundPropertyModel, die Methode "object.getDuration()" aufzurufen. Ich muss sein Verhalten ändern, um es eine eigene Getter-Methode zu nennen und deshalb brauche ich eine Wicket-spezifische Lösung dafür. – mvantroba