Ответ 1
Немного приличное веб-приложение состоит из сочетания шаблонов дизайна. Я упомянул только самые важные.
Модель контроллера модели Model
Основным (архитектурным) шаблоном проектирования, который вы хотите использовать, является шаблон Model-View-Controller. Контроллер должен быть представлен сервлетом, который (in) напрямую создает/использует конкретную модель и представление на основе запроса. Модель должна быть представлена джавабскими классами. Это часто можно отделить в бизнес-модели, которая содержит действия (поведение) и модель данных, которая содержит данные (информацию). Вид должен быть представлен JSP файлами, которые имеют прямой доступ к (Data) Model по EL (язык выражений).
Затем существуют варианты, основанные на действиях и событиях. Популярные:
-
Запрос (действие) на основе MVC: это самый простой способ реализации. Модель (бизнес) напрямую работает с объектами
HttpServletRequest
иHttpServletResponse
. Вы должны сами собирать, конвертировать и проверять параметры запроса (в основном). Вид может быть представлен простым ванильным HTML/CSS/JS и не поддерживает состояние по запросам. Вот как среди прочих Spring MVC, Struts и Stripes работает. -
MVC на основе компонентов: это сложнее реализовать. Но вы получаете более простую модель и вид, в котором весь "сырой" API сервлета полностью абстрагирован. У вас не должно быть необходимости самостоятельно собирать, конвертировать и проверять параметры запроса. Контроллер выполняет эту задачу и устанавливает собранные, преобразованные и проверенные параметры запроса в модели. Все, что вам нужно сделать, это определить методы действий, которые напрямую работают с свойствами модели. Вид представлен "компонентами" в аромате тегов JSP или XML-элементов, которые, в свою очередь, генерируют HTML/CSS/JS. Состояние представления для последующих запросов сохраняется в сеансе. Это особенно полезно для серверных преобразований, проверки и изменения значений. Вот как среди других JSF, Wicket и Play! работает.
В качестве побочного примечания, хобби вокруг с доморощенной структурой MVC - это очень хорошее упражнение по обучению, и я рекомендую его, если вы храните его в личных/личных целях. Но как только вы станете профессионалом, тогда настоятельно рекомендуется выбрать существующую структуру, а не изобретать свои собственные. Изучение существующей и хорошо развитой структуры занимает в течение длительного времени меньше времени, чем разработка и поддержание надежной структуры.
В нижеследующем подробном объяснении я ограничусь запросом на основе MVC, поскольку это проще реализовать.
Образец переднего контроллера (шаблон посредника)
Во-первых, часть контроллера должна реализовать шаблон Front Controller (который является специализированным видом шаблон посредника). Он должен состоять только из одного сервлета, который обеспечивает централизованную точку входа для всех запросов. Он должен создать модель на основе информации, доступной по запросу, такой как путь 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
должен следовать шаблону стратегии . Его нужно определить как абстрактный/тип интерфейса, который должен выполнять работу, основанную на аргументах переданного абстрактного метода (это отличие от команды pattern, в котором тип abstract/interface должен выполнять работу на основе аргументов, которые были переданы во время создания реализации).
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.
}
}
}
Factory шаблон метода
ActionFactory
должен следовать шаблону Factory. В принципе, он должен обеспечить метод создания, который возвращает конкретную реализацию абстрактного/интерфейса. В этом случае он должен вернуть реализацию интерфейса Action
на основе информации, предоставленной запросом. Например, method и pathinfo ( pathinfo - это часть после контекста и пути сервлета в URL-адресе запроса, за исключением строки запроса).
public static Action getAction(HttpServletRequest request) {
return actions.get(request.getMethod() + request.getPathInfo());
}
В свою очередь, actions
должен быть частью static/applicationwide Map<String, Action>
, который содержит все известные действия. Это зависит от вас, как заполнить эту карту. Жестко прописывать:
actions.put("POST/register", new RegisterAction());
actions.put("POST/login", new LoginAction());
actions.put("GET/logout", new LogoutAction());
// ...
Или настраивается на основе файла свойств/XML-конфигурации в пути к классам: (псевдо)
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()
. Это добавляет дополнительный абстрактный слой, чтобы скрыть исходный API сервлетов. В итоге вы должны в конечном итоге создать объявления ноль import javax.servlet.*
в каждой реализации Action
. В терминах JSF это то, что FacesContext
и ExternalContext
классы. Вы можете найти конкретный пример в этом ответе.
Тогда существует шаблон состояния для случая, когда вы хотите добавить дополнительный уровень абстракции для разделения задач по сбору параметров запроса, их преобразования, проверки их, обновления значений модели и выполнения действий. В терминах JSF это то, что делает LifeCycle
.
Затем создайте Composite pattern для случая, когда вы хотите создать компонентный вид, который может быть присоединен к модели и чей поведение зависит от состояния жизненного цикла, основанного на запросе. В терминах JSF это означает UIComponent
.
Таким образом, вы можете постепенно развиваться в сторону основывания на основе компонентов.