Feed Rss



Апр 16 2009

RIA. Теория и практика толстых клиентов

рубрика: Javascript Автор:

Содержание

  1. Преамбула.
  2. На кого ориентирована данная статья.
  3. RIA — Rich Internet Application.
  4. Почему толстый клиент, а не обычный веб-сайт?
  5. Почему обычный веб-сайт, а не толстый клиент?
  6. Что стоит учесть при разработке RIA.
    1. JS — не только рюшечки.
    2. На клиенте тоже бывает MVC.
    3. Что вынести в контроллер?
    4. Почему Observer — хорошо?
  7. Способы оптимизации толстого клиента.

Преамбула

Начать писать эту статью я собирался еще пару месяцев назад, но как-то не представлял целостного материала, и на тот момент она была бы скорее заметками об использовании Javascript в средних и крупных проектах, и только сейчас все мысли сформировались в целостную статью, а времени достаточно, что бы ее оформить «на бумаге»…

На кого ориентирована данная статья.

Надеюсь что материал получится изложить достаточно доступно для начинающих разработчиков, но даже они должны знать что такое Javascript, как он взаимодействует с сервером и иметь хотя бы некоторые представления о понятии «шаблоны проектирования» или хотя бы знать где о них почитать. Так же желателен опыт разработки на Javascript приложений более серьездных, чем вывод сообщений в alert() и заполнение списков связанными данными.

RIA — Rich Internet Application.

Итак, что же такое RIA? Rich Internet Application (богатое интернет приложение, толстый клиент) — это часть клиент-серверной системы (а именно — клиент), позволяющее перенести больш?ю часть логики от удаленного сервера на компьютер пользователя. В рамках веб-сайтов RIA — приложения написанные для выполнения в браузере или специальной виртуальной машине, такой как JavaVM или FlashPlayer. Наиболее часто все же встречаются RIA написанные на Javascript.

Если при обычном подходе корзина пользователя в интернет магазине находилась в базе данных на сервере и обновлялась по мере поступающих запросов от клиента к серверу (Рис. 1), то теперь, создавая RIA, вся эта логика может находиться в браузере каждого пользователя, а данные лишь переодически синхронизироваться с сервером (Рис. 2).

Почему толстый клиент, а не обычный веб-сайт?

Разобравшись что же такое RIA и как оно устроено, можно говорить о преимущетсвах данной архитектуры:

  • толстый клиент не заставляет пользователя ждать реакции на каждое действие, так как нет необходимости каждый раз обращаться к серверу за обработкой данных и генерацией ответа. Пользователь будет доволен, а значит мы сможем снова его увидеть на нашем сайте.
  • каждое действие теперь не обязано перерисовывать всю страницу, с которой работает Пользователь, а может лишь изменять ту часть, что изменилась. Это приводит к тому, что нет надобности забивать канал сервера бессмысленной передачей одних и тех же данных — полезная пропускная способность канала увеличивается. Второй плюс этого пункта — Пользовать может работать с Приложением не отвлекаясь на ограничения реального мира: тонкий интернет-канал, долгий ответ от сервера и т.д.
  • Действия не требующие нетривиальной логики (проверка форм, подсчет суммы покупок и т.п.) теперь могут быть выполнены прямо в браузере Пользователя. А как следствие — загрузка ЦП сервера будет ниже.
  • RIA можно сделать автономным. И тогда пользователь сможет работать с сайтом даже в отсутствии доступа к Интернет, а все внесенные изменения смогут синхронизироваться при первом подключении к Сети.
  • В RIA интерфейс может быть динамичнее. Теперь серфинг по сайту не будет скучной сменой одного слайда-страницы на другой. Теперь данные смогут гармонично изменять свой вид при помощи богатых графических средств.

Наверное можно привести еще аргументы в пользу богатого приложения, но и этого списка уже должно быть достаточно, что бы задуматься о разработке веб-сайтов именно в таком ключе. Но несмотря на такие красноречивые обещания выгоды, есть и недостатки. Именно о них и поговорим ниже.

Почему обычный веб-сайт, а не толстый клиент?

