LXF94:Команды и фабрики

Материал из Linuxformat.

Перейти к: навигация, поиск

Содержание

Команды и фабрики

ЧАСТЬ 6 Антон Черноусов готов познакомить вас с очередной партией паттернов, которые помогут сделать ваши приложения еще более гибкими и расширяемыми.

Вместо предисловия

В предыдущей статье мой коллега Александр Бабаев рассмотрел вопросы организации и использования БД в Java-приложениях, и в том числе вопросы подключения к БД посредством ConnectionPool.

Сегодня мы рассмотрим применение двух паттернов, безусловно, оказавших огромное воздействие на проектирование систем – Command и Factory Method. Их применение позволит сделать ваше приложение расширяемым.

Команды

В 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() обозначенные выше
Рис. 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, для которой уже реализована большая часть функционала:

Хранение настроек команд

Создание экземпляра класса по имени

Фабрика команд

Что дальше?


Литература

  1. Тейт, Б. Горький вкус Java: Библиотека программиста. – СПб: Питер, 2003. – 333 с.
  2. Мартин, Р.С. Быстрая разработка программ: принципы, примеры, практика. – М.: Издательский дом «Вильямс», 2004. – 752 с.: ил.
Личные инструменты
  • Купить электронную версию
  • Подписаться на бумажную версию