Ideas for refactoring the way Wicket uses models.

Issues with current models (and ideas for solutions)

Component uses models in a non-type-safe manner

Following the IModel generification, Component retains get/setDefaultModelObject methods that compromise type-safety.

Is it even necessary for Component itself to have a model? What if we simply removed these methods and introduced a new interface, IModelUsingComponent<T>, to be implemented by Component subclasses that use a model of type T?

If we remove the methods from Component, then should Component even retain the reference to the model? It's kept in Component's "data" array as a memory-saving strategy. If we move it to subclasses then apps will potentially require an additional 4 bytes of memory for each instance.

IModel is required, even when it is not needed

If an application creates many components with static models, then it must create a Model instance for each one, however, the Model instances don't provide any value except to implement IModel.

If we introduce IModelUsingComponent<T>, then it can extend IModel<T>. In this manner, a Component can be its own model. Each Component subclass can independently decide whether to wrap static models in Model or store them directly.

Having IModelUsingComponent<T> extend IModel<T> would also be a partial solution to the problem of constructing complex components described below.

IComponentAssignedModel breaks IModel contract

IComponentAssignedModel is confusing for developers because is not really a model: it's a model factory. When the application passed one to a component, it's the factory-generated model that's actually used. The IComponentAssignedModel instance itself throws UnsupportedOperationException if an application attempts to call getObject or setObject. Developers have to consider IComponentAssignedModel every time they use IModel. Every IModel instance must be "wrapped" before it can be used because it might be an IComponentAssignedModel. This goes against the spirit of IModel being a simple indirection interface.

I think something like an IComponentAwareModel with a single method, setComponent, would work better. The model would be simply associated with its component, rather than being used as a factory to generate the real model. All IModel references would refer to valid models, even if they have not yet been associated with a component. Chained models would implement IComponentAwareModel and pass the component to their inner models (if the inner models implemented IComponentAwareModel themselves).

Components cannot be explicitly configured to inherit a model

The strategy for locating an inheritable model is built into the Component class instead of being a model itself, and can only be triggered if no model is assigned to the component. However, many times a model is not assigned to a component because it simply doesn't need one. Component cannot distinguish between these two cases, so it assumes that if getModel is called on a component, then the model is needed, otherwise it isn't. The consequence of this is that it is impossible to create a component that simultaneously an inherited model and an inheritable model: the component must have no model in order to inherit a model, but it also must have a model in order for that model to be inherited. Wrapping an inherited model with CompoundPropertyModel in the initModel method is not possible because initModel may not be called.

A possible solution is to put the logic to find an inheritable model in a model, maybe InheritingModel. The model would be an implementation of IComponentAwareModel described above. Component would instantiate an instance of this model within initModel if no model was provided, but the model could also be explicitly defined.

With this solution, a complex Component subclass could be defined as such:

MyComplexComponent.java
public class MyComplexComponent extends Component {
    public MyComplexComponent(IModel model) {
        // IModel provided:  use it.
        // Look ma, no wrapping!
        setModel(new CompoundPropertyModel(model));
        instantiateChildren();
    }
    public MyComplexComponent() {
        // No model:  create InheritingModel.
        // Otherwise model-less components won't find it!
        setModel(new CompoundPropertyModel(new InheritingModel()));
        instantiateChildren();
    }
    private void instantiateChildren() {
        // Create lots of child object without models.
    }
}
It is frequently difficult or impossible to create child components in a component's constructor

Enabling a complex component to create all of its child components in its constructor would simplify component development. Currently, creating child components in the constructor is difficult because in the constructor, the component's parent, and potentially its model, are unknown.

Implementing the refactorings described above would resolve most of the issues with creating child components in the constructor.

  • No labels