HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaspringMinor

Circular dependencies between immutable objects; the Freeze Pattern

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
theobjectsfreezecircularbetweenimmutablepatterndependencies

Problem

Generally, I structure small threadsafe immutable objects like this:

public class SmallObject {
    private final String state;
    public SmallObject(final String state) {
        this.state = state;
    }

    // ...
}


And then wire these up in Spring like this:


    


However, this leads to complications with circular dependencies. To keep the immutability when this happens, I use a "freeze" pattern, where the variables are set once. This is what I want reviewed:

public class SmallObject {
    private String state = null;

    public void setState(String state) {
        if (this.state != null) {
            throw new IllegalStateException("state already set: '" + state + "'.");
        }
        this.state = state;
    }

    private void ensureInitialized() {
        if (this.state == null) {
            throw new IllegalStateException(
                "state must be set before this instance is used."
            );
        }
    }

    // ... For every additional method on the object, I call 
    // ensureInitialized() first.
}


And then wire them up like this:


    

Solution

This class should instead use an AtomicReference to ensure the state is kept valid. Alternatively, you should incorporate thread-safe handling of the String.

Consider:

private final AtomicReference stateref = new AtomicReference();

public void setState(final String state) {
    // only one initializer will succeed (assuming state is not null)...
    if (!stateref.compareAndSet(null, state)) {
        throw new IllegalStateException("state already set: '" + state + "'.");
    }
}

private void ensureInitialized() {
    if (stateref.get() == null) {
        throw new IllegalStateException(
            "state must be set before this instance is used."
        );
    }
}


This pattern ensures usage is consistent, there can be only one initialization of the instance, and that any thread-unsafe practices are handled well.

Code Snippets

private final AtomicReference<String> stateref = new AtomicReference<String>();

public void setState(final String state) {
    // only one initializer will succeed (assuming state is not null)...
    if (!stateref.compareAndSet(null, state)) {
        throw new IllegalStateException("state already set: '" + state + "'.");
    }
}

private void ensureInitialized() {
    if (stateref.get() == null) {
        throw new IllegalStateException(
            "state must be set before this instance is used."
        );
    }
}

Context

StackExchange Code Review Q#20163, answer score: 7

Revisions (0)

No revisions yet.