Веб-програми "Шаблони дизайну" [закрито]


359

Я розробляю просту веб-програму. Я новачок у цьому веб-домені. Мені потрібні були поради щодо моделей дизайну, таких як розподіл відповідальності між сервлетами, критерії створення нового сервлету тощо.

Насправді на моїй домашній сторінці у мене мало об’єктів, і відповідно до кожної з них у нас є кілька варіантів, як додавання, редагування та видалення. Раніше я використовував один сервлет за такі параметри, як Servlet1 для додавання образу1, Сервлет2 для редагування сукупності1 тощо, і таким чином у нас виникла велика кількість сервлетів.

Тепер ми змінюємо дизайн. Моє питання - як ви точно обираєте, як ви обираєте відповідальність сервлета. Якщо у нас є один сервлет на сутність, який буде обробляти всі його варіанти та пересилати запит на рівень обслуговування. Або у нас повинен бути один сервлет для всієї сторінки, який буде обробляти весь запит сторінки, а потім пересилати його до відповідного сервісного рівня? Крім того, чи слід об’єкт запиту пересилати на рівень обслуговування чи ні.


8
Насправді не офіційні зразки дизайну, але не забувайте PRG (після переадресації-отримання) та Hijax (спочатку робіть роботу без js, а потім викрадайте посилання та кнопки з ajax)
Ніл МакГуйган

Відповіді:


488

Трохи пристойний веб-додаток складається з поєднання моделей дизайну. Згадаю лише найважливіші.


Шаблон контролера перегляду моделі

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

Потім існують варіанти залежно від способів обробки дій та подій. Популярними є:

  • MVC на основі запиту (дії) : це найпростіший спосіб реалізації. ( Бізнес ) Модель працює безпосередньо HttpServletRequestі HttpServletResponseоб'єкти. Ви повинні зібрати, перетворити та перевірити параметри запиту (в основному) самостійно. View може бути представлений простий ванілі HTML / CSS / JS і не підтримувати стан між запитами. Так працює серед інших весняний MVC , Struts and Stripes .

  • MVC на основі компонентів : це важче здійснити. Але ви закінчуєте більш просту модель і вигляд, в якому всі "сирі" сервлетські API абстрагуються повністю. У вас не повинно виникати необхідності самостійно збирати, конвертувати та перевіряти параметри запиту. Контролер виконує це завдання і встановлює зібраний, перетворені і підтверджені параметри запиту в моделі . Все, що вам потрібно зробити - це визначити методи дій, які безпосередньо працюють із властивостями моделі. View представлений «компонент» в ароматі JSP або бібліотеки тегів XML елементів , які , в свою чергу , генерує HTML / CSS / JS. Стан Поглядудля наступних запитів зберігається в сесії. Це особливо корисно для подій на конвертації, валідації та зміні значення на стороні сервера. Ось як серед інших JSF , Wicket and Play! працює.

Як зауваження, захоплення навколо доморощеної рамки MVC - це дуже приємна навчальна робота, і я рекомендую її до тих пір, поки ви зберігаєте це в особистих / приватних цілях. Але після того, як ви будете професійними, настійно рекомендується вибирати існуючі рамки, а не винаходити власні. Навчання існуючих та добре розроблених рамок займає в довгостроковій перспективі менше часу, ніж самостійно розробляти та підтримувати міцні рамки.

У нижченаведеному детальному поясненні я обмежуся запитувати MVC на основі запиту, оскільки це легше здійснити.


Шаблон переднього контролера ( візерунок посередника )

По-перше, частина контролера повинна реалізувати схему переднього контролера (що є спеціалізованим видом шаблону посередника ). Він повинен складатися лише з одного сервлета, який забезпечує централізовану точку входу для всіх запитів. Він повинен створити Модель на основі інформації, доступної запитом, наприклад, інформація про шлях або сервлет-шлях, метод та / або конкретні параметри. Бізнес - модель називається 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по собі є модель даних . 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 - це частина після контексту та шлях сервлетів у 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 у класі class: (псевдо)

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робиться.

