Шаблоны проектирования веб-приложений [закрыто]


359

Я разрабатываю простое веб-приложение. Я новичок в этом веб-домене. Мне нужен был ваш совет относительно шаблонов проектирования, таких как распределение ответственности между сервлетами, критерии создания нового сервлета и т. Д.

На самом деле, у меня есть несколько сущностей на моей домашней странице, и в соответствии с каждой из них у нас есть несколько вариантов, таких как добавление, редактирование и удаление. Ранее я использовал один сервлет для опций, таких как Servlet1 для добавления entity1, Servlet2 для редактирования entity1 и так далее, и таким образом мы получили большое количество сервлетов.

Сейчас мы меняем наш дизайн. Мой вопрос заключается в том, как именно вы выбираете, как вы выбираете ответственность сервлета. Если у нас есть один сервлет для каждой сущности, который обработает все его опции и перенаправит запрос на сервисный уровень. Или у нас должен быть один сервлет для всей страницы, который будет обрабатывать весь запрос страницы и затем перенаправлять его на соответствующий уровень обслуживания? Кроме того, должен ли объект запроса перенаправляться на сервисный уровень или нет.


8
Не совсем официальные шаблоны проектирования, но не забывайте PRG (post-redirect-get) и Hijax (сначала работайте без js, а затем перехватывайте ссылки и кнопки с помощью ajax)
Нил Макгиган

Ответы:


489

Немного приличное веб-приложение состоит из комбинации шаблонов дизайна. Я упомяну только самые важные.


Модель просмотра модели

Базовым (архитектурным) шаблоном проектирования, который вы хотели бы использовать, является шаблон Model-View-Controller . Контроллер должен быть представлен сервлета , который (в) непосредственно создает / использует конкретный модель и View на основе запроса. Модель должны быть представлены классами JavaBeans. Это часто делится на бизнес-модель, которая содержит действия (поведение) и модель данных, которая содержит данные (информацию). View должен быть представлен JSP файлы , которые имеют прямой доступ к ( Data ) Модель Е.Л. (Expression Language).

Затем существуют вариации, основанные на том, как обрабатываются действия и события. Популярные из них:

  • MVC, основанный на запросах (действиях) : это самый простой для реализации. ( Бизнес ) Модель работает непосредственно HttpServletRequestи HttpServletResponseобъекты. Вы должны собрать, преобразовать и проверить параметры запроса (в основном) самостоятельно. View может быть представлен простой ванили 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 и т. Д., И вы должны изменить примеры кода в этом ответе (также the ActionFactory) для извлечения registerи loginчастей request.getServletPath()вместо.


Стратегия

ActionДолжны следовать шаблону стратегии . Он должен быть определен как абстрактный / интерфейсный тип, который должен выполнять работу на основе переданных аргументов абстрактного метода (в этом отличие от шаблона Command , в котором абстрактный / интерфейсный тип должен выполнять работу на основе аргументы, которые были переданы при создании реализации).

public interface Action {
    public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception;
}

Вы можете сделать Exceptionболее конкретным с пользовательским исключением, как ActionException. Это просто базовый пример, остальное зависит только от вас.

Вот пример того, LoginActionкоторый (как следует из его названия) входит в систему пользователя. Само по Userсебе является моделью данных . View известно о присутствии 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)тогда.


Другие образцы

Это были важные образцы до сих пор.

Чтобы продвинуться дальше, вы можете использовать шаблон Facade для создания Contextкласса, который в свою очередь оборачивает объекты запроса и ответа и предлагает несколько удобных методов, делегирующих объектам запроса и ответа, и Action#execute()вместо этого передает их в качестве аргумента в метод. Это добавляет дополнительный абстрактный слой, чтобы скрыть необработанный API сервлетов. Затем вы должны в итоге получить нулевые import javax.servlet.* объявления в каждой Actionреализации. С точки зрения JSF, это то , что FacesContextи ExternalContextклассы делают. Вы можете найти конкретный пример в этом ответе .

Затем есть шаблон State для случая, когда вы хотите добавить дополнительный уровень абстракции, чтобы разделить задачи сбора параметров запроса, их преобразования, проверки, обновления значений модели и выполнения действий. С точки зрения JSF, это то, что LifeCycleделает.

