Цепной порядок в Гуаве - PullRequest
       7

Цепной порядок в Гуаве

11 голосов
/ 11 февраля 2011

Я немного новичок в Гуаве, и это стиль.Я определенно копаю это, но одна вещь, которую я продолжаю спотыкаться, - это порядок цепочек методов.Кажется, эта проблема возникает чаще всего при использовании соединения Ordering s.Я должен продолжать задавать себе такие вопросы, как:

  • Куда уходит natural?
  • Куда уходит nullFirst (или последний)?
  • Какой nullsFirst что делает?(В приведенном ниже примере, один для хоста, один для фамилии, один для имени?)

Вот пример того, над чем я только что работал.Это выглядит громоздко, и я просто не уверен, правильно ли я все это собрал.У меня есть несколько JUnits для его проверки, и, кажется, все в порядке, но всегда есть такие причудливые граничные случаи.

Ordering<Host> lastNameThenFirstNameOrdering = Ordering.natural().nullsFirst().onResultOf(new Function<Host, String>() {
    public String apply(Host host) {
        return host.getLastName();
    }}).compound(Ordering.natural().nullsFirst().onResultOf(new Function<Host, String>() {
    public String apply(Host host) {
        return host.getFirstName();
    }})).nullsFirst();

Что касается актуального вопроса: есть ли четко определенное правило для того, как эти вещи выполняются?Кажется, это в порядке очереди, но у меня возникают проблемы с сообщением об этом.

edit: Просто хотел указать на большой, уродливый код, который я пытался заменить:

    Ordering<Host> ordering2 = new Ordering<Host>() {
        public int compare(Host host1, Host host2) {
            if (host1 == null || host2 == null) {
                return host1 == host2 ? 0 : ((host1 == null) ? -1 : 1); 
            }
            if(host1.getLastName() != null || host2.getLastName() != null){
                if (host1.getLastName() == null) {
                    return -1;
                } else if (host2.getLastName() == null) {
                    return 1;
                }

                if (host1.getLastName().compareTo(host2.getLastName()) != 0) {
                    return host1.getLastName().compareTo(host2.getLastName());
                }
            }

            if (host1.getFirstName() == null) {
                return -1;
            } else if (host2.getFirstName() == null) {
                return 1;
            }

            return host1.getFirstName().compareTo(host2.getFirstName());
        }};

Ответы [ 3 ]

24 голосов
/ 11 февраля 2011

Я думаю, что вы делаете правильно, но ужасно безобразно. Попробуйте это для удобства чтения:

Использовать Enum

Переместите функции в перечисление, которое реализует Function<Host, String>. Каждый из элементов enum может иметь свою собственную реализацию.

enum HostFunctions implements Function<Host, String>{
    GETFIRSTNAME{

        @Override
        public String apply(final Host host){
            return host.getFirstName();
        }
    },
    GETLASTNAME{

        @Override
        public String apply(final Host host){
            return host.getLastName();
        }
    }

}

Сделайте отступ вашего кода

Теперь ссылайтесь на эти функции перечисления и сделайте отступ для вашего кода правильно Вот как это будет выглядеть:

final Ordering<Host> orderingByLastAndFirstName =
    Ordering
        .natural()
        .nullsFirst()
        .onResultOf(HostFunctions.GETLASTNAME)
        .compound(
            Ordering
                .natural()
                .nullsFirst()
                .onResultOf(HostFunctions.GETFIRSTNAME))
        .nullsFirst();

Я бы сказал, что это делает все намного более понятным.

Конфигурация IDE

Относительно правильного отступа (по крайней мере, если вы используете Eclipse), посмотрите этот вопрос:

Как сделать отступ для свободного интерфейса шаблон «правильно» с затмением?

Перечисляет как функции

Относительно перечисления: это называется одноэлементным рисунком перечисления. Парни из гуавы используют его по всей своей кодовой базе. Прочитайте об этом в Википедии или в Effective Java , пункт 3. Хотя оба этих источника говорят о перечислениях из одного элемента, подход здесь почти одинаков.

11 голосов
/ 11 февраля 2011

Каждый цепочечный вызов «оборачивает» предыдущий порядок в новый, так что вы правы, порядок выполнения можно рассматривать как «задний ход».

Я написал и рассмотрел класс Ordering, и мне все еще регулярно приходится останавливаться и чесать голову над правильным чередованием nullsFirst (), onResultOf () и reverse ()!

9 голосов
/ 11 февраля 2011

Следующее было бы моим предпочтением для этого, при условии, что вы должны иметь возможность обрабатывать null хосты, имена и фамилии.Мне кажется, что не null имя и фамилия должны быть требованием класса Host.Как правило, вы должны стараться не допускать, чтобы коллекции содержали null объектов.

Ordering<Host> lastNameFirstNameOrdering = new Ordering<Host>() {
  @Override public int compare(Host left, Host right) {
    return ComparisonChain.start()
      .compare(left.getLastName(), right.getLastName(), Ordering.natural().nullsFirst())
      .compare(left.getFirstName(), right.getFirstName(), Ordering.natural().nullsFirst())
      .result();
  }
}.nullsFirst();

В качестве альтернативы я бы использовал подход, аналогичный подходу Шона, но разбил бы все на части для удобства чтения.

Ordering<Host> lastNameOrder = Ordering.natural().nullsFirst()
    .onResultOf(Host.LAST_NAME);
Ordering<Host> firstNameOrder = Ordering.natural().nullsFirst()
    .onResultOf(Host.FIRST_NAME);
Ordering<Host> orderingByLastAndFirstName =
     lastNameOrder.compound(firstNameOrder).nullsFirst();

Имейте в виду, что вы также можете сделать эти отдельные упорядочения статическими конечными полями класса, что позволит вам легко использовать их в любом месте при сортировке, например Host.LAST_NAME_ORDER.

...