Цепочный заказ в Гуаве
Я немного новичок в Guava и его стиле. Я определенно копаю его, но одна вещь, с которой я держусь, - это порядок прикованных методов. Там, где у меня эта проблема, чаще всего при использовании составной 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());
}};
Ответы
Ответ 1
Каждый цепочный вызов "обертывает" предыдущее упорядочение на новый, поэтому вы правы, порядок выполнения можно считать "назад".
Я написал и рассмотрел класс Ordering, и мне по-прежнему приходится останавливаться и царапать голову над правильным чередованием нулевых значенийFirst() и onResultOf() и reverse()!
Ответ 2
Я думаю, что вы делаете правильно, но ужасно уродливо. Попробуйте это для удобочитаемости:
Использовать Enum
Переместите функции в перечисление, которое реализует Function<Host, String>
. Каждый из элементов перечисления может обеспечить его собственную реализацию.
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), см. этот вопрос:
Как отступать от свободного интерфейса шаблон "правильно" с eclipse?
Перечисления как функции
Что касается перечисления: это называется однолучевым шаблоном перечисления. Ребята из Guava используют его всю свою базу кода. Прочитайте об этом в wikipedia или в Эффективная Java, Пункт 3. Хотя эти источники и говорят об отдельных элементах перечисления, подход здесь почти такой же.
Ответ 3
Следующее было бы моим преимуществом для этого, предполагая, что вы должны иметь возможность обрабатывать хосты, имена и фамилии null
. Мне кажется, что имя 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
.