4

I use the builder pattern to make an object. The problem is that I have to get arguments from command line. I want to pass one to six arguments and, based on the arguments amount, call the relevant methods.

For example, if I pass 3 args, the program will call first three methods; first four methods for 4 args and so on;

ExampleClass obj = new ExampleClass.Builder()
                .method1(args[0])
                .method2(args[1])
                .method3(args[2])
                .build();

Is it even possible to make it efficiently? The only thing that occurs to me is reflection, but I'm not sure if this is a good idea.

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
Alxa
  • 79
  • 1
  • 2
  • 11
  • Why don't you check `args.length` and then just call `methodX` when args has the required length? – f1sh Nov 04 '20 at 18:15
  • Does the order of the method calls matter? Does `method1()` have to be called first? Does `method2` have to be called second? – Abra Nov 04 '20 at 18:29
  • Yes, it does. `method3` will never be called after `method1`. `method2` cannot be called first. – Alxa Nov 04 '20 at 18:31

2 Answers2

2

The trick is to break up the chain of method calls into separate statements rather than try to do it all in one statement. Save the intermediate builder objects in a temporary variable and then call build() at the end.

ExampleClass.Builder builder = new ExampleClass.Builder();

if (args.length >= 1) { builder = builder.method1(args[0]); }
if (args.length >= 2) { builder = builder.method2(args[1]); }
if (args.length >= 3) { builder = builder.method3(args[2]); }
if (args.length >= 4) { builder = builder.method4(args[3]); }
if (args.length >= 5) { builder = builder.method5(args[4]); }
if (args.length >= 6) { builder = builder.method6(args[5]); }

ExampleClass obj = builder.build();
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
1

You have a queue of methods defined in the order in which they must be executed.

class Builder {
    Queue<Function<String, Builder>> methods 
        = new LinkedList<>(List.of(this::method1, this::method2, this::method3));
}

You have a queue of arguments formed from a raw array.

Queue<String> argsQueue = new LinkedList<>(Arrays.asList(args));

Given that the method query is always longer than the queue of arguments, the algorithm is

Builder builder = new ExampleClass.Builder();
while (!argsQueue.isEmpty()) {
    builder.getMethods().poll().apply(argsQueue.poll());
}
ExampleClass instance = builder.build();

It shouldn't necessarily be queues.

When you add/remove a method, you would need to take care only of methods.

The whole (dummy) example:

class ExampleClass {
    public static void main(String[] args) {
        Queue<String> argsQueue = new LinkedList<>(Arrays.asList(args));

        Builder builder = new ExampleClass.Builder();
        while (!(argsQueue.isEmpty() || builder.getMethods().isEmpty())) {
            builder.getMethods().poll().apply(argsQueue.poll());
        }
        ExampleClass instance = builder.build();
    }

    public static class Builder {

        private final Queue<Function<String, Builder>> methods = new LinkedList<>(List.of(this::method1, this::method2, this::method3));

        public Queue<Function<String, Builder>> getMethods() {
            return methods;
        }

        public Builder method1(String s) {
            System.out.println("method1 " + s);
            return this;
        }

        public Builder method2(String s) {
            System.out.println("method2 " + s);
            return this;
        }

        public Builder method3(String s) {
            System.out.println("method3 " + s);
            return this;
        }

        public ExampleClass build() {
            System.out.println("building...");
            return new ExampleClass();
        }
    }
}
Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142