[Из песочницы] Wicket+лямбды: типобезопасная и лаконичная реализация IModel
Стандартная задача при разработке веб-приложения: есть объект данных, требуется эти данные отобразить (вывести в HTML). В Apache Wicket данные для этого привязываются к компонентам (которые и будут заниматься отображением) с помощью моделей (реализующих интерфейс IModel).Вероятнее всего, эту публикацию будут читать те, кто уже в курсе, но на всякий случай: главный метод из IModel, который нас интересует, это:
T getObject (); Абстракция простая и лаконичная, но не всё так просто на практике. Под катом — сказ о том, как Java 8 помогла победить многословность и небезопасность стандартных подходов.Данные Подопытный класс данных: public class User implements Serializable { private final String name; private final int age;
public User (String name, int age) { this.name = name; this.age = age; }
public String getName () { return name; }
public int getAge () {
return age;
}
}
Попытка №1: мы писали, мы писали…
Вот как можно подойти к решению поставленной задачи:
public class AbstractReadOnlyModelPanel extends Panel {
public AbstractReadOnlyModelPanel (String id, IModel
add (new Label («name», new AbstractReadOnlyModel
add (new Label («name», PropertyModel.of (model, «name»)));
add (new Label («age», PropertyModel.of (model, «age»)));
}
}
Ух ты, гораздо компактнее. Но в бочке мёда есть немало дёгтя: Во-первых и в-главных — компилятор нам не помощник. Можно указать несуществующее свойство, можно указать свойство не того типа, можно сделать ещё много интересных ошибок.
Во-вторых: рефлексия. Не критично, но её наличие не радует.
На сцену выходят лямбды
К счастью, Java 8 уже давно выпущена, и лямбды вместе с Method References уже спешат на помощь:
public class GetterModel {
private final E entity;
private final IModel private GetterModel (E entity, IModel public static return new GetterModel<>(entity, null, getter);
} public static return new GetterModel<>(null, entityModel, getter);
} @Override
public P getObject () {
return getter.getPropertyValue (getEntity ());
} private E getEntity () {
return entityModel!= null? entityModel.getObject () : entity;
}
}
public interface IPropertyGetter add (new Label («name», GetterModel.ofModel (model, User: getName)));
add (new Label («age», GetterModel.ofModel (model, User: getAge)));
}
}
Почти так же кратко, как и в примере с PropertyModel, к тому же: типобезопасно: компилятор проверит, что тип совпадает (если, конечно, задействовать более разборчивый, чем Label, компонент);
гораздо более безопасно с точки зрения возможности опечаток: если вы опечатаетесь, компилятор это скорее всего отловит;
не используется рефлексия.
По сравнению с PropertyModel, правда, есть и некоторые недостатки: GetterModel — только для чтения, тогда как PropertyModel позволяет и писать. Добавление ещё и setter-а лишает задумку элегантности, к тому же добавляет ещё один источник ошибок (появляется возможность указать setter от одного свойства, а getter от другого).
PropertyModel позволяет обращаться к вложенным свойствам с помощью выражений вида «outerObject.itsProperty.propertyOfProperty».
Зато есть приятный бонус: аналог магической возможности PropertyModel использовать в качестве источника данных и модели, и POJO реализуется без всякой магии: мы просто добавили два фабричных метода (ofModel () и ofObject ()).Ссылки
Apache Wicket Framework
Java: Lambda Expressions
Java: Method References