Статьи

Использование Google Guava’s Ordering API

Мы играем немного больше с библиотекой Google Guava — какая замечательная библиотека! Самое последнее, для чего мы его использовали, — это сортировка компараторов для наших доменных объектов. Вот как. Используя Apache IsisJDO Objectstore , рекомендуется, чтобы ваши классы реализовывали java.lang.Comparable и использовали SortedSet для коллекций. Вы можете увидеть это в ToDoItem архетипа Isis, где ToDoItem имеет рекурсивное отношение к себе:

1
2
3
4
5
public class ToDoItem implements Comparable<ToDoItem> {
    ...
    private SortedSet<ToDoItem> dependencies = Sets.newTreeSet();
    ...
}

Как лучше всего реализовать метод compareTo ? Вот оригинальная реализация:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
public int compareTo(final ToDoItem other) {
    if (isComplete() && !other.isComplete()) {
        return +1;
    }
    if (!isComplete() && other.isComplete()) {
        return -1;
    }
    if (getDueBy() == null && other.getDueBy() != null) {
        return +1;
    }
    if (getDueBy() != null && other.getDueBy() == null) {
        return -1;
    }
    if (getDueBy() == null && other.getDueBy() == null ||
        getDueBy().equals(this.getDueBy())) {
        return getDescription().compareTo(other.getDescription());
    }
    return getDueBy().compareTo(getDueBy());
}

Юк! В основном это говорит:
* заказать еще не завершенные объекты перед завершенными объектами
* там, где есть связь, заказ по дате выполнения (поместите те без даты по дате в последнюю очередь)
* где есть галстук, порядок по описанию.

Вот как это переписать, используя класс Order в Guava. Сначала давайте создадим несколько экземпляров Ordering для скалярных типов:

01
02
03
04
05
06
07
08
09
10
11
public final class Orderings {
 
    public static final Ordering<Boolean> BOOLEAN_NULLS_LAST =
        Ordering.<Boolean>natural().nullsLast();
    public static final Ordering<LocalDate> LOCAL_DATE_NULLS_LAST =
        Ordering.<LocalDate>natural().nullsLast();
    public static final Ordering<String> STRING_NULLS_LAST =
        Ordering.<String>natural().nullsLast();
 
    private Orderings(){}
}

Теперь мы можем переписать ToDoItem compareTo() декларативным способом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class ToDoItem implements Comparable {
 
    ...
 
    public int compareTo(ToDoItem o) {
        return ORDERING_BY_COMPLETE
               .compound(ORDERING_BY_DUE_BY)
               .compound(ORDERING_BY_DESCRIPTION)
               .compare(this, o);
    }
 
    public static Ordering<ToDoItem> ORDERING_BY_COMPLETE = new Ordering<ToDoItem>(){
        public int compare(ToDoItem p, ToDoItem q) {
            return Orderings.BOOLEAN_NULLS_LAST.compare(p.isComplete(), q.isComplete());
        }
    };
 
    public static Ordering<ToDoItem> ORDERING_BY_DUE_BY = new Ordering()<ToDoItem>{
        public int compare(ToDoItem p, ToDoItem q) {
            return Orderings.BOOLEAN_NULLS_LAST.compare(p.getDueBy(), q.getDueBy());
        }
    };
 
    public static Ordering<ToDoItem> ORDERING_BY_DESCRIPTION = new Ordering()<ToDoItem>{
        public int compare(ToDoItem p, ToDoItem q) {
            return Orderings.STRINGS_NULLS_LAST.compare(
              p.getDescription(), q.getDescription());
        }
    };

Теперь, по общему признанию, это вряд ли оправдывает весь этот шаблон для только одного метода в одном классе; конечно нет! Но сейчас у нас есть небольшая алгебра, которую мы можем использовать для объединения всех классов предметной области в нашей предметной модели. Другие классы домена, использующие ToDoItem могут упорядочивать себя, используя естественное упорядочение ToDoItem (доступное из Ordering.natural() ), или они могут создавать новые упорядочения, используя различные упорядочения ToDoItem.ORDERING_BY_xxx .

Ссылка: используя Google Guava Ordering API от нашего партнера JCG Дэна Хейвуда в блоге Дэна Хейвуда .