вторник, 5 февраля 2013 г.

DRY principle and URLs

Почти сразу после начала разработки сайта (в моём случае на Spring MVC) я заметил, что адрес конкретной страницы приходится использовать во многих местах моего приложения. Во-первых, в самом контроллере, при задании mapping-а:
@RequestMapping(value = "/series/add", method = RequestMethod.GET)
public AddSeriesForm showForm() {
...
}
И, если не аннотировать весь класс, снова, но уже для метода POST:
@RequestMapping(value = "/series/add", method = RequestMethod.POST)
public String processInput(@Valid AddSeriesForm form, BindingResult result) {
...
}
Во-вторых, во view, чтобы задать URL на который будут отправлены данные из формы (хотя в данном случае action можно не указывать):
<form:form action="/series/add" method="post" modelAttribute="addSeriesForm">
...
</form:form>
view raw add.jsp hosted with ❤ by GitHub
В-третьих, на страницах тоже решил отобразить ссылку для добавления серии:
<a href="/series/add">Добавить серию</a>
view raw index1.jsp hosted with ❤ by GitHub
В-четверых, я же ещё и интеграционные тесты пишу на WebDriver-е и ему надо знать какую страницу открывать:
driver.get("http://localhost/series/add");
Как видите, мне встретилось четыре места, где URL страницы необходимо указать. Возможно, что ваш список будет меньше или, напротив, больше. Так или иначе я решил, что принцип "Не повторяйся" (Don't Repeat Yourself, DRY) можно применить и к адресу страницы. Тогда-то и пришла идея создать специальный класс, в котором можно было бы собрать адреса всех страниц сайта. Теперь, при переименовании страницы внести изменения нужно будет лишь в один файл.
Сначала, я назвал класс SiteMap. Название хоть и отражало суть, но оказалось довольно длинным и громоздким, поэтому я переименовал его в Url:
public final class Url {
public static final String ADD_SERIES_PAGE = "/series/add";
}
view raw Url.java hosted with ❤ by GitHub
Использование одного Java класса в других, разумеется, тривиально:
@RequestMapping(value = Url.ADD_SERIES_PAGE, method = RequestMethod.GET)
public AddSeriesForm showForm() {
...
}
Немного сложнее было «пробросить» эту константу в JSP файлы, но и это не проблема:
<%@ page import="ru.mystamps.web.Url" %>
<spring:url var="addSeriesUrl" value="<%= Url.ADD_SERIES_PAGE %>" />
<a href="${addSeriesUrl}">Добавить серию</a>
view raw index2.jsp hosted with ❤ by GitHub
Вот вроде бы и всё. Ан нет, всё просто только в книжках да статьях, в жизни немного иначе: да, теперь хотя бы в Java/JSP коде я устранил дублирование, но что делать с XML файлами и другими конфигами? Например, при ограничении доступа к странице с помощью Spring Security:
<intercept-url pattern="/series/add" access="hasRole('ROLE_USER')" />
А что если вы возвращаете путь к JSP файлу из контроллера?
return "series/add";
Или в tiles.xml для указания пути адреса и пути к JSP файлу:
<definition name="series/add" extends="defaultTemplate">
<put-attribute name="title" value="Добавить серию" />
<put-attribute name="body" value="/WEB-INF/tiles/body/series/add.jsp" />
</definition>
view raw tiles.xml hosted with ❤ by GitHub
Я пока оставил это как есть, хотя варианты существуют:
  1. универсальный: вынести константы в properties файл, и фильтровать ресурсы при сборке, с помощью maven-resources-plugin
  2. только для вышеприведенного варианта с возвращаением пути к JSP файлу: в Spring-а можно использовать соглашение об именовании — если метод возвращает void, то Spring пытается использовать адрес из mapping в качестве пути к view.
  3. в Tiles есть возможность уменьшить дублирование с помощью соглашений и регулярных выражений (но у меня, сходу, не завелось)
Некоторое время я думал, что этим никто кроме меня не заморачивается и поэтому был удивлен, когда выйдя на новую работу обнаружил в коде знакомый мне класс Url :)

Комментариев нет:

Отправить комментарий