1

I have a java project that involves building some pretty complex objects. There are quite a lot (dozens) of different ones and some of them have a HUGE number of parameters. They also need to be immutable.

So I was thinking the builder pattern would work, but it ends up require a lot of boilerplate. Another potential solution I thought of was to make a mutable class, but give it a "frozen" flag, a-la ruby.

Here is a simple example:

public class EqualRule extends Rule {
    private boolean frozen;
    private int target;

    public EqualRule() {
        frozen = false;
    }

    public void setTarget(int i) {
        if (frozen) 
            throw new IllegalStateException(
                "Can't change frozen rule.");

        target = i;
    }

    public int getTarget() {
        return target;
    }

    public void freeze() {
        frozen = true;
    }

    @Override
    public boolean checkRule(int i) {
        return (target == i);
    }
}

and "Rule" is just an abstract class that has an abstract "checkRule" method.

This cuts way down on the number of objects I need to write, while also giving me an object that becomes immutable for all intents and purposes. This kind of act like the object was its own Builder... But not quite.

I'm not too excited, however, about having an immutable being disguised as a bean however.

So I had two questions: 1. Before I go too far down this path, are there any huge problems that anyone sees right off the bat? For what it's worth, it is planned that this behavior will be well documented... 2. If so, is there a better solution?

Thanks

5
  • 1
    This looks awfully like the builder pattern: Foo().setBar(5).addBaz(1).addBaz(2).build(). Upon the final build() call, the builder object returns an immutable implementation of Foo, which can as well be the builder itself in frozen state.
    – 9000
    Commented May 25, 2013 at 17:04
  • So basically, you're saying change the setters from void to the type of object itself and I'm in business? I guess since at no point will I be using these different rules in their subtype form will it really matter that they have setters on them... Since type erasure will make them be forgotten. Commented May 25, 2013 at 17:31
  • Making setters return this is another pattern, called fluid interface; to me it seems very convenient. One of the nice things about it is that a "setter" becomes a "modifier" that can return a different object (not this) is a particular step allows for a different, optimized representation.
    – 9000
    Commented May 25, 2013 at 18:06
  • I'm aware of fluid interface but not really that up on design patters in general, so thank you. If you want to submit that as an answer then I can accept it. Commented May 25, 2013 at 18:32
  • It looks really crappy to me if you use flag, go for mutable or not there is no try.
    – Mateusz
    Commented May 26, 2013 at 7:41

2 Answers 2

1

Here is @9000 suggestion with a fluent setter that returns a new object:

public abstract class Rule {

    public Rule shallowClone() {
        // see http://stackoverflow.com/questions/5395501/shallow-vs-deep-copies-in-immutable-objects
    }
}

public class EqualRule extends Rule {

    private int target;

    public int getTarget() {
        return target;
    }

    public EqualRule setTarget(int target) {
         EqualRule clone = super.shallowClone();
         clone.target = target;
         return clone;
    }

    @Override
    public boolean checkRule(int i) {
        return (target == i);
    }
}

boolean result = new EqualRule().setTarget(1).checkRule(1);

Pros: Allows creation of immutable objects in a strongly typed manner (less human errors when using the POJO).

Cons: More human errors when implementing the POJO.

0

If you don't want to pass a mutable object into an immutable object (or vice versa) you can use a map.

public class EqualRule extends Rule {

    private final int target;

    public EqualRule(final Map<String,Object> config) {
      this.target = config.get("target");
    }

    public int getTarget() {
        return target;
    }

    @Override
    public boolean checkRule(int i) {
        return (target == i);
    }
}

You can automate this using the Jackson library. It can inject data to immutable objects (final members) or fake immutability (private setter) automatically. See the @JsonProperty annotation in the Jackson library:

public class EqualRule extends Rule {

    private final int target;

    @JsonCreator
    public EqualRule(@JsonProperty("target") int target) {
      this.target = target;
    }

    public int getTarget() {
        return target;
    }

    @Override
    public boolean checkRule(int i) {
        return (target == i);
    }
}

final ObjectMapper mapper = new ObjectMapper(); //singleton
final ObjectNode node = mapper.createObjectNode();
node.put("target", 1);
// not as efficient since internally causes serialization/deserialization.
EqualRule rule = mapper.convertValue(node, EqualRule.class);

There is a side benefit that the conversion would allow you to make the code less vulenrable to schema changes and more backwards compatible. For example when converting from json to pojo you can handle or ignore unknown properties.

2
  • Hmm.. That looks like an interesting thing to do, but in this case it's not being serialized at all so I can't really justify importing a whole external json library. I have heard good things about jackson in the past before but it's not really applicable. Commented May 25, 2013 at 22:06
  • Added more code to explain the tradeoffs vs passing a map to the cotr. Commented May 26, 2013 at 7:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.