Partially Constructed Objects

The Titanic under construction

Painful experience has taught me that it's a bad idea for an object to pass a reference to itself out from its own constructor. The danger is that until the constructor has finished running the object is only partially constructed. A self reference that leaks out of the constructor can end up being used to call back into the object while it is in an inconsistent state, thereby breaking the contract of its interface. My favourite solution is to use third-party composition: if a relationship exists between two objects, some other object should establish the relationship.

A common case in which I exposed references to partially constructed objects was when I defined composite objects in which a parent created its children in its constructor and children held back-references to their parents.

For example:

class Parent {
    List children = new ArrayList();
    String name;
    
    public Parent(String name) {
        this.children.add( new Child(this,"child1") );
        this.children.add( new Child(this,"child2") );
        this.name = name;
    }

    public String getName() {
        return name;
    }

    ...
}

public class Child {
    Parent parent;
    String name;

    public Child( Parent parent, String name ) {
        this.parent = parent;
        this.name = name;
    }
    
    public String getName() {
        return fullName;
    }

    ....
}

Code like this contains a subtle trap that can trick an unwary programmer into introducing a bug during later maintenance. The parent reference is passed to the child before the parent is fully constructed. If the child constructor is changed to call back to the parent, it will call into a partially constructed object before that object can fulfil the preconditions of the called method.

Here's a contrived example. The call to parent.getName() will throw a NullPointerException.

public class Child {
    Parent parent;
    String fullName;

    public Child( Parent parent, String name ) {
        this.parent = parent;
        this.name = parent.getName() + "." + name;
    }
    
    public String getName() {
        return name;
    }

    ...
}

The problem is easy to spot in this tiny example, and easy to avoid. But in a large system it can be difficult to determine when the object refered to by a method parameter is constructed, and therefore which parameters of a method refer to partially constructed objects that cannot safely be invoked. The programmer will assume that all references passed to a method refer to valid objects. After all, it should be impossible to obtain a reference to an unconstructed object, right?

The best solution I have found is to use third-party compostion. If there is a relationship between two objects, a third object — often the object that constructs the two related objects — should establish that relationship. Because the related objects are constructed before the relationship is established, they can safely call each other's methods.

For example, if the Parent/Child example was rewritten in this style, the parent/child relationship would be established externally to both the parent and the child:

Parent parent = new Parent("parent");
Child child1 = new Child("child");
Child child2 = new Child("child2");

parent.addChild(child1);
parent.addChild(child2);

This style of composition makes the architecture of the system easier to understand. The relationships between objects at the same architectural level are explicitly defined in the same part of the code, instead of being scattered around the constructors of related objects.

This style also makes it easier to cleanly implement application configuration. The system can interpret a configuration file as directives to instantiate objects and plug them together.

Posted on April 21, 2004 [ Permalink | Comments ]