Ashutosh Jindal Ashutosh Jindal - 5 months ago 13
Java Question

Adapting the Builder pattern for method invocation

This is an attempt to understand a portion of ITEM 40: Design Method Signatures Carefully from Effective Java 2nd Edition.

One of the things suggested to improve method signature readability is to aim for four or fewer parameters. It is suggested that longer parameter lists be managed by using a variety of techniques one of which is as follows :


A third technique that combines aspects of the first two is to adapt
the Builder pattern (Item 2) from object construction to method
invocation. If you have a method with many parameters, especially if
some of them are optional, it can be beneficial to define an object
that represents all of the parameters, and to allow the client to make
multiple “setter” calls on this object, each of which sets a single
parameter or a small, related group. Once the desired parameters have
been set, the client invokes the object’s “execute” method, which does
any final validity checks on the parameters and performs the actual
computation.


I am familiar with the Builder pattern as it is used for object construction, but am not sure whether I have correctly understood how to adapt it to method invocation.

Here is what I have thus far :

( I have attempted to improve the method invocation for the
move
method)

public class Space {

public static class Builder {
// Required parameters
private final int x;
private final int y;
private final int z;

// optional params
private long time = 0;

public Builder(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}

public Builder time(long val) {
time = val;
return this;
}

public void move() {
if (x == 0 || y == 0 || z == 0) {
throw new IllegalArgumentException("Cannot move to the centre of the universe");
}

// Do the actual work here
}
}

// public void move(int x, int y, int z, long time) {
// // Do the work here
// }

public static void main(String[] args) {
new Builder(1, 1, -1).time(1234).move();
}
}


Is my interpretation of Joshua Bloch's advice correct ?

bn. bn.
Answer

I've done reifications of the Builder pattern using an interface defined inside the class I want to instantiate.

See here for extra info on this and the related "bidirectional builder": http://www.javaworld.com/javaworld/jw-01-2004/jw-0102-toolbox.html?page=3

I would do something like the following; although this is overkill perhaps for 3 required fields, it is very helpful for constructing larger objects while accounting for every required field.

public Class Space
{    
    public interface Builder
    {
        public Space build();
        public int x();
        public int y();
        public int z();
    }

    // Build a complete Space object accounting for every field. 
    public Space(Space.Builder spaceBuilder)
    {
        this.x = spaceBuilder.x();
        this.y = spaceBuilder.y();
        this.z = spaceBuilder.z();
        this.time = builder.time();
    }

    // Might not be necessar if you update time when you update x, y, and z
     public Builder time(long val)
     {
            time = val;
            return this;
     }

     public void move()
     {
        // ...
     }

    public static void main(String[] args)
    {
        new Builder()
        {   
            @Override
            public Space build(){ return new Space(this);}

            @Override
            public int x(){ return 1;}

            @Override
            public int y{ return 1;}

            @Override int z{ return -1;}
        }.build().time(1234).move();
    }
}
Comments