Self executing command

133 Views Asked by At

This piece of code is a mix of command and builder patterns:

    CommandBuilder.create("one value").execute();

    CommandBuilder.create("another value").withBigDecimal(BigDecimal.ZERO).withNumber(123).execute();

all it does is a simple printing:

one value
another value 123 0

The goal is to get rid of the execute() method. Which means that after last withXXX() method ( or without any withXXX()) the code would do the printing itself.

Here is the current code:

import java.math.BigDecimal;

public class CommandBuilder {

    private final String value;
    private Integer number;
    private BigDecimal bigDecimal;

    private CommandBuilder(String value) {
        this.value = value;
    }

    public static CommandBuilder create(String value){
        return new CommandBuilder(value);
    }

    public CommandBuilder withNumber(Integer number){
        this.number = number;
        return this;
    }

    public CommandBuilder withBigDecimal(BigDecimal bigDecimal){
        this.bigDecimal = bigDecimal;
        return this;
    }

    public void execute(){
        StringBuilder sb = new StringBuilder();
        sb.append(value);
        if(number != null){
            sb.append(" ").append(number);
        }
        if(bigDecimal != null){
            sb.append(" ").append(bigDecimal);
        }

        System.out.println(sb);
    }
}

Is this possible in java?

EDIT ADDITIONAL INFO

Maybe my mistake is that I didn't give any information about intent. I tried to obfuscate the problem not to make it to hard to comprehend and too broad to answer.

So this piece of code is DSL. It's the simplest case where commands just executes action ( eg. store some info in database). Another case is when a command have to return a value ( full implementation of the Builder Pattern with build() method ) - but in this case there is no problem cause return if always last method in chain.

I think that the real problem is: How to delay evaluation till whole builder methods chain is processed?

2

There are 2 best solutions below

1
On

You could get what you want with a lambda:

import java.math.BigDecimal;
import java.util.function.Consumer;

class CommandBuilder {

    private final String value;
    private Integer number;
    private BigDecimal bigDecimal;

    private CommandBuilder(String value) {
        this.value = value;
    }

    public static void createAndExecute(String value, Consumer<CommandBuilder> consumer){
        CommandBuilder builder = new CommandBuilder(value);
        consumer.accept(builder);
        builder.execute();
    }

    public CommandBuilder withNumber(Integer number){
        this.number = number;
        return this;
    }

public CommandBuilder withBigDecimal(BigDecimal bigDecimal){
    this.bigDecimal = bigDecimal;
    return this;
}

public void execute(){
    StringBuilder sb = new StringBuilder();
    sb.append(value);
    if(number != null){
        sb.append(" ").append(number);
    }
    if(bigDecimal != null){
        sb.append(" ").append(bigDecimal);
    }

    System.out.println(sb);
}

}

And use it like this:

CommandBuilder.createAndExecute("something", builder -> builder.withNumber(1));

I guess it would be better however to set the value also in the Consumer like this:

CommandBuilder.createAndExecute(builder -> builder.withValue("something").withNumber(1));
0
On

You could do smth like this: import java.math.BigDecimal;

public class CommandBuilder {

    private final String value;
    private Integer number;
    private BigDecimal bigDecimal;

    private CommandBuilder(String value) {
        this.value = value;
    }

    public static CommandBuilder create(String value){
        return new CommandBuilder(value);
    }

    public static CommandBuilder create(String value, boolean exec){
        CommandBuilder builder = create(value);
        if(exec){
            execute();
        }
        return builder;

    }


    public CommandBuilder withNumber(Integer number){
        this.number = number;
        return this;
    }

    public CommandBuilder withNumber(Integer number, boolean exec){
        CommandBuilder builder = withNumber(number);
        if(exec){
            execute();
        }
        return builder;
    }

    public CommandBuilder withBigDecimal(BigDecimal bigDecimal){
        this.bigDecimal = bigDecimal;
        return this;
    }

    public CommandBuilder withBigDecimal(BigDecimal bigDecimal, boolean exec){
        CommandBuilder builder = withBigDecimal(bigDecimal);
        if(exec){
            execute();
        }
        return builder;
    }

    public void execute(){
        StringBuilder sb = new StringBuilder();
        sb.append(value);
        if(number != null){
            sb.append(" ").append(number);
        }
        if(bigDecimal != null){
            sb.append(" ").append(bigDecimal);
        }

        System.out.println(sb);
    }
} 

And then use it like following:

CommandBuilder.create("anothervalue").withBigDecimal(BigDecimal.ZERO).withNumber(123, true);

It's some kind of workaround but I actually believe there is no other solution in Java because somehow you have to call a execution method.