Шаблоны проектирования веб-приложений
Я разрабатываю простое веб-приложение. Я новичок в этом веб-домене. Мне нужен был ваш совет относительно шаблонов проектирования, таких как распределение ответственности между сервлетами, критерии создания нового сервлета и т. Д.
На самом деле у меня есть несколько сущностей на моей домашней странице, и в соответствии с каждой из них у нас есть несколько вариантов, таких как добавление, редактирование и удаление. Ранее я использовал один сервлет для каждой опции, например Servlet1 для добавления entity1, Servlet2 для редактирования entity1 и так далее, и таким образом мы получили большое количество сервлетов.
Сейчас мы меняем наш дизайн. Мой вопрос в том, как именно вы выбираете, как вы выбираете ответственность сервлета. Если у нас есть один сервлет на объект, который обработает все его опции и перенаправит запрос на сервисный уровень. Или у нас должен быть один сервлет для всей страницы, который будет обрабатывать весь запрос страницы и затем перенаправлять его на соответствующий уровень обслуживания? Кроме того, должен ли объект запроса перенаправляться на сервисный уровень или нет.
5 ответов
Немного приличное веб-приложение состоит из смеси шаблонов дизайна. Я упомяну только самые важные.
Модель контроллера вида модели
Базовым (архитектурным) шаблоном проектирования, который вы хотели бы использовать, является шаблон Model-View-Controller. Контроллер должен быть представлен сервлетом, который (в) напрямую создает / использует конкретную модель и представление на основе запроса. Модель должна быть представлена классами Javabean. Это часто делится на бизнес-модель, которая содержит действия (поведение) и модель данных, которая содержит данные (информацию). Представление должно быть представлено файлами JSP, которые имеют прямой доступ к модели (данных) через EL (язык выражений).
Затем существуют варианты, основанные на том, как обрабатываются действия и события. Популярные из них:
MVC, основанный на запросах (действиях): это самый простой для реализации. (Бизнес) Модель работает напрямую с
HttpServletRequest
а такжеHttpServletResponse
объекты. Вы должны собрать, преобразовать и проверить параметры запроса (в основном) самостоятельно. Представление может быть представлено простым ванильным HTML/CSS/JS, и оно не поддерживает состояние между запросами. Так работает Spring MVC, Struts и Stripes.Компонентный MVC: это сложнее реализовать. Но в итоге вы получаете более простую модель и представление, в которых весь "сырой" API сервлетов полностью отделен. Вам не нужно собирать, преобразовывать и проверять параметры запроса самостоятельно. Контроллер выполняет эту задачу и устанавливает собранные, преобразованные и проверенные параметры запроса в модели. Все, что вам нужно сделать, это определить методы действия, которые работают непосредственно со свойствами модели. Представление представлено "компонентами" в виде тегов JSP или элементов XML, которые, в свою очередь, генерируют HTML/CSS/JS. Состояние просмотра для последующих запросов сохраняется в сеансе. Это особенно полезно для событий преобразования, проверки и изменения на стороне сервера. Вот как среди прочего JSF, Wicket and Play! работает.
В качестве дополнительного примечания, увлечение домашним фреймворком MVC - это очень хорошее упражнение для обучения, и я рекомендую его, если вы держите его в личных / личных целях. Но как только вы станете профессионалом, тогда настоятельно рекомендуется выбрать существующий фреймворк, а не заново изобретать свой собственный. Изучение существующей и хорошо разработанной структуры занимает в долгосрочной перспективе меньше времени, чем разработка и поддержание надежной структуры самостоятельно.
В приведенном ниже подробном объяснении я ограничусь запросом MVC, поскольку его легче реализовать.
Шаблон переднего контроллера ( шаблон посредника)
Во-первых, часть Controller должна реализовывать шаблон Front Controller (который является специализированным типом шаблона Mediator). Он должен состоять только из одного сервлета, который обеспечивает централизованную точку входа для всех запросов. Он должен создавать модель на основе информации, доступной по запросу, такой как pathinfo или servletpath, метод и / или конкретные параметры. Бизнес-модель называется Action
в ниже HttpServlet
пример.
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
Action action = ActionFactory.getAction(request);
String view = action.execute(request, response);
if (view.equals(request.getPathInfo().substring(1)) {
request.getRequestDispatcher("/WEB-INF/" + view + ".jsp").forward(request, response);
}
else {
response.sendRedirect(view); // We'd like to fire redirect in case of a view change as result of the action (PRG pattern).
}
}
catch (Exception e) {
throw new ServletException("Executing action failed.", e);
}
}
Выполнение действия должно вернуть некоторый идентификатор, чтобы найти представление. Простейшим было бы использовать его в качестве имени файла JSP. Отобразить этот сервлет на конкретный url-pattern
в web.xml
например, /pages/*
, *.do
или даже просто *.html
,
В случае префикс-паттернов, как, например, /pages/*
Затем вы можете вызвать URL-адреса, такие как http://example.com/pages/register, http://example.com/pages/login и т. д., и предоставить /WEB-INF/register.jsp
, /WEB-INF/login.jsp
с соответствующими действиями GET и POST. Части register
, login
и т. д. затем доступны request.getPathInfo()
как в примере выше.
Когда вы используете суффикс-шаблоны, такие как *.do
, *.html
и т. д., затем вы можете вызывать URL-адреса, такие как http://example.com/register.do, http://example.com/login.do и т. д., и вам следует изменить примеры кода в этом ответе (также ActionFactory
) чтобы извлечь register
а также login
части по request.getServletPath()
вместо.
Стратегия
Action
должен следовать шаблону стратегии. Он должен быть определен как абстрактный / интерфейсный тип, который должен выполнять работу на основе переданных аргументов абстрактного метода (в этом отличие от шаблона Command, в котором абстрактный / интерфейсный тип должен выполнять работу на основе аргументы, которые были переданы при создании реализации).
public interface Action {
public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
Вы можете сделать Exception
более конкретно с пользовательским исключением, как ActionException
, Это просто базовый пример, остальное зависит только от вас.
Вот пример LoginAction
который (как следует из его названия) входит в систему пользователя. User
сама по себе является моделью данных. Представление знает о наличии User
,
public class LoginAction implements Action {
public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
String username = request.getParameter("username");
String password = request.getParameter("password");
User user = userDAO.find(username, password);
if (user != null) {
request.getSession().setAttribute("user", user); // Login user.
return "home"; // Redirect to home page.
}
else {
request.setAttribute("error", "Unknown username/password. Please retry."); // Store error message in request scope.
return "login"; // Go back to redisplay login form with error.
}
}
}
Шаблон фабричного метода
ActionFactory
должен следовать шаблону фабричного метода. По сути, он должен предоставлять креационный метод, который возвращает конкретную реализацию абстрактного / интерфейсного типа. В этом случае он должен вернуть реализацию Action
интерфейс на основе информации, предоставленной запросом. Например, метод и pathinfo (pathinfo - это часть после пути контекста и сервлета в URL-адресе запроса, исключая строку запроса).
public static Action getAction(HttpServletRequest request) {
return actions.get(request.getMethod() + request.getPathInfo());
}
actions
в свою очередь должен быть статичным Map<String, Action>
который содержит все известные действия. Это зависит от вас, как заполнить эту карту. жестко прописывать:
actions.put("POST/register", new RegisterAction());
actions.put("POST/login", new LoginAction());
actions.put("GET/logout", new LogoutAction());
// ...
Или настраивается на основе файла конфигурации свойств /XML в classpath: (псевдо)
for (Entry entry : configuration) {
actions.put(entry.getKey(), Class.forName(entry.getValue()).newInstance());
}
Или динамически на основе сканирования в пути к классам для классов, реализующих определенный интерфейс и / или аннотацию: (псевдо)
for (ClassFile classFile : classpath) {
if (classFile.isInstanceOf(Action.class)) {
actions.put(classFile.getAnnotation("mapping"), classFile.newInstance());
}
}
Имейте в виду, чтобы создать "ничего не делать" Action
для случая нет отображения. Пусть, например, вернет непосредственно request.getPathInfo().substring(1)
затем.
Другие модели
Это были важные образцы до сих пор.
Чтобы сделать шаг вперед, вы можете использовать шаблон Фасад для создания Context
класс, который в свою очередь оборачивает объекты запроса и ответа и предлагает несколько удобных методов, делегирующих объектам запроса и ответа, и передает их в качестве аргумента в Action#execute()
метод вместо. Это добавляет дополнительный абстрактный слой, чтобы скрыть необработанный Servlet API. Затем вы должны в конечном итоге с нуля import javax.servlet.*
декларации в каждом Action
реализация. С точки зрения JSF, это то, что FacesContext
а также ExternalContext
занятия делают. Вы можете найти конкретный пример в этом ответе.
Затем есть шаблон State для случая, когда вы хотите добавить дополнительный уровень абстракции, чтобы разделить задачи сбора параметров запроса, их преобразования, проверки, обновления значений модели и выполнения действий. С точки зрения JSF, это то, что LifeCycle
делается.
Затем есть шаблон Composite для случая, когда вы хотите создать компонентное представление, которое может быть присоединено к модели и поведение которого зависит от состояния жизненного цикла на основе запроса. С точки зрения JSF, это то, что UIComponent
представлять.
Таким образом, вы можете постепенно развиваться в сторону компонентной структуры.
Смотрите также:
В взбитом паттерне MVC сервлет является "C" -контроллером.
Его основная задача - выполнить первоначальную оценку запроса, а затем отправить обработку на основе первоначальной оценки конкретному работнику. Одной из обязанностей работника может быть настройка некоторых bean-компонентов уровня представления и перенаправление запроса на страницу JSP для рендеринга HTML. Таким образом, по одной только этой причине вам нужно передать объект запроса на сервисный уровень.
Я бы не стал писать сырье Servlet
классы. Работа, которую они выполняют, очень предсказуема и типична, что делает эту структуру очень хорошо. К счастью, есть много доступных, проверенных временем кандидатов (в алфавитном порядке): Apache Wicket, Java Server Faces, Spring и многие другие.
ИМХО, нет большой разницы в случае веб-приложения, если смотреть на него с точки зрения распределения ответственности. Однако сохраняйте четкость в слое. Сохраняйте что-либо исключительно для целей презентации на уровне представления, например элемент управления и код, специфичные для веб-элементов управления. Просто сохраните свои сущности на бизнес-уровне, а все функции (например, добавьте, измените, удалите) и т. Д. На бизнес-уровне. Однако их рендеринг в браузере должен обрабатываться на уровне представления. Для.Net шаблон ASP.NET MVC очень хорош с точки зрения разделения слоев. Посмотрите на шаблон MVC.
Я использовал структуру Struts и нахожу ее довольно простой в изучении. При использовании структуры Struts каждая страница вашего сайта будет содержать следующие элементы.
1) Используемое действие вызывается каждый раз, когда обновляется HTML-страница. Действие должно заполнять данные в форме при первой загрузке страницы и обрабатывать взаимодействия между веб-интерфейсом пользователя и бизнес-уровнем. Если вы используете страницу jsp для изменения изменяемого объекта Java, копия объекта Java должна храниться в форме, а не в оригинале, чтобы исходные данные не изменялись, пока пользователь не сохранит страницу.
2) Форма, которая используется для передачи данных между действием и страницей jsp. Этот объект должен состоять из набора методов получения и установки атрибутов, которые должны быть доступны для файла jsp. Форма также имеет метод проверки данных до их сохранения.
3) Страница JSP, которая используется для отображения окончательного HTML-страницы. Страница jsp представляет собой гибрид HTML и специальных тегов Struts, используемых для доступа к данным в форме и манипулирования ими. Хотя Struts позволяет пользователям вставлять Java-код в JSP-файлы, вы должны быть очень осторожны, потому что это делает ваш код более трудным для чтения. Java-код внутри jsp-файлов сложен для отладки и не может быть проверен модулем. Если вы обнаружите, что пишете более 4-5 строк кода Java внутри файла JSP, код, вероятно, следует перенести в действие.
Отличный ответBalusC охватывает большинство шаблонов для веб-приложений.
Некоторое приложение может потребовать Chain-of-liability_pattern
В объектно-ориентированном проектировании шаблон цепочки ответственности - это шаблон проектирования, состоящий из источника командных объектов и серии объектов обработки. Каждый объект обработки содержит логику, которая определяет типы объектов команд, которые он может обрабатывать; остальные передаются следующему объекту обработки в цепочке.
Вариант использования для использования этого шаблона:
Когда обработчик для обработки запроса (команда) неизвестен и этот запрос может быть отправлен нескольким объектам. Обычно вы устанавливаете преемник для объекта. Если текущий объект не может обработать запрос или обработать его частично и перенаправить тот же запрос на объект- преемник.
Полезные вопросы / статьи по SE:
Зачем мне использовать цепочку ответственности вместо декоратора?
Обычные способы использования цепи ответственности?
шаблон цепи ответственности от oodesign
chain_of_responsibility от создания источника