Adapting the Builder Template to Call a Method

This is an attempt to understand part of ITEM 40: CONFIRMING APPROVAL PROCESS APPROACHES from Effective Java 2nd Edition.

One of the things suggested to improve the readability of code signatures is to focus on four or more parameters. It is proposed to manage longer lists of parameters using various methods, one of which is as follows:

The third method, which combines the aspects of the first two, is to adapt the Builder template (element 2) from building the object to the invocation method. If you have a method with many parameters, especially if some of them are optional, it may be useful to define an object that represents all the parameters and allows the client to make several โ€œsetterโ€ calls to this object, each of which sets up one parameter or a small related group. Once the required parameters have been set, the client calls the "execute" objects method, which makes any final parameter validation and calculation.

I am familiar with the Builder pattern because it is used to build an object, but I'm not sure if I understood correctly how to adapt it to a method call.

Here is what I have so far:
(I tried to improve the method call 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?

+6
source share
3 answers

I made reifications of the Builder template using the interface defined inside the class I want to create.

See here for more information about this and the corresponding "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 possible for the three required fields, it is very useful for building larger objects when taking into account each 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(); } } 
+1
source

As I see this, you will have your own builder class associated with the parent class (for example, with a non-static inner class ), and clients will use it as follows:

 Space space = new Space(); ... Space.MoveBuilder moveBuilder = space.new MoveBuilder(1, 1, -1); moveBuilder.setTime(1234); moveBuilder.execute(); 

This could be further simplified using the free constructor and factory method:

 space.startMove(1, 1, -1).withTime(1234).endMove(); 
+2
source

I created the Java Invoice Builder , which at compile time creates collectors that invoke methods. It also adds support for default values โ€‹โ€‹for method parameters in Java.

It can be used for classes or interfaces. By adding @GenerateMethodInvocationBuilder and @Default annotations to the type.

 @GenerateMethodInvocationBuilder public interface BitBucketServerService { @GET("/rest/api/1.0/projects/{projectkey}/repos/{repositoryslug}/pull-requests?direction={direction}&at={at}&state={state}&order={order}&withattributes={withattributes}&withproperties={withproperties}") Call<BitbucketServerResponse<BitBucketServerPullRequest>> pullRequests(// @Default("PROJ") @Query("projectkey") String projectKey,// @Default("REPO") @Query("repositoryslug") String repositoryslug,// @Default("INCOMING") @Query("direction") String direction,// @Default("23") @Query("at") String at,// @Default("OPEN") @Query("state") String state,// @Default("NEWEST") @Query("order") String order,// @Default("true") @Query("withattributes") String withattributes,// @Default("true") @Query("withproperties") String withproperties); } 

Then, when the compiler is compiled, a builder will be created, and you can call it with the default parameters:

 BitBucketServerServicePullRequestsBuilder.pullRequests() .invoke(bitBucketServerService); 

Or set any parameters you like:

 BitBucketServerServicePullRequestsBuilder.pullRequests() .withAt("24") .invoke(bitBucketServerService); 

More on GitHub: https://github.com/tomasbjerre/java-method-invocation-builder

0
source

All Articles