Le 08/08/2013 01:13, Gilles a écrit : > On Wed, 7 Aug 2013 18:04:39 -0400, Evan Ward wrote: >> Hi, >> >> As a grateful user of the library, I would prefer the Fluent API and >> immutability. I have implemented this pattern in some of my own projects >> and it makes the API very easy to use. >> >> Most of the time, when the interface is small, inheritance isn't really >> necessary and I just implement the withXxx methods directly. A one line >> 'return new ...' isn't hard to maintain with IDE support for renames and >> add/remove parameters. >> >> When inheritance is part of the API you can parametrize the base class >> and add an abstract create method, like so: >> >> abstract class Parent<T extends Parent> implements Interface{ >> protected abstract T create(...); >> public T withXxx(x){ >> return create(x, ...); >> } >> } >> class Child extends Parent<Child> { >> protected T create(...){ >> return new Child(...); >> } >> } > > Did I understand correctly that the base class "Parent" is assumed to > have complete knowledge of all its subclass "Child": the signature of > "create" must contain the arguments for fields declared in "Child"?
I don't think so. The following code displays: original: 3 1.0 changed : 2 1.0 This means that despite withI() was defined only in the base class and despite this base class doesn't know anything about the Derived class, the withI call was properly executed and the modified object was really a Derived instance. The drawback is that the user had to do the cast from BAse to Derived when calling withI in the statement: Derived changed = (Derived) original.withI(2); I tried a few tricks with the templates to prevent this, but did not succeed. I'm not sure it is a big problem, though. interface IChanger<T extends IChanger<T>> { public T withI(int i); } interface DChanger<T extends DChanger<T>> { public T withD(double d); } class Base implements IChanger<Base> { int i; public Base(int i) { this.i = i; } public Base create (int i) { return new Base(i); } public Base withI(int i) { return create(i); } public int getI() { return i; } } class Derived extends Base implements DChanger<Derived> { double d; public Derived(int i, double d) { super(i); this.d = d; } public Derived create(int i, double d) { return new Derived(i, d); } public Derived create(int i) { return new Derived(i, d); } public Derived withD(double d) { return create(i, d); } public double getD() { return d; } } Derived original = new Derived(3, 1.0); Derived changed = (Derived) original.withI(2); System.out.println("original: " + original.getI() + " " + original.getD()); System.out.println("changed : " + changed.getI() + " " + changed.getD()); best regards, Luc > > Gilles > >> The create method forces the implementing class to have an appropriate >> constructor. The withXxx methods are implemented only once and the >> inheriting class only has to implement one more method. The type >> parameterization is invisible to the user if they use the Child class or >> the Interface. The down side is that Child can not be subclassed >> correctly, but this enforces the rule "design for inheritance or prevent >> it". This is similar to the pattern used in MarkupBuilder from jatl.[1] >> (jatl uses a mutable base class) >> >> Regards, >> Evan >> >> [1] >> >> http://code.google.com/p/jatl/source/browse/src/main/java/com/googlecode/jatl/MarkupBuilder.java >> >> >> --------------------------------------------------------------------- >> To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org >> For additional commands, e-mail: dev-h...@commons.apache.org > > > --------------------------------------------------------------------- > To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org > For additional commands, e-mail: dev-h...@commons.apache.org > --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org For additional commands, e-mail: dev-h...@commons.apache.org