3

Can one create an extensible class hierarchy in java whose methods are fluent and can be invoked in any order? (YES! see answer below), even for existing classes when you don't have access to the source, provided the methods are fluant!

I'm retrofitting an existing hierarchy and hope to use a factory or at least a generic constructor and (eventually) immutable builder patterns (JB P.14). The methods that set fields return void - it would be better for them to return a generic T instead - that way we will gain the ability to do method chaining (they all call super now).

Goals:
1. Avoid having to create a static getFactory() method in every class.
2. Simple method signatures.
3. Create a factory method that is generic, yet will catch problems at compile time.
4. Get compile time errors instead of run time errors when mistakes are made.

As requested, the non-generic code is very simple, but doesn't work.

public class A {
    private String a = null;
    protected A setA(String a){
        this.a = a;
        return this;//<== DESIRE THIS TO BE CHAINABLE
    }
    protected static A factory(){
       return new A();
    }
}  

.

public class B extends A {
    private String b = null;
    protected Foo setB(String b){
        this.b = b;
        return this;//<== DESIRE THIS TO BE CHAINABLE
    }
    protected static B factory(){
        return new B();
    }
}

Now a caller could TRY to call B.factory().setA("a").setB("b")//won't compile

But that can't compile because setA() returns an A, not a B. You COULD make it work by overriding the setA() in B, calling setB() and returning B instead of the A. To avoid delegating for each of those methods is the point. I simply want an extensible group of chainable class methods that can be invoked in any order. B.getFactory().B("b").A("a") works obviously.

ggb667
  • 1,881
  • 2
  • 20
  • 44
  • 2
    related: [Is there a way to refer to the current type with a type variable?](http://stackoverflow.com/questions/7354740/is-there-a-way-to-refer-to-the-current-type-with-a-type-variable) – Paul Bellora Aug 02 '13 at 23:19
  • There is no reason to write `interface Chainable>` instead of just `interface Chainable`; same with `Foo`, `Bar`, `Fubar`, and `FooBarBar` – newacct Aug 05 '13 at 05:20
  • "Correct because the this is the same as the type of Foo" No, it is not correct. `this` may not be a `T`. – newacct Aug 05 '13 at 09:15
  • 1
    Can you post the original non-generic version? It's hard to see just what problem you're trying to solve. – David Moles Sep 26 '13 at 22:14
  • Not to sure about java, but some programming language allow to Shadow function. So Bar could shadow setA and return a Bar instead of a Foo. The new setA would just call the one from the base class. – the_lotus Nov 01 '13 at 18:12
  • @GGB667 Can you please explain how [the post I linked to](http://stackoverflow.com/q/7354740/697449) doesn't answer your bounty question? It seems like "extensible" is the problem point but it would help for you to clarify that. – Paul Bellora Nov 03 '13 at 00:51
  • If you wish to call setB(), you'll need to know the item is of type B, not A. So the simplest solution seems to me to forget about inheritance. Why do you think you need it? – flup Nov 03 '13 at 18:48
  • Because the class hierarchy already exists and there are hundreds of existing protected methods. – ggb667 Nov 06 '13 at 15:03
  • Does anyone know why the syntax highlighting is working but the indentation is not? – ggb667 Nov 07 '13 at 15:26
  • 1
    Also take a look at the "curiously recurring template pattern" by Coplien. – flup Nov 07 '13 at 21:00
  • Yes, did that. but it becomes a mess pretty fast and it's hard to maintain and read and use. – ggb667 Jan 27 '14 at 20:58

4 Answers4

3

The answer (to my surprise and satisfaction) is YES. I answered this question myself: You can do this with a little work if the method invocations return instances of the class in question (see chainable below). I also found an even easier way do this if you can edit the top level source:

In the top level class (A):

protected final <T> T a(T type) {
    return type
}

Assuming C extends B and B extends A.

Invoking:

C c = new C();
//Any order is fine and you have compile time safety and IDE assistance.
c.setA("a").a(c).setB("b").a(c).setC("c");

Example 1 and 3 are ways to make a existing class hierarchy fluent and to allow methods to be called in any order so long as the existing classes are fluent (but you don't have access to or can't change the source). WAY2 is an example where you do have access to the source, and want the calls to be as simple as possible.

Full SSCCE:

import static java.lang.System.out;

public class AATester {
    public static void main(String[] args){

        //Test 1:
        for(int x: new int[]{ 0, 1, 2 } ){
            A w = getA(x);
            //I agree this is a nasty way to do it... but you CAN do it.
            Chain.a(w.setA("a1")).a(w instanceof C ? ((C) w).setC("c1") : null );
            out.println(w);
        }

        //Test for WAY 2: Hope this wins Paul Bellora's approval 
        //for conciseness, ease of use and syntactic sugar.
        C c = new C();
        //Invoke methods in any order with compile time type safety!
        c.setA("a2").a(c).setB("b2").a(c).set("C2");
        out.println(w);

        //Example 3, which is Example 1, but where the top level class IS known to be a "C"
        //but you don't have access to the source and can't add the "a" method to the 
        //top level class.  The method invocations don't have to be as nasty as Example 1.
        c = new C();
        Chain.a(c.setA("a3")).a(c.setB("b3")).a(c.setC("c3"));//Not much larger than Example 2.
        out.println(w);
    }
    public static getA(int a){//A factory method.
        A retval;//I don't like multiple returns.
        switch(a){
            case 0:  retval = new A(); break;
            case 1:  retval = new B(); break;
            default: retval = new C(); break;
        }
        return retval;
    }
}

Test class A

public class A {
   private String a;
   protected String getA() { return a; }

   //WAY 2 - where you have access to the top level source class.
   protected final <T> T a(T type) { return type; }//This is awesome!       

   protected A setA(String a) { this.a=a; return this; }//Fluent method
   @Override
   public String toString() {
      return "A[getA()=" + getA() + "]";
   }
}

Test class B

public class B extends A {
   private String b;
   protected String getB() { return b; }
   protected B setB(String b) { this.b=b; return this; }//Fluent method
   @Override
   public String toString() {
      return "B[getA()=" + getA() + ", getB()=" + getB() + "]\n  " 
      + super.toString();
  }
}

Test Class C

public class C extends B {
   private String c;
   protected String getC() { return c; }
   protected C setC(String c) { this.c=c; return this; }//Fluent method
   @Override
   public String toString() {
      return "C [getA()=" + getA() + ", getB()=" + getB() + ", getC()=" 
             + getC() + "]\n  " + super.toString();
   }
}

The Chain class

/**
 * Allows chaining with any class, even one you didn't write and don't have 
 * access to the source code for, so long as that class is fluent.
 * @author Gregory G. Bishop ggb667@gmail.com (C) 11/5/2013 all rights reserved. 
 */
public final class Chain {
   public static <K> _<K> a(K value) {//Note that this is static
      return new _<K>(value);//So the IDE names aren't nasty
   }
}

Chain's helper class.

/** 
 * An instance method cannot override the static method from Chain, 
 * which is why this class exists (i.e. to suppress IDE warnings, 
 * and provide fluent usage). 
 *
 * @author Gregory G. Bishop ggb667@gmail.com (C) 11/5/2013 all rights reserved.
 */
final class _<T> {
   public T a;//So we may get a return value from the final link in the chain.
   protected _(T t) { this.a = t }//Required by Chain above
   public <K> _<K> a(K value) {
      return new _<K>(value);
   }
}

Output:

A [get(A)=a]
B [get(A)=a, getB()=null]
  A [getA()=a]
C [getA()=a, getB()=null, getC()=c)]
  B [get(A)=a, getB()=null]
  A [get(A)=a]

QED. :)

I've not ever seen anyone do this; I think it could be a new and potentially valuable technique.

P.S. With regard to the "elvis like usage", it is 1 or 2 lines vs 8 or more.

Book b = null; 
Publisher p = null; 
List books = null; 
String id = "Elric of Melnibone";

books = Chain.a(b = findBook(id)).a(b != null ? p = b.getPublisher() : null)
                                 .a(p != null ? p.getPublishedBooks(): null).a;

out.println(books==null ? null : Arrays.toString(books.toArray()));

vs:

Book b = null; 
Publisher p = null; 
List books = null; 
String id = "Elric of Melnibone";

b = findBook(id);
Array[] books = null;
if( b != null ) {
    p = b.getPublisher();
    if(p != null) {
        books = p.getPublishedBooks();
    }
} 

out.println(books==null ? null : Arrays.toString(books.toArray()));

No NPE, and if the chain completes you get all the books published by the publisher of "Elric of Melnibone" (i.e. all the books "Ace" publishers has published), and if not you get a null.

Community
  • 1
  • 1
ggb667
  • 1,881
  • 2
  • 20
  • 44
  • 4
    Hmm, this is an amusing (ab)use of nulls, ternaries, and fluent methods. Unfortunately it puts a pretty horrific syntactic burden on the caller. "Fluent calls" are syntactic sugar to begin with, so it seems contradictory to clutter them with so much checking and casting. I would hate to have to maintain any serious code using this pattern, esp. if I didn't write it. Still, it's an interesting idea - thanks for sharing. – Paul Bellora Nov 06 '13 at 05:43
  • 1
    More low-level: note that `(w instanceof C ? (C) w : null ) == null ? null : ((C) w).setC("c")` can be reduced to `(w instanceof C ? ((C) w).setC("c") : null )` (`instanceof` checks for null). There are also a number of compiler errors in your example: `getA` needs a return type, `B` should presumably extend `A`, and so on for `C`, and a few missing semicolons. I'm just pointing these out since it reads like an SSCCE. – Paul Bellora Nov 06 '13 at 05:46
  • Paul, I’m sorry about the syntax errors. Thanks for pointing out a better way to do the null assistance. I fixed the semicolons and extends. The intent is to make it a SSCCE. I typed this by hand without the benefit of an IDE earlier. The admittedly complex Example 1 was not the main point. Please note WAY2 and Example 3 which are much cleaner and more useful examples. I was just showing that it is possible to do what was being asked in the other question. Your link helped me think about the problem properly Paul, which led me to my solution (thank you). – ggb667 Nov 06 '13 at 19:57
  • 1
    Well, the 2nd and 3rd approaches are definitely cleaner. The 3rd doesn't really seem "fluent" to me since it relies on nested calls to `a` - to compile its example would need more parentheses on the end, demonstrating the nested-ness. The 2nd makes interesting use of the `a` method to essentially hand a more specific type of itself back and continue the chain - it gets closest to being fluent. – Paul Bellora Nov 07 '13 at 06:09
  • 1
    However, all your approaches rely on the fluent interface type being declared locally. I feel like a true fluent interface shouldn't need to be declared at all, for example `String s = new StringBuilder().append("asdf").append(42).append("qwerty").toString();` or using your hierarchical example, `new C().setA("").setC("")`. I think the crux of it is: if it gets declared locally, why not just ditch the fluency and write multiple statements? All the same, this has been an interesting thought experiment - thanks for involving me. – Paul Bellora Nov 07 '13 at 06:11
  • That's true, you do require a local reference. But it lets you basically perform an elvis operator on each indirection. As to why? It lets you assign a value on a single line, and keep the flow of your program (not with the elvisistic abuse obviously). – ggb667 Nov 07 '13 at 16:02
  • 1
    A nicely fluent interface would/could/should read more like `same(publisher()).as(Book.with(title("Elric of Melnibone"))`. Or something wildly different. But you can do better than chaining your getters. – flup Nov 08 '13 at 00:35
  • You can make the names whatever you want. The classes (and the getters) already exist. – ggb667 Nov 19 '13 at 19:31
3

I believe there is a way to do this with generics... Syntax is a little less clean than the desired...

Here is the client code...

    B<B> b = B.factoryB();
    b.setA("a").setB("b");

    A<A> ba = A.factoryA();

    ba.setA("a");

Top level (real) class

public  class A<S extends A> extends Chained<S> {
    private String a = null;

    protected A() {
        }

    public S setA(String a) {
        this.a = a;
        return me();
    }
    public static A<A> factoryA() {
        return new A<A>();
    }
}

Example Subclass

public  class B<S extends B> extends A<S> {
    private String b = null;

    B() {
    }

    public S setB(String b) {
        this.b = b;
        return me();
    }
    public static B<B> factoryB() {
        return new B<B>();

    }

}

Helper

public  abstract class Chained<S extends Chained> {
    // class should be extended like:
    // ... class A<S extends A> extends Chained<S>

    public Chained() {
    }

    public final S me() {
        return (S) this;
    }
}

It's far from perfect and can be made not to work (if you really wanted to)

Alan Spencer
  • 566
  • 3
  • 12
  • 1
    If you opt for the recursive bounds you should avoid raw types, e.g. `Chained>`. – Paul Bellora Nov 07 '13 at 22:39
  • Unless you do what Paul says, this won't actually work past the first invocation. For instance `c.setA("a").setA("a").set("c");` won't compile because the second `setA` gives you an `A`, not a `C`. If you do what Paul says though it works. However you don't need Chained at all, `A` can just be `public class A> {`. The protected final `S me() { return (S)this; }` at least will work for all of the children. – ggb667 Nov 11 '13 at 21:09
  • May I offer something additional? The reference `this` can be passed to `protected final S me(A ref)` as a parameter, and then compared to the `this` of the `me()` method - if they don't match an exception can be thrown, which will guard against one of the holes in the curiously recurring template pattern (passing the wrong object or `null`). EX: `protected final S me(A type) { if( this!= ref ) { throw new IllegalArgumentException(ref==null?"Null received (required this).":"Type passed in was not this." } return (S)this; }` – ggb667 Nov 11 '13 at 21:12
  • I don't see a way to either construct or return an S of the appropriate type from a factory without a untyped warning. Is there a way to do that? – ggb667 Nov 14 '13 at 16:17
  • I believe you're right, you need something to "cast" it from a B to a B unfortunately. Unless, that is, you list use B>>> for as many as you'll need... :( – Alan Spencer Nov 19 '13 at 17:05
0

If source code is accessible, by extending what Alan wrote, I would add supplementary classes to hide generics while allowing inheritance and very compact syntax. BaseA and BaseB do the hierarchy while A and B do hide the generics.

BaseA
 +- A
 +- BaseB
     +- B


public class BaseA<S extends BaseA<?>> {
    private String a = null;

    protected BaseA() {
    }

    @SuppressWarnings("unchecked")
    public S setA(String a) {
        this.a = a;
        return (S) this;
    }

}

public class A extends BaseA<A> {
    public static A factoryA() {
        return new A();
    }
}

public class BaseB<S extends BaseB<?>> extends BaseA<S> {
    private String b = null;

    protected BaseB() {
    }

    @SuppressWarnings("unchecked")
    public S setB(String b) {
        this.b = b;
        return (S) this;
    }

}

public class B extends BaseB<B> {
    public static B factoryB() {
        return new B();
    }
}

public class Main {
    public static void main(String[] args) {
        B.factoryB().setA("").setB("").setB("").setA("").setA("");
    }
}
sebtic
  • 198
  • 1
  • 6
-1

A fluent interface is a different concern from the normal set of command-query methods that you already have. Separation of concerns makes it a good idea to separate them.

Since you have an existing hierarchy of code: Write a fluent facade that does the dirty work for you.

See also Martin Fowler: Domain-Specific Languages, 4.2: The need for a Parsing Layer.

flup
  • 26,937
  • 7
  • 52
  • 74
  • Though this could be done, everything would need to be essentially duplicated in each class, and the other classes add additional methods, therefore this solution breaks down as additional methods are added at every level in the code. – ggb667 Nov 07 '13 at 15:35
  • I've updated with some more explanation why this is a good idea. – flup Nov 07 '13 at 20:44