javaconstructorimmutabilityflyweight-pattern

Skipping superfluous verification with immutable flyweights


I have an immutable class that looks something like this:

final class Foo {
  private final String name;
  private final MutableObject mo;

  public Foo(String name, MutableObject mo) {
    mo = mo.clone();
    if(!Foo.testValidity(mo)) // this test is very expensive
      throw new IllegalArgumentException();

    this.name = name;
    this.mo = mo;
  }

  public Foo bar(Foo that) {
    return new Foo(this.name, that.mo);
  }
}

The bar method returns a Foo object by mixing the internals of two existing Foo objects. Because the MutableObject is already in a Foo object, it is guaranteed to be valid and doesn't need copying or verification (which the constructor currently does).

Because the verification (and possibly the clone?) are expensive, I'd like to avoid them if possible. What's the best way to do this? This is was what I came up with:

final class Foo {
  private final String name;
  private final MutableObject mo;

  public Foo(String name, MutableObject mo) {
    this(name, mo, VerificationStyle.STRICT);
  }

  private Foo(String name, MutableObject mo, VerificationStyle vs) {
    if(vs == VerificationStyle.STRICT) {
      mo = mo.clone();
      if(!Foo.testValidity(mo)) // this test is very expensive
        throw new IllegalArgumentException();
    }

    this.name = name;
    this.mo = mo;
  }

  public Foo bar(Foo that) {
    return new Foo(this.name, that.mo, VerificationStyle.LENIENT);
  }

  private static enum VerificationStyle { STRICT, LENIENT; }
}

I thought that, at least, it would be cleaner/clearer than using a dummy parameter and less error prone than swapping the order, but is there a better way to do this? How would this normally be accomplished?


Solution

  • Maybe hide the constructor altogether and create new instances using a factory-like method, e.g.:

      private Foo(String name, MutableObject mo) {
        this.name = name;
        this.mo = mo;
      }
      public Foo bar(Foo that) {
        return new Foo(this.name, that.mo);
      }
      public static Foo create(String name, MutableObject mo) {
        mo = mo.clone();
        if(!Foo.testValidity(mo)) // this test is very expensive
          throw new IllegalArgumentException();
        return new Foo(name, mo);
      }