You are viewing an old version of this page. View the current version.

Compare with Current View Page History

Version 1 Next »

Table of contents

The Partially Constructed Object Dillema

Wicket naturally lends itself to object-oriented representations of web pages, and one aspect of this approach is using inheritance to pull behavior common to a set of pages into a superclass shared by those pages. For example, several pages might have a shared base class in Template.java (with corresponding markup in Template.html) which sets up a header and side navigation menu that is common to those pages.

A problem is encountered, however, when it comes to allowing subclasses to modify the base class behavior through method overrides. For example, let's assume the Template has a header with the page title, and that this title changes per page. A plausible solution is to provide an abstract getPageTitle method which subclasses will override. In the Template constructor, it will add a label with the result of this method:

public abstract class Template extends WebPage { 

  public Template(PageParameters params) { 
     super(params); 
     add(new Label("headerTitle",getPageTitle()));
  }
  protected abstract String getPageTitle();
}

Let's also assume that a certain page subclass of Template wants the title in question to depend upon data passed in through PageParameters at construction:

public class class SomePage extends Template { 

  Object myObject;
  public SomePage(PageParameters params) {
    super(params);
    long objectId = params.getLong("objectId");
    myObject = SomeService.lookupById(objectId);
  }

  protected String getPageTitle() { 
    return "Editing: " + myObject.getSomeProperty; //Null Pointer Exception
  }
}

The problem we encounter is that the code to look up myObject above has not yet run when getPageTitle() is invoked in the constructor of Template. In general, invoking overridable methods from a java constructor is considered bad practice, because the object in question has not necessarily been completely constructed (meaning that subclass constructor(s) have not yet run) when a base class constructor is being invoked.

Possible Solutions

There is no consensus in the wicket community on a standard pattern for solving this problem. There are however a number of typical solutions.

Two-phase construction with an internal init() method

In this solution, an init() method is declared in the base class, and subclasses are expected to:

  1. Override init() to do any setup work, rather than doing that work in the constructor. The constructor is instead used to do setup of the model as well as the initialization of any local data that is dependent on PageParameter data.
  2. Call super.init() as the first line in init(), ensuring that init() mimics a constructor, so that the base class is set up before the subclass
  3. Call init() at the end of their constructor(s), so that initialization is sure to happen

There is no way to ensure that the above set up constraints are obeyed, but it nevertheless provides a simple solution to the partially-constructed-object problem without reliance on any class outside the pages in question. The above example, adopting this strategy, would look like:

public abstract class Template extends WebPage { 

  public Template(PageParameters params) { 
     super(params); 
  }

  protected void init() {   
     add(new Label("headerTitle",getPageTitle()));
  }  
  
  protected abstract String getPageTitle();
}

public class class SomePage extends Template { 

  Object myObject;
  public SomePage(PageParameters params) {
    super(params);
    long objectId = params.getLong("objectId");
    myObject = SomeService.lookupById(objectId);
    init();
  }

  protected String getPageTitle() { 
    return "Editing: " + myObject.getSomeProperty;
  }
}

One remaining problem with this strategy is that if SomePage is itself subclassed, there is no clean way to ensure that init() is called after the most-derived class is constructed, and ensure that it is called only once, without testing and setting an isInitialized field.

Two-phase construction with an IComponentInstantiationListener

This approach removes the need to ensure that init() is called by each page subclass. Instead,

  • No labels