пятница, 25 февраля 2011 г.

Паттерн Front Controller

Доброе время суток всем, кто читает мой блог.
Начинаем цикл статей, а возможно и не цикл, посвященных проектированию «корпоративных» приложений. Сегодня разберем паттерн Front Controller. Пример будет на Java, возможно в последующих статьях появятся примеры на PHP.

Итак, начнем.

Зачем нужен этот паттерн. Front Controller или контроллер запросов объединяет все действия по обработке запросов в одном месте, распределяя их выполнение посредством единого объекта обработчика. Это бывает очень удобно, когда, например, для web-сайта нам надо выполнить вначале проверку на авторизацию пользователя, а потом производить другие действия.
Uml диаграмма данного паттерна выглядит так:

Рис. 1 - UML диаграмма паттерна

Реализуем данный шаблон.
Структура проекта:

Рис. 2 - структура проекта

Класс FrontServlet реализует паттерн Front Controller. Данный контроллер будет обрабатывать запрос get стиля: http://localhost:8080/index?command=ggg

public class FrontServlet extends HttpServlet{
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        FrontCommand command = null;
        try {
            command = getCommand(request);
            command.init(getServletContext(), request, response);
            command.process();
        } catch (ApplicationException e) {
            e.printStackTrace();
        }
    }

    private FrontCommand getCommand(HttpServletRequest request)
            throws ApplicationException {
        try {
            return (FrontCommand) getCommandClass(request).newInstance();
        } catch (Exception e) {
            throw new ApplicationException();
        }
    }

    private Class getCommandClass(HttpServletRequest request) {
        Class result;
        final String commandClassName = "net.inver.web." +
                (String) request.getParameter("command") + "Command";

        try {
            result = Class.forName(commandClassName);
        } catch (ClassNotFoundException e) {
            result = UnknownCommand.class;
        }
        return result;
    }
}

Контроллер выполняет команды, которые являются наследниками абстрактного класса FrontCommand.

abstract public class FrontCommand {
    protected ServletContext context;
    protected HttpServletRequest request;
    protected HttpServletResponse response;

    public void init(ServletContext context,
                     HttpServletRequest request,
                     HttpServletResponse response) {
        this.context = context;
        this.request = request;
        this.response = response;
    }

    abstract public void process() throws ServletException, IOException;

    protected void forward(String target) throws ServletException, IOException {
        RequestDispatcher dispatcher = context.getRequestDispatcher(target);
        dispatcher.forward(request, response);
    }
}

Метод, который надо реализовать в наследниках – process. В ваших командах в данном методе будет происходить выполнение вашей логики.
Метод init будет выполняться в самом начале.
В нашем приложении у нас будет 2 команды – home и unknown.
2-я команда – это логика, которая будет выполняться при запросе несуществующей страницы.
Для каждой команды нам надо создать по JSP странице, которые собственно и будут выводиться. По Фаулеру, в этом приложении, кроме шаблона Front Controller, применяется еще несколько. Рассказ о них мы опустим:)

Ну и web.xml:



  
    FrontController
    net.inver.web.FrontServlet
  

  
    FrontController
    /index
  



В результате:
Все http-запросы будут обрабатываться этим сервлетом. Для того, чтобы ипользовать запросы к картинкам, стилям и прочему, нужно это реализовывать отдельно.

Пример реального использования данного шаблона во фреймворках – Spring framework, Zend framework.
В спринге в действии по-умолчанию вызывается – home, в зенде – indexAction.

Исходники: Source

Спасибо за внимание.

P.S. Для того, чтобы действия выполнять по адресам, которые нам нужны, применяется другой паттерн, который, возможно. называется Router(в зенде по крайней мере так). Но об этом в другой статье.

4 комментария:

  1. Front controller один на все приложение? В таком случае получается что для более менее "ентерпрайзного" приложения прийдется пол сотни разных команд делать, для каждой из которых отдельный класс итд. вагон "холостого" кодирования. Какие бенефиты перед Dispatcher(Router) + Controllers? имхо задача структурирования запросов там лучше решается.

    PS.

    ОтветитьУдалить
  2. и PS. в чем отличие от Command из GoF?

    ОтветитьУдалить
  3. Ага.. хорошие кстати вопросы)
    У меня встречный! Под Controllers ты что подразумевал?
    по этому же Фаулеру есть 3 типа:
    Page Controller
    Front Controller
    Application Controller
    Который из них?

    ОтветитьУдалить
  4. Page controller навеное, тот что из MVC

    ОтветитьУдалить