В этой статье я бы хотел осветить аспекты применения MVC-шаблона для построения надёжных и гибких приложений. Также указать на возможные проблемы его использования и способы решения обнаруженных проблем.
Здесь рассматривается вариант использования MVC Model 2 или Model 2-шаблонов, относящихся к разработке Java веб приложений.
Model View Controller шаблон позволяет чётко отделить логику обработки запроса пользователя (Controller) от логики, отвечающей за формирование представления (View).
Не применяя данный подход, можно прийти к тому, что логика обработки запроса и логика отображения будут тесно переплетены между собой в коде. Что приведёт к так называемому спагетти. Это когда по мере обработки запроса от пользователя одновременно формируется ответ. Не будет возможности отдельно протестировать логику обработки запроса и логику формирования отображения, что приведёт к неизбежным ошибкам.
Для построения приложений MVC-шаблон можно использовать по-разному. Один из вариантов со своими преимуществами и недостатками представлен ниже.
Можно организовать систему в модули, которые будут слабо связаны между собой. Слабая связанность даёт определённые преимущества в процессе разработки и эксплуатации системы такие как, повторное использование модулей в системе. Модули можно отдельно тестировать от всей системы. Относительно легко заменять, добавлять и удалять модули в процессе эксплуатации, не внося в систему больших изменений.
При этом модули будут связаны только на уровне доменных моделей. Контроллеры и представления разных модулей не будут знать друг о друге. Для работы с системой, пользователи будут вызывать функции каждого модуля последовательно A, B, C, D. Создал сущность через модуль A, потом она стала доступна в модуле B. Создав новую сущность в модуле B, она и сущность модуля A будут доступны в модуле C и т.д. и т.п.
В такой системе пользователи должны чётко представлять себе процесс работы с ней. В какие модули, в какой последовательности заходить, чтобы выполнить задачу.
Каждый новый пользователь должен проходить специальное обучение для работы с ней. Что является, по сути, сдерживающим фактором на пути к массовому использованию системы.
Тут напрашивается решение - связать модули между собой, например, ссылками. Чтобы по нажатию на ссылку в представлении модуля A, вызывался контроллер модуля B. По ссылке из представления модуля B вызывался контроллер модуля C. И т.д.
Пользователи будут рады, что теперь им не нужно будет держать в голове, как и в какой последовательности, работать с системой.
Но при этом, поддержка системы значительно усложнится. Из-за того, что все модули станут тесно связаны между собой. Весь процесс работы с системой будет распределён среди кучи файлов. Любая модификация потребует тестирование всей системы.
И это, как правило, часто происходит при построении приложений. А причина состоит в том, что за деталями не видно сути вещей. Думая в терминах Model, View, Controller легко выйти за рамки определённого модуля и превратить всю систему в один сплошной монолитный модуль.
Для решения этой проблемы понадобится посредник - Root Controller или Front Controller. И сделать так, чтобы все модули общались через него. А он сам руководствовался бы конфигурацией, какой модуль, при каких условиях показывать пользователю для работы.
Здесь требуется пояснение. Т.к. пользователь работает с системой асинхронно, то Root Controller должен хранить информацию об активном модуле. Чтобы обеспечить слабую связанность модулей, они могут общаться с Root Controller посредством событий. Конфигурация будет определять, при каких событиях, какие модули предоставлять пользователю для работы.
Пример конфигурации:
<application initial="A"> <view id="A"> <on event="next" to="B"/> </view> <view id="B" > <on event="previous" to="A"/> <on event="next" to="C"/> </view> <view id="C"> <on event="previous" to="B"/> <on event="next" to="D"/> </view> <view id="D"> <on event="previous" to="C"/> <on event="finish" to="finish"/> </view> <on event="cancel" to="cancel"/> <final id="finish" /> <final id="cancel" /> </application>
Используя только конфигурацию, можно изменить порядок работы с системой. Например, оставить только 2 шага из 4х.
<application initial="A"> <view id="A"> <on event="next" to="D"/> </view> <view id="D"> <on event="previous" to="A"/> <on event="finish" to="finish"/> </view> <on event="cancel" to="cancel"/> <final id="finish" /> <final id="cancel" /> </application>
Преимущества очевидны. Представления и контроллеры модулей оказались нетронутыми, а поведение приложения изменилось.
Основная логика приложения находится в одном файле, что намного удобнее, чем, если бы она была разнесена по нескольким.
Прошу заметить, конфигурация в XML формате здесь была представлена для наглядности. Вполне возможно реализовать такую же конфигурацию используя Domain Specific Language, что обеспечит проверку конфигурации на стадии компиляции.
Для Java уже существуют фреймворки, использующие данный подход, который позволяет избежать сильной связанности между модулями системы. Как вы можете догадаться, эти решения основаны на конечных автоматах или Finite State Machines.
- Spring Web Flow – его удобно использовать в приложениях использующих HTTP Request/Response подход.
- ADF Task Flows – похожая реализация на Spring Web Flow, но от Oracle.
- Lexaden Web Flow – создана специально для быстрого создания гибких корпоративных AJAX приложений с использованием компонентного фреймворка Vaadin.
Основные отличия этих фреймворков в том, что Spring Web Flow и ADF Task Flows тесно связаны с HTTP Request/Response моделью и их сложно использовать с AJAX компонентными фреймворками, такими, как Vaadin. Приходится полностью обновлять всю страницу, игнорируя встроенные AJAX возможности компонентного фреймворка.
Lexaden Web Flow же позволяет использовать всю мощь данного подхода на уровне AJAX запросов. В купе с компонентной моделью Vaadin он позволяет создавать гибкие, масштабируемые, надежные приложения корпоративного уровня.
Релоцировались? Теперь вы можете комментировать без верификации аккаунта.