Модульное проектирование RIA проектов

    Проблемы при разработке больших проектов


    ninjaОтделение котлет данных от представления, проектирование, велосипеды… главное без фанатизма. Какая проблема часто встречается при разработке RIA приложений. Команда сильных программистов «влюбляется» в новый проект, и с мыслями «сейчас мы сделаем нечто — главное все сделать самим, чтобы ни один чужой баг не закрался» начинают отказываться от готовых фреймворков, библиотек, решений.
    Правда существует одно логичное объяснение этой тенденции, боязнь завязаться на чужой продукт, бывает такое, что в ходе разработки выясняется — такой интерфейс будет очень сложно разработать на доступном инструментарии, и рождаются костыли, правки чужих фреймворков и тд. Хотел бы предложить решение этой проблемы. Создание менеджера интерфейсов (модулей). Один модуль может использовать ExtJS, другой dhtmlx, а может и вообще чистый Javascript

    Не чужое — а открытое


    Какие плюсы использования готовых решений? Проще найти нового человека на проект, когда вы используете известный сторонний продукт. В ситуации когда вы остались единственный из создателей фреймворка и решаетесь уйти, то скорее всего новая команда перепишет все с нуля. Плюс правка багов и добавление новых фич пока вы работаете над бизнес логикой. Хотите мощное оружие в разделении данных от представления – используйте ExtJS например, зачем писать свой фреймворк!? Нужно динамически подгружать зависимости – посмотрите в сторону YepNope. Работу с dom предоставьте jQuery или любому другому известному фреймворку. Сконцентрируйтесь на результате. Я понимаю что большинство программистов любит писать все с нуля – но это не от того что он хочет учится, развиваться… это от лени… да-да, именно от лени. Изучение чужого кода, чтение документации куда сложнее чем разработка всего “с нуля”. Когда пишешь все “с нуля” многому не научишься, люди учатся в основном от других, исследуя чужую работу перенимаешь опыт (это как съесть печень своего противника), а в совокупности со своим опытом придумываешь что-то новое… более совершенное. Создавая стабильные приложения за короткие сроки вы получите больше признания чем закапываясь в правках очередного своего мега-фреймворка после выхода нового браузера IE6, IE7, IE8, IE9.
    Если у вас цель написать свой фреймворк, вместо разработки ERP на которую вам дали срок 2 месяца – то начните с ОС… или с архитектуры процессора… а почему нет!?

    Модульность как лекарство от многих болезней


    Модульность – это принцип разделения сложной системы на отдельные части – модули. Какую выгоду мы получаем – упрощаем разработку, тестирование и поддержание системы, сводим число связей между различными частями системы к минимуму.
    Разработкой ядра и модулями могут заниматься разные люди в команде, причем совершенно не мешаясь друг другу. Рефакторинг ядра, или модулей никак не влияет друг на друга и может проводиться параллельно и независимо.

    Ядро – всему голова


    Ядро приложения – это менеджер частей системы. Топ менеджер комнании, который выполняет функции супервизора. Он контролирует доступ к ресурсам и общение между объектами компании. Но никак не контроль загрузки товара на склад, выдача провианта и тд. Этим пусть занимаются утилиты и библиотеки, которым будут делегироваться задания.
    В обязанности ядра входят:
    • Делегация загрузки модулей и необходимых зависимостей отделу библиотеке (в нашем случае я выбрал YepNope).
    • Делегация создания канвы для интерфейса модуля.
    • Передача информации между модулями.
    • Оповещение о каких либо событиях системы (закрытие приложения, активация нового модуля, изменения размера окна, потеря коннекта с сервером и тд.).
    • Выполнение запросов модулей (например загрузка другого модуля)
    Полный код проекта смотрите на GitHub

    Упрощенный вариант ядра:
    /*=== 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
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 12

      +1
      думаю YUI3 Вам понравится
        0
          0
          Быть может, вы имели в виду CommonJS Modules/1.0 :)
            +2
            ну я вообще намекал автору, что он не одинок в своих изысканиях.
          +5
          $('').appendTo('body');

          Медленно жарить в кипящей смоле за такой код…
            +4
            $('<div id="'+library.name+'"></div>').appendTo('body');

            Ну вы поняли, да?
              +2
              а с чего это ресайз в ядре находится?
              а с чего это ядро привязано к дому?
              нафига каждому модулю по диву? а если ему не нужен див? а если ему нужно 2 дива?
                +4
                Неплохая попытка получить «слабую связанность». Но ещё все впереди :)
                1. Все сильно завязано на jQeury — нужен враппер
                2. Модули знают что $ и $("script[id=core]") есть в глобалах — кроме контекста модуль ничего не должен знать
                3. Нужно убрать smth.onResize («Оповещение о каких-либо событиях системы») модули могут слушать эвент «resize». Вообще модулям достаточно иметь 2 метода: init() и destroy()

                Мне не очень нравится способ «саморегистрации» модулей. Если модуль подчиняется ядру, то почему он ему приказывает «Эй, ядро, это Модуль1 пришел — впиши меня!». Ядро должно быть авторитарным.
                У ядра есть список модулей из которых состоят приложение. Ядро загружает модуль каким-либо образом (асинхронно или при препроцессинге), вписывает, запускает, убивает. Модуль должен просить разрешения на все действия вне контекста(«DOM элемента»).
                — Модуль1: «хочу послушать событие resize, можно?»
                — Ядро: «нет, ты bacground процесс тебе нельзя!»
                ну или как-то так
                  0
                  Если бы я в рамках статьи начал писать еще и враппер, то я думаю понятнее она бы не стала, jQuery можно расценивать как псевдокод.
                  Про список модулей: в моей реальном проекте как раз так и реализовано, для каждого пользователя в зависимости от прав приходит json со списком доступных библиотек и всех ее свойств.
                  Я считаю что не нужно усложнять движок без необходимости для простых примеров, если все учесть — это не статью, а книгу писать надо.
                  +1
                  Почему в ядре завязка на DOM?
                    0
                    Потому что ядро не сферическое в вакууме, а работает тесно с DOM, вот верстку выносить в ядро точно нельзя, а контекст модуля расценивайте как tform в делфи. Ядро же должно уметь скрыть окно модуля, изменить размер, если например мы его развернули. Переключить фокус на другое окно. В делфи и .net приложениях этим занимается winapi по средствам событий, которые приходят форме. А в вебе я бы вынес это в ядро. Можно конечное сделать класс для низкоуровневой работы с окнами, но зачем же так усложнять для просто примера
                    +1
                    Если у вас цель написать свой фреймворк, вместо разработки ERP на которую вам дали срок 2 месяца – то начните с ОС… или с архитектуры процессора… а почему нет!?

                    Все очень просто — заморочиться над архитектурой процессора на пару месяцев гораздо интереснее, чем разрабатывать мифическое ERP, а через два месяца все равно получить по шапке.

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое