|
|
(10 промежуточных версий не показаны.) |
Строка 1: |
Строка 1: |
- | == Команды и фабрики ==
| + | #REDIRECT [[LXF94:Java EE]] |
- | ''ЧАСТЬ 6 '''Антон Черноусов''' готов познакомить вас с очередной партией паттернов, которые помогут сделать ваши приложения еще более гибкими и расширяемыми.''
| + | |
- | | + | |
- | === Вместо предисловия ===
| + | |
- | | + | |
- | В предыдущей статье мой коллега Александр Бабаев рассмотрел вопросы организации и использования БД в Java-приложениях, и в том числе вопросы подключения к БД посредством ConnectionPool.
| + | |
- | | + | |
- | Сегодня мы рассмотрим применение двух паттернов, безусловно, оказавших огромное воздействие на проектирование систем – Command и Factory Method. Их применение позволит сделать ваше приложение расширяемым.
| + | |
- | | + | |
- | === Команды ===
| + | |
- | | + | |
- | В [[LXF92|LXF92]] мы кратко описали стратегии, предназначенные для реализации «Контроллера», и обещали более подробно рассмотреть стратегию Command and Controller. Чтобы выполнить это обещание, нам придется сначала познакомиться с паттерном Command.
| + | |
- | | + | |
- | Задача, которая стоит перед контроллером (сервлетом) при получении управляющего сигнала, как правило, заключается в выполнении последовательности действий, часто атомарной (т.е. обрабатываемой как единое целое). Например, в сервлете AddressBookServlet, реализованном в предыдущей статье, метод handleEdit вызывается, когда адрес на который обращается пользователь – это “/edit”.
| + | |
- | | + | |
- | К сожалению, на примере AddressBookServlet мы видим, что при увеличении функциональности web-приложения растёт и количество методов, реализованных в сервлете; класс «засоряется», код становится менее структурированным и читабельным. Решить проблемы с кодом можно, «обернув» методы в специальные классы, которые будут выполнять атомарные операции и предоставлять сервлету стандартный интерфейс, предназначенный для этих целей. Для выполнения поставленной задачи воспользуемся паттерном Command.
| + | |
- | | + | |
- | Команды удобны прежде всего тем, что они маскируют конкретную реализацию, находящуюся за интерфейсной прослойкой. Интерфейс остается одним и тем же, независимо от того, с чем работает команда [1].
| + | |
- | | + | |
- | public interface Command {
| + | |
- | public void execute() throws Exception;
| + | |
- | }
| + | |
- | | + | |
- | Выше представлен простой интерфейс Command всего с одним методом execute(), который и олицетворяет идею одноименного паттерна. Он будет нашим стандартым интерфейсом. Более сложная реализация интерфейса включает метод unexecute(). Класс, реализующий интерфейс Command, инкапсулирует в методе execute() обозначенные выше [[Image:Java 6 1.jpg|left|300px|trumb|Рис. 1. Диаграмма классов.]] атомарные операции, а в методе unexecute() реализуется механизм отмены. Часто методы execute() и unexecute() называют do() и undo(), соответственно.
| + | |
- | | + | |
- | Выделим команды, которые нам необходимо реализовать (отметим удачное разделение на методы): Add, Auth, Edit, View, Remove. Учитывая то, что у наших команд будут некоторые идентичные методы и атрибуты, предлагаю создать абстрактный класс AbstractHTTPCommand, в котором они будут собраны. Класс будет реализовывать интерфейс Command, и его наследование автоматически позволит обеспечить необходимый уровень интеграции. Итак, в основе каждой команды будет лежать абстрактный класс AbstractHTTPCommand, в котором реализован интерфейс для выполнения операций (на Рис. 1 вы можете видеть диаграмму классов команд нашего приложения).
| + | |
- | | + | |
- | Определим общие методы для абстрактного класса: initCommand() – предназначен для инициализации команды, makeDataToView() – для подготовки данных для отображения в случае их изменения, outputPage() – метод для переадресации пользователя (он будет перенесен из AddressBookServlet без изменений) и другие. Ниже представлена реализация методов initCommand() и makeDataToView():
| + | |
- | | + | |
- | protected void initCommand(ServletContext sc, HttpServletRequest
| + | |
- | aRequest,
| + | |
- | HttpServletResponse aResponse, String viewPath,
| + | |
- | String resultPath, String errorPath) {
| + | |
- | this.setSc(sc);
| + | |
- | this.setARequest(aRequest);
| + | |
- | this.setAResponse(aResponse);
| + | |
- | this.setResultPath(resultPath);
| + | |
- | this.setErrorPath(errorPath);
| + | |
- | this.setViewPath(viewPath);
| + | |
- | }
| + | |
- | public void makeDataToView() {
| + | |
- | Map<String, String> numbers = new HashMap<String, String>();
| + | |
- | Map<String, String> comments = new HashMap<String, String>();
| + | |
- | for (Map.Entry<String, Contact> entry :
| + | |
- | _addressBook.getContacts().entrySet()) {
| + | |
- | numbers.put(entry.getKey(), entry.getValue().getNumber());
| + | |
- | comments.put(entry.getKey(), entry.getValue().getComment());
| + | |
- | }
| + | |
- | aRequest.setAttribute(“numbers”, numbers);
| + | |
- | aRequest.setAttribute(“comments”, comments);
| + | |
- | if (aRequest.getAttribute(“message”) == null) {
| + | |
- | aRequest.setAttribute(“message”, “”);
| + | |
- | }
| + | |
- | }
| + | |
- | | + | |
- | Метод makeDataToView() – существенная часть метода handleView() класса AddressBookServlet. Вы можете удивиться, для чего метод initCommand() содержит так много параметров; это необходимо для того, чтобы в момент создания команды полностью передать ей всю необходимую для ее выполнения информацию. Параметры viewPath, resultPath и errorPath появились не случайно – они предназначены для адресов (видов, если использовать термины MVC), используемых в случае простого отображения данных, удачного и, соответственно, неудачного выполнения команды.
| + | |
- | | + | |
- | Перейдем к реализации самих команд. Рассмотрим, например, метод execute() класса EditHTTPCommand. Он практически полностью соответствует первоначальному методу handleEdit класса AddressBookServlet, исключая переадресацию пользователя на конкретный вид.
| + | |
- | | + | |
- | public void execute() throws Exception {
| + | |
- | if (aRequest.getParameter(“number”) == null) {
| + | |
- | _addressBook.removeContactByNumber(aRequest.getParameter(“number”));
| + | |
- | aRequest.setAttribute(“message”,“Не определено, что редактировать”);
| + | |
- | outputPage(this.getErrorPath(), aRequest, aResponse);
| + | |
- | } else if (aRequest.getParameter(“edited”) != null) {
| + | |
- | _addressBook.editContactByNumber(aRequest.getParameter(“edited”),
| + | |
- | aRequest.getParameter(“name”),
| + | |
- | aRequest.getParameter(“number”),
| + | |
- | aRequest.getParameter(“comment”));
| + | |
- | aRequest.setAttribute(“message”, “Контакт \”” +
| + | |
- | aRequest.getParameter(“name”) + “\” отредактирован”);
| + | |
- | makeDataToView();
| + | |
- | outputPage(this.getResultPath(), aRequest, aResponse);
| + | |
- | } else {
| + | |
- | Contact contact = _addressBook.getContactByNumber(aRequest.getParameter(“number”));
| + | |
- | aRequest.setAttribute(“action”, “edit”);
| + | |
- | aRequest.setAttribute(“edit.name”, contact.getName());
| + | |
- | aRequest.setAttribute(“edit.number”, contact.getNumber());
| + | |
- | aRequest.setAttribute(“edit.comment”, contact.getComment());
| + | |
- | outputPage(this.getViewPath(), aRequest, aResponse);
| + | |
- | }
| + | |
- | }
| + | |
- | | + | |
- | Остальные команды реализуются аналогичным образом. Исключение из общего процесса рефакторинга кода составит команда ViewHTTPCommand, для которой уже реализована большая часть функционала:
| + | |
- | | + | |
- | === Хранение настроек команд ===
| + | |
- | | + | |
- | | + | |
- | | + | |
- | === Создание экземпляра класса по имени ===
| + | |
- | | + | |
- | | + | |
- | | + | |
- | === Фабрика команд ===
| + | |
- | | + | |
- | | + | |
- | | + | |
- | === Что дальше? ===
| + | |
- | | + | |
- | | + | |
- | ----
| + | |
- | | + | |
- | == Литература ==
| + | |
- | | + | |
- | #Тейт, Б. Горький вкус Java: Библиотека программиста. – СПб: Питер, 2003. – 333 с.
| + | |
- | #Мартин, Р.С. Быстрая разработка программ: принципы, примеры, практика. – М.: Издательский дом «Вильямс», 2004. – 752 с.: ил.
| + | |