Тоді є складений шаблон для випадку, який ви хочете створити на основі компонента, який можна приєднати до моделі і поведінка якого залежить від стану життєвого циклу на основі запиту. З точки зору 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 шаблоні Servlet є "C" - контролер.

Основна його робота - зробити початкову оцінку запиту, а потім відправити обробку на основі первинної оцінки конкретному працівникові. Однією з обов'язків працівника може бути налаштування деякого компонента презентаційного шару та пересилання запиту на сторінку JSP для надання HTML. Отже, лише з цієї причини вам потрібно передати об’єкт запиту на рівень обслуговування.

Я б не хотів би починати писати сирі Servletкласи. Робота, яку вони виконують, є дуже передбачуваною та плитною, що таке рамки робить дуже добре. На щастя, є багато доступних, перевірених часом кандидатів (в алфавітному порядку): Apache Wicket , Face Server Faces , Spring, щоб назвати декілька.


5

ІМХО, у випадку веб-застосунку немає великої різниці, якщо дивитися на це з погляду відповідальності. Однак зберігайте чіткість у шарі. Зберігайте що-небудь суто для цілей презентації в шарі презентації, як контроль та код, характерні для веб-елементів управління. Просто тримайте ваші особи на рівні бізнесу та всі функції (наприклад, додавання, редагування, видалення) тощо у бізнес-шарі. Однак візуалізація їх у браузері для обробки в презентаційному шарі. Для .Net, модель ASP.NET MVC дуже хороша в плані збереження розділених шарів. Погляньте на шаблон MVC.


ви можете піти трохи явно в те, що повинно йти в сервлет?
mawia

Сервлет повинен бути контролером, якщо ви використовуєте MVC.
Кангкан

3

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

1) Дія, яка використовується, викликається щоразу, коли HTML-сторінку оновлюється. Дія повинна заповнювати дані у формі під час першого завантаження сторінки та обробки взаємодій між веб-інтерфейсом та бізнес-рівнем. Якщо ви використовуєте сторінку jsp для зміни змінного об'єкта java, копія об’єкта java повинна зберігатися у формі, а не оригіналі, щоб вихідні дані не змінювались, якщо користувач не збереже сторінку.

2) Форма, яка використовується для передачі даних між дією та jsp-сторінкою. Цей об'єкт повинен складатися з набору getter та setters для атрибутів, які повинні бути доступними для файлу jsp. Форма також має метод перевірки даних до її збереження.

3) Сторінка jsp, яка використовується для візуалізації кінцевого HTML сторінки. Сторінка jsp - це гібрид HTML-тегів та спеціальних тегів-рядків, що використовуються для доступу та маніпулювання даними у формі. Хоча підказки дозволяють користувачам вставляти код Java у jsp-файли, ви повинні бути дуже обережними щодо цього, оскільки це ускладнює читання вашого коду. Код Java всередині файлів jsp важко налагоджувати, і його неможливо перевірити. Якщо ви виявите, що ви пишете більше 4-5 рядків коду Java у файлі jsp, код, ймовірно, повинен бути переміщений до дії.


Примітка. У рядках 2 об'єкт Form посилається як Модель, але працює так само, як я описав у своїй початковій відповіді.
EsotericNonsense

3

Відмінна відповідь BalusC охоплює більшість моделей веб-додатків.

Деякі програми можуть вимагати Chain-of-odgovornosti_pattern

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

Використовуйте регістр, щоб скористатися цією схемою:

Коли обробник для обробки запиту (команди) невідомий, і цей запит може бути надісланий декільком об'єктам. Як правило, ви встановлюєте заперечення спадкоємця . Якщо поточний об'єкт не може обробити запит або частково обробити його і передати той самий запит наступнику об'єкту- .

Корисні питання / статті SE:

Чому я коли-небудь використовувати ланцюжок відповідальності за декоратор?

Загальні звичаї для ланцюжка відповідальності?

схема ланцюжка відповідальності від дизайну

ланцюг_відповідальності від створення джерел

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.