Затем есть шаблон Composite для случая, когда вы хотите создать компонентное представление, которое может быть присоединено к модели и поведение которого зависит от состояния жизненного цикла на основе запроса. С точки зрения JSF, это то, что UIComponentпредставляют.

Таким образом, вы можете постепенно развиваться в сторону компонентной структуры.


Смотрите также:


4
@masato: Вы можете сделать это, например, в статическом блоке инициализатора.
BalusC

1
@masato: кстати, если вы хотите получить их web.xml, то вы можете использовать ServletContextListenerдля этого. Есть завод реализовать (и зарегистрируйтесь <listener>в web.xml) и сделать работу наполнения во время contextInitialized()метода.
BalusC

3
Сделайте работу, которую должен выполнить post_servlet в действии. Вы не должны иметь более одного сервлета. Деловые вещи должны быть сделаны в классах действия. Если вы хотите, чтобы это был новый запрос, вернитесь в другое представление, которое вызовет перенаправление, и выполните задание в новом действии, связанном с запросом GET.
BalusC

2
Зависит. Проще всего просто сделать это правильно в Actionреализации так же, как и с обычными сервлетами (см. Также вики-сервлеты для базового примера, который вы можете рефакторировать далее в некоторый Validatorинтерфейс). Но вы также можете сделать это до вызова действия, но это более сложно, так как требует, чтобы правила валидации были известны для каждого просмотра. JSF покрыла это, предлагая required="true", validator="customValidatorName", и т.д. в XHTML - разметки.
BalusC

2
@AndreyBotalov: проверьте исходный код фреймворков MVC, таких как JSF, Spring MVC, Wicket, Struts2 и т. Д. Все они с открытым исходным кодом.
BalusC

13

В взбитом паттерне MVC сервлет является "C" -контроллером.

Его основная задача - выполнить первоначальную оценку запроса, а затем отправить обработку на основе первоначальной оценки конкретному работнику. Одной из обязанностей работника может быть настройка некоторых компонентов уровня представления и перенаправление запроса на страницу JSP для визуализации HTML. Таким образом, только по этой причине вам необходимо передать объект запроса на сервисный уровень.

Я бы не стал писать сырые Servletклассы. Работа, которую они выполняют, очень предсказуема и типична, что очень хорошо делает эта структура. К счастью, есть много доступных, проверенных временем кандидатов (в алфавитном порядке): Apache Wicket , Java Server Faces , Spring и многие другие.


5

ИМХО, нет большой разницы в случае веб-приложения, если смотреть на него с точки зрения распределения ответственности. Однако сохраняйте четкость в слое. Сохраняйте что-либо исключительно для целей презентации на уровне представления, например элемент управления и код, специфичные для веб-элементов управления. Просто сохраните свои сущности на бизнес-уровне, а все функции (например, добавьте, измените, удалите) и т. Д. На бизнес-уровне. Однако рендеринг их в браузере должен обрабатываться на уровне представления. Для .Net шаблон ASP.NET MVC очень хорош с точки зрения разделения слоев. Посмотрите на шаблон MVC.


Вы можете сказать немного явно, что должно идти в сервлете?
Mawia

Сервлет должен быть контроллером, если вы используете MVC.
Кангкан

3

Я использовал структуру 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, код, вероятно, следует перенести в действие.


Примечание. В распорках 2 объект Form называется моделью, но работает так же, как я описал в своем первоначальном ответе.
EsotericNonsense

3

Отличный ответ BalusC охватывает большинство шаблонов для веб-приложений.

Некоторое приложение может потребовать Chain-of-liability_pattern

В объектно-ориентированном проектировании шаблон цепочки ответственности представляет собой шаблон проектирования, состоящий из источника объектов команд и серии объектов обработки. Каждый объект обработки содержит логику, которая определяет типы объектов команд, которые он может обрабатывать; остальные передаются следующему объекту обработки в цепочке.

Вариант использования для использования этого шаблона:

Когда обработчик для обработки запроса (команда) неизвестен и этот запрос может быть отправлен нескольким объектам. Обычно вы устанавливаете преемник для объекта. Если текущий объект не может обработать запрос или обработать его частично и перенаправить тот же запрос к объекту- преемнику .

Полезные вопросы / статьи по SE:

Зачем мне использовать цепочку ответственности над декоратором?

Обычные способы использования цепи ответственности?

шаблон цепи ответственности от oodesign

chain_of_responsibility от создания источника

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.