В предыдущем разделе я перечислил основные преимущества RIA перед классическим веб-сайтом. Все они достаточно весомы и вроде бы уже убедили перейти на новую архитектуру, но есть ли у такого подхода недостатки? Масса!

  • Самая существенная проблема — толстый клиент сложен в разработке. Если сроки малы, команда разработчиков малопрофессиональна, архитектор даже не вникал в суть проекта (а порой архитектора и вообще нет), то я бы не рекомендовал пытаться реализовать такую систему. Не стоит забывать о том, что необходимо продублировать логику приложения на клиенте (часто дублируется только часть логики, но и эта часть может достигать 70-80%%), необходимо продублировать обработку всевозможных ошибок и исключительных ситуаций (а именно это является большей частью работы над системой, нежели программирование удачных сценариев), на проектирование придется потратить значительно больше времени, так как пара неверных предположений в дизайне архитектуры и переписывать прийдется очень много.
  • Много кода — долгая предзагрузка (Рис. 3). Вобщем-то это не такая уж и проблема, если ваш проект смог о себе заявить и является полезным инструментом для пользователей, так как из графика видно, что объем загружаемых данных у RIA значительно выше (а время отклика соответственно ниже) только первое время работы с сайтом. Если же пользователь проводит на нем несколько часов в день, то он будет готов пожертвовать чуть более длительной первой загрузкой вначале, но зато потом будет существенно экономить время и трафик.
  • Браузеры. Да, браузер пользователя — следующая существенная проблема перехода к толстым клиентам. Разнообразие трактовок стандарта ECMA поражает. И хотя разработчики Javascript фреймворков практически нивелируют эту проблему, некоторые особенности необходимо знать и не забывать о них, иначе разработка превратиться в кошмар. (таким кошмаром для меня иногда бывает лишняя запятая в конце массивов, так как IE не понимает такой конструкции1)Вторая проблема из-за браузеров — скорость обработки Javascript. И хотя последние тенденции ведут к тому, что эту проблему можно будет вычеркнуть, в настоящий момент о ней не стоит забывать — часто толстый клиент может вешать браузер на некоторое время из-за повышенных нагрузок на ЦП.
  • Индексация поисковыми системами. Вобщем-то как и следовало ожидать — поисковик может захватить только моментное состояние страницы — взаимодействовать посредством Javascript он не станет и соответственно всю доступную информацию проиндексировать врядли сможет. И хотя можно постараться сделать возможность взаимодействия с сайтом и доступ ко всем разделам как с JS так и без него, но врядли это будет окупаться, так как ориентироваться надо скорее на пользователя, а он редко когда прийдет без JS.

Что стоит учесть при разработке RIA.

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

Во-вторых стоит понимать, что Javascript штука нестабильная. Нестабильная в том плане, что открыть спецификации ECMA и следовать тому что там написано — недостаточно. Прийдется многое искать в Интернете, спрашивать у более опытных знакомых, пытаться понять, эксперементируя с данными.

В-третьих — RIA очень близок к обычному прикладному ПО, а значит подходы реализации для них будут практически идентичные, и именно поэтому хорошо было бы заранее ознакомиться с вариантами архитектур «настольного» ПО (шаблоны проектирования тут сыграют не последнюю роль).

Прежде чем приступить к разработке сложной системы, стоит учесть ее стоимость. Нередко может получиться так, что проще купить большинство компонент, а не пытаться реализовать их самостоятельно (и да простят меня вездесущие любители велосипедов).

JS — не только рюшечки.

Как уже я упоминал ранее — разрабатывать RIA удобнее сейчас используя Javascript. Именно он позволяет создавать сложные приложения, которые не выделяются из страницы, а дополняют ее (скажем тот же flash выглядит нелепыми вставками-островками, отстраненно подгружается и может блокироваться вещами вроде AdBlockPlus). Но почему-то сложилась такая традиция — думать о JS как о каком-то выродке — недоязыке, способном генерировать только уродские alert’ы, пробегаться по массиву элементов и, возможно, изменять стили участков документа. Но так кажется только на первый взгляд. И все из-за того, что первые версии браузеров слишком примитивно и неоднозначно поддерживали стандарты этого языка. Появление фреймворков типа Mootools, JQuery, ExtJS и пр. позволила перейти на новый более качественный уровень разработки клиентских приложений.

Не нужно забывать о том, что JS — ОО язык, а это значит что на нем возможно реализовать основные ОО парадигмы и, как следствие, шаблоны. Задумывались ли вы, как создать шаблон Одиночка на JS? Достаточно просто, и мы даже можем защитить методы и свойства от внешнего доступа:

var Singleton = function () {
    // тут идут приватные свойства и методы
    var private_variable = 'foo';

    privateFunction() {}

    // а тут описываем публичные методы и свойства

    return {
        public_variable : 'bar',
        publicFunction : function () {}
    }
}();

Все что мы могли бы описать на сервере мы можем описать и на клиенте — любая архитектура классов, алгоритмы поведения и пр.

На клиенте тоже бывает MVC.

Любой более-менее опытный разработчик уже не раз слышал об MVC — этом преславутом распределении на Model, View и Controller. И почему-то все воспринимают на веру что конечное приложение в идеале должно вписываться именно в такую архитектуру, и только некоторые со временем понимают всю абсурдность подобной затеи. Во-первых сам шаблон имеет в своем основании другой шаблон, который называется Controller и описан в GRASP, зачем к нему дописали еще две буквы и приподносят как совершенно иную идею непонятно. А во-вторых приложения очень редко ограничиваются тремя слоями. Часто их может быть 4, 5 и много больше — чем сложнее система, тем больше уровней абстракции у нее будет. Так в рамках веб-проекта преславутый MVC превращается в MV(MVC)C — представление в архитектуре серверной части, является клиентом со своими M, V и C.

Так у нашего интернет-магазина может быть модель Пользователь и на сервере и на клиенте. У них может быть не только схожая архитектура, но и поведения чем-то будут похожи. Хотя клиентская часть подталкивает на более сложные связи между объектами и способами их взаимодействия.

Что вынести в контроллер?

Какие же обязанности получают компоненты архитектуры MVC на клиенте? На рисунке ниже (Рис. 4) схематически представлены основные ответсевнности между объектами: Пользователь совершает действие в браузере, на которое реагирует элемент представления (например щелчок мышкой); Представление делегирует это событие в контроллер, Контроллер создает или изменяет какие-то объекты Модели, а так же возможно создает новые элементы представления.

Таким образом очевидно, что в контроллер должны попасть методы обработки всех возможных действий Пользователя. Хорошим вопросом сейчас станет: «А как же меняются представления и откуда представление знает куда делегировать действия Пользователя?». За осведомленность представления отвечает контроллер, который их и создает (представления всмысле) — тоесть он сам сообщает им на какие действия куда направлять обработку, а на изменение представления влияет… модель. Но ведь изменять View в Model это очень нехорошо по причине высокого связывания, а так же система слишком много начинает знать о способах представления информации — повторно модель невозможно будет использовать! Именно для того, что бы этого избежать и необходим шаблон Observer.

Почему Observer — хорошо?

Шаблон Observer отвечает за связь между объектами через интерфейсы, а не через прямое влияние, что существенно снижает связность. Суть шаблона довольно проста. Попробуем рассмотреть ее на примере:

Есть объект Chart, он является представлением (например графиком). Он умеет себя дорисовывать, основываясь на предоставленных данных. За это у него отвечает метод update( x, y), который ставит новую точку на координатной плоскости с связывает ее прямой с предыдущей точкой. Таким образом мы можем нарисовать воображаемый график так:

var chart = nwe Chart();
chart.update( 1, 1 );
chart.update( 2, 3 );
chart.update( 3, 5 );
chart.update( 4, 2 );
chart.update( 5, 4 );

и в результате получим такую картинку (Рис. 5):

Но одно дело, если это просто абстрактный график, а другое — статистическая информация, обновляемая на основании модели. Допустим что наш график отображает кол-во денег на счете при каждой транзакции, а модель этого счета будет экземпляр класса Account1. Этот класс обладает свойством amount (общее кол-во) и методами deposite( x ) и withdrawal( x ), которые вызываются для пополнения и списания денег со счета соответственно.

var Account = new Class( {
    amount : 0, // начальная сумма
    transactions_count : 0,
    deposite : function ( amount ) {
        this.amount += amount;
        this.transactions_count++;
    },
    withdrawal : function ( amount ) {
        this.amount -= amount;
        this.transactions_count++;
    }
} );

Как должна выглядеть работа на уровне моделей для списания и зачисления денег? Ну как-то вот так:

