Проблемы при разработке больших проектов
Отделение Правда существует одно логичное объяснение этой тенденции, боязнь завязаться на чужой продукт, бывает такое, что в ходе разработки выясняется — такой интерфейс будет очень сложно разработать на доступном инструментарии, и рождаются костыли, правки чужих фреймворков и тд. Хотел бы предложить решение этой проблемы. Создание менеджера интерфейсов (модулей). Один модуль может использовать ExtJS, другой dhtmlx, а может и вообще чистый Javascript
Не чужое — а открытое
Какие плюсы использования готовых решений? Проще найти нового человека на проект, когда вы используете известный сторонний продукт. В ситуации когда вы остались единственный из создателей фреймворка и решаетесь уйти, то скорее всего новая команда перепишет все с нуля. Плюс правка багов и добавление новых фич пока вы работаете над бизнес логикой. Хотите мощное оружие в разделении данных от представления – используйте ExtJS например, зачем писать свой фреймворк!? Нужно динамически подгружать зависимости – посмотрите в сторону YepNope. Работу с dom предоставьте jQuery или любому другому известному фреймворку. Сконцентрируйтесь на результате. Я понимаю что большинство программистов любит писать все с нуля – но это не от того что он хочет учится, развиваться… это от лени… да-да, именно от лени. Изучение чужого кода, чтение документации куда сложнее чем разработка всего “с нуля”. Когда пишешь все “с нуля” многому не научишься, люди учатся в основном от других, исследуя чужую работу перенимаешь опыт (
Если у вас цель написать свой фреймворк, вместо разработки ERP на которую вам дали срок 2 месяца – то начните с ОС… или с архитектуры процессора… а почему нет!?
Модульность как лекарство от многих болезней
Модульность – это принцип разделения сложной системы на отдельные части – модули. Какую выгоду мы получаем – упрощаем разработку, тестирование и поддержание системы, сводим число связей между различными частями системы к минимуму.
Разработкой ядра и модулями могут заниматься разные люди в команде, причем совершенно не мешаясь друг другу. Рефакторинг ядра, или модулей никак не влияет друг на друга и может проводиться параллельно и независимо.
Ядро – всему голова
Ядро приложения – это менеджер частей системы. Топ менеджер комнании, который выполняет функции супервизора. Он контролирует доступ к ресурсам и общение между объектами компании. Но никак не контроль загрузки товара на склад, выдача провианта и тд. Этим пусть занимаются утилиты и библиотеки, которым будут делегироваться задания.
В обязанности ядра входят:
- Делегация загрузки модулей и необходимых зависимостей
отделубиблиотеке (в нашем случае я выбрал YepNope). - Делегация создания канвы для интерфейса модуля.
- Передача информации между модулями.
- Оповещение о каких либо событиях системы (закрытие приложения, активация нового модуля, изменения размера окна, потеря коннекта с сервером и тд.).
- Выполнение запросов модулей (например загрузка другого модуля)
Упрощенный вариант ядра:
/*=== core.js ===*/ !function () { var listOfLibraries = {}; $(window).resize(function() { $.each(listOfLibraries, function(){ this.body.onResize($(window).width(), $(window).height()); }); }); $("script[id=core]").bind('request', function (data) { switch (data.header) { case "attach": var library = { name: data.body.name, body: new data.body.module() }; listOfLibraries[library.name] = library; $('<div id="'+library.name+'"></div>').appendTo('body'); var moduleContext = $('#'+library.name); library.body.main(moduleContext); break; } }); }();
После своей загрузки, ядро слушает канал сообщений request, при подключении нового модуля он добавляет его в коллекцию listOfLibraries. В этой коллекции хранятся все загруженные модули.
Даешь независимость модулям!
Пример модуля:
/*=== HelloWorld.js ===*/ !function () { var Lib = function () { var context; this.main = function (moduleContext) { context = moduleContext; context.html('Hello World'); } this.onResize = function(width, height){ context.width(width); context.height(height); } } $("script[id=core]").trigger({ type: "request", header: "attach", body: { module: Lib, name: "HelloWorld" } }); } ();
Как видно из примера ядро общается с внешними компонентами по средствам событий, вернее одного события “request” – пула сообщений.
Тело сообщения состоит из заголовка (header) и тела (body). В данном случае модуль сообщает ядру что он хочет зарегистрироваться в системе под именен “HelloWorld” и сообщает ссылку на тело модуля.
Каждый модуль получает от ядра контекст с которым ему нужно работать. Общение с ядром, другими модулями или элементами страницы должно осуществляться через сообщения.
Зачем же нужно тогда ядро, если мы могли легко в модуле создать контекст и работать с ним!?
Отвечаю: для реализации многооконного интерфейса, для подгрузки модулей по требованию, для возможности загрузки зависимостей модулей, для создания единой системы общения между модулями, для просто поддержания кода… и тд.
В итоге мы получили легковесное модульное решение, при котором даже при полном изменении логики ядра – код модулей не меняется.
Все изменения касаются лишь добавления новых событий. Для унификации интерфейса для общения ядра с модулями лучше выделить абстрактный класс с базовой реализацией рутины и все Lib классы модулей наследовать от него.
window.Module = function () { this.context; } window.Module.prototype = { main: function (context) { this.context = context; }, onResize: function (width, height) { } }
При подключении модуля к ядру нужно лишь убедиться, что подключаемый модуль наследует функционал абстрактного класса Module, в случае успеха подключать его к системе.
Полный код проекта смотрите на GitHub
