Проблемы при разработке больших проектов
Отделение
Правда существует одно логичное объяснение этой тенденции, боязнь завязаться на чужой продукт, бывает такое, что в ходе разработки выясняется — такой интерфейс будет очень сложно разработать на доступном инструментарии, и рождаются костыли, правки чужих фреймворков и тд. Хотел бы предложить решение этой проблемы. Создание менеджера интерфейсов (модулей). Один модуль может использовать 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