var account = new Account();
account.deposite( 1 );
account.deposite( 2 );
account.deposite( 2 );
account.withdrawal( 3 );
account.deposite( 2 );

Как же нам отрисовать каждое изменение на графике? Можно например так:

var chart = nwe Chart();
var account = new Account();

account.deposite( 1 );
chart.update( account.transactions_count, account.amount );
account.deposite( 2 );
chart.update( account.transactions_count, account.amount );
account.deposite( 2 );
chart.update( account.transactions_count, account.amount );
account.withdrawal( 3 );
chart.update( account.transactions_count, account.amount );
account.deposite( 2 );
chart.update( account.transactions_count, account.amount );

С одной стороны цель вроде бы достигнута. С другой мучают сомнения а что если графиков на странице станет несколько, или какие-то элементы должны окраситься в другой цвет на странице, если сумма счета будет меньше ноля? Тогда прийдется добавлять по строке на каждый такой элемент, а местами еще и логику через if…else дописывать. Получитсья какой-то ужас. Тут и приходит на помощь Observer.

Классы, реализующие этот шаблон, должны иметь два дополнительных метода:

  • addEvent( type, method ) — добавляющий в список «слушателей» типа type ссылку на метод method
  • fireEvent( type, arguments ) — итерирующий список «слушателей» типа type и вызывающий метод method с аргументами arguments.

Теперь наш класс Account будет выглядеть так:

var Account = new Class( {
    Implements : Events, // в Mootools таким образом автоматически добавляются возможности Observer'а
    amount : 0, // начальная сумма
    transactions_count : 0,
    deposite : function ( amount ) {
        this.amount += amount;
        this.transactions_count++;

        this.fireEvent( 'onAmountChange', [ this.transactions_count, this.amount ] );
    },
    withdrawal : function ( amount ) {
        this.amount -= amount;
        this.transactions_count++;

        this.fireEvent( 'onAmountChange', [ this.transactions_count, this.amount ] );
    }
} );

Ну а код отображения информации на графике можно переписать так:

var chart1 = nwe Chart();
var chart2 = nwe Chart();
var chart3 = nwe Chart();
var account = new Account();

account.addEvent( 'onAmountChange', chart1.update );
account.addEvent( 'onAmountChange', chart2.update );
account.addEvent( 'onAmountChange', chart3.update );

account.deposite( 1 );
account.deposite( 2 );
account.deposite( 2 );
account.withdrawal( 3 );
account.deposite( 2 );

Теперь при каждом изменении остатка на счете, все графики будут перерисовываться. Таким образом объекты, отвечающие за представления не будут знать о модели, а модель о представлении. Они будут осведомлены только о параметрах, которые передаются в сообщениях.

В более ОО языках шаблон реализуется немного иначе — через интерфейсы, но и в таком виде Observer упрощает во многом жизнь. В случае с MVC он позволяет изменять представление при каких-либо изменениях в модели, пораждая события и делегируя их заранее подписанным объектам.

Способы оптимизации толстого клиента.

Говорить много об оптимизации RIA нет особого смысла — для нее справедливы все замечания по оптимизации любого Javascript кода на клиенте, но отдельно хочу заметить что тут не будет лишним отложенная загрузка дополнительных скриптов по мере надобности, так как сразу весь код приложения врядли понадобиться. А так же использование CDN.

Понравился пост? Подпишись на RSS!

Метки: , , , , , ,

5 ответов на “RIA. Теория и практика толстых клиентов”

  1. Surg420 says:

    Сумбурно немного. Алексей, соберись!
    Наблюдатель эт конечно славненько, но уже как-то банально…
    А вообще утолщение клиента тенденция приятная.
    давай ещё, хоть тебя почитаю)

  2. Алексей Токарь says:

    2<>Surg420<>: не только ж на тебя статья расчитана.

    На неделе, возможно, состряпаю материал по более редким шаблонам с конкретными примерами.

  3. Serge says:

    >таким кошмаром для меня иногда бывает лишняя запятая в конце массивов, так как IE не понимает такой конструкции.

    Проблема решается использованием нормального IDE. На сайте ExtJS есть FAQ по этому вопросу.

  4. Алексей Токарь says:

    2<>Serge<>: Вы слишком буквально воспринимаете сказанное :)

  5. Serge says:

    :) Но для меня это действительно был кошмар… по-началу )))