Picocli: Отдельная ответственность за заполнение класса и выполнение приложения - PullRequest
0 голосов
/ 08 января 2019

Перед использованием cli у меня будет начальный класс, который вызывает мой класс ApplicationPropertiesProvider (который читает мой файл свойств), а затем запускает бизнес-логику. Таким образом, произошло разделение, у ApplicationPropertiesProvider была только одна работа.

Теперь с picocli, руководство / документация утверждает, что я должен использовать CommandLine.run (objectToPopulate, args) или CommandLine.call (objectToPopulate, args). Поэтому класс, заполняемый параметрами cli (ApplicationPropertiesProvider), должен реализовывать Runnable или Callable. Теперь я могу просто вставить свой стартовый код класса Starter в метод run () или call () и отказаться от класса Starter. Но мне это не нравится, я хочу разделить класс, просто содержащий свойства, и мой класс Starter.

Вид грязного обходного пути, о котором я подумал и показал в своем примере ниже, будет состоять в том, чтобы передать аргументы из метода main конструктору моего класса Starter, заполнить ApplicationPropertiesProvider с помощью CommandLine.run (), но реализовать только пустой run () или вызовите метод (), чтобы он немедленно вернулся в мой начальный класс, где я затем начал бизнес-логику. Это было бы результатом, о котором я прошу (разделение), но в таком случае это кажется действительно глупым.

Также еще один вопрос, который только что возник: если у меня есть стандартный случай наличия нескольких классов, содержащих бизнес-код, а также их собственные свойства (вместо одного класса, предоставляющего свойство): возможно ли заполнить несколько разных классов одним кликом? вызов, т.е. вызов «команды test.jar --a --b», где параметр «a» идет прямо к экземпляру класса «X», а «b» - к экземпляру «Y»?

public class Starter  {
    public static void main(String[] args) {
        new Starter(args);
    }

    public Starter(String[] args) {
        app = ApplicationPropertiesProvider.getInstance();
        CommandLine.run(app, args);
        //then kick off the business logic of the application
    }
}

@Command(...)
public class ApplicationPropertiesProvider implements Runnable {
    //annotated properties
    @Option(...)
    private String x;

    @Override
    public void run() { }

1 Ответ

0 голосов
/ 10 января 2019

Методы run и call являются удобными методами, позволяющими приложениям сокращать свой шаблонный код. Вам не нужно использовать , чтобы использовать их. Вместо этого вы можете использовать метод parse или parseArgs. Это выглядит примерно так:

1   @Command(mixinStandardHelpOptions = true)
2   public class ApplicationPropertiesProvider { // not Runnable
3       //annotated properties
4       @Option(...)
5       private String x;
6       // ...
7   }
8
9   public class Starter  {
10      public static void main(String[] args) {
11          ApplicationPropertiesProvider app = ApplicationPropertiesProvider.getInstance();
12          try {
13              ParseResult result = new CommandLine(app).parseArgs(args);
14              if (parseResult.isUsageHelpRequested()) {
15                  cmd.usage(System.out);
16              } else if (parseResult.isVersionHelpRequested()) {
17                  cmd.printVersionHelp(System.out);
18              } else {
19                  new Starter(app); // run the business logic
20              }
21          } catch (ParameterException ex) {
22              System.err.println(ex.getMessage());
23              ex.getCommandLine().usage(out, ansi);
24          }
25      }
26
27      public Starter(ApplicationPropertiesProvider app) {
28          // kick off the business logic of the application
29      }
30  }

Это хорошо, просто строки 11-24 представляют собой шаблонный код. Вы можете опустить это и позволить picocli сделать эту работу за вас, позволив аннотированному объекту реализовать Runnable или Callable.

Я понимаю вашу мысль о разделении интересов и имею разные классы для бизнес-логики и класса, который обладает свойствами. У меня есть предложение, но сначала позвольте мне ответить на ваш секундный вопрос:

Можно ли заполнить несколько разных классов одним вызовом cli?

Picocli поддерживает "Mixins", которые позволяют вам это делать. Например:

class A {
    @Option(names = "-a") int aValue;
}
class B {
    @Option(names = "-b") int bValue;
}
class C {
    @Mixin A a;
    @Mixin B b;
    @Option(names = "-c") int cValue;
}

// populate C's properties as well as the nested mixins
C c = CommandLine.populate(new C(), "-a=11", "-b=22", "-c=33");
assert c.a.aValue == 11;
assert c.b.bValue == 22;
assert c.cValue   == 33;

Теперь давайте соединим все это:

class A {
    @Option(names = "-a") int aValue;
    @Option(names = "-b") int bValue;
    @Option(names = "-c") int cValue;
}
class B {
    @Option(names = "-x") int xValue;
    @Option(names = "-y") int yValue;
    @Option(names = "-z") int zValue;
}
class ApplicationPropertiesProvider {
    @Mixin A a;
    @Mixin B b;
}
class Starter implements Callable<Void> {
    @Mixin ApplicationPropertiesProvider properties = ApplicationPropertiesProvider.getInstance();

    public Void call() throws Exception {
        // business logic here
    }

    public static void main(String... args) {
        CommandLine.call(new Starter(), args);
    }
}

Это дает вам разделение интересов: свойства расположены в ApplicationPropertiesProvider, бизнес-логика в классе Starter. Это также позволяет группировать свойства, которые логически принадлежат друг другу, в отдельные классы, вместо того, чтобы иметь единый полигон в ApplicationPropertiesProvider.

Класс Starter реализует Callable; это позволяет вам опустить приведенную выше логическую схему и запустить приложение в одной строке кода в main

...