28 янв. 2011 г.

Чему поучиться у ExtJS

Вступление

Вчера меня снова потянуло на ExtJS. Видимо, жарища так влияет. Поковырявшись в интернете, я нашел черновую версию книги ExtJS in Action, загрузил ее в свой Sony Reader и стал читать. После прочтения трех первых глав пришло осознание того, насколько красиво там сделаны некоторые базовые вещи. В этой статейке я остановлюсь на двух моментах.




1. Config-объекты и xtypes

В ExtJS, если существует компонент, например, MyComponent, то у него обязательно есть родственник MyComponentConfig. Последний содержит все настройки компонента. И возможны два случая.

Либо он передается в конструктор MyComponent-у, и экземпляр MyCompoment создается немедленно. В этом случае такой объект называется config object.
Либо он содержится в коллекции элементов (родительского) контейнера. Тогда контейнер создаст компонент на основе конфигурации, когда в этом будет необходимость. В этом случае MyComponentConfig называют xtype.
Так как ExtJS – это Javascript библиотека, а Javascript – язык с нестрогой типизацией, то MyComponentConfig как такового нету. Вместо него используются Javascript plain objects с полем xtype.

Рассмотрим пример. Мы хотим создать textbox. Вот его настройки:
var fieldConfig = {
    xtype: 'textfield',
    fieldLabel: 'Your name',
    value: 'test'
};
Теперь можно создать textbox явно:
var field = new Ext.form.TextField(fieldConfig);
field.render(document.body);

А можно создать форму с текстбоксом вот так:
var form = new Ext.form.FormPanel({
    items: [ fieldConfig ]
});
form.render(document.body);
Во втором случае есть контейнер (FormPanel), и конфигурация текстбокса находится в коллекции items. Как видно, текстбокс никто не создает – его создаст контейнер.
Вроде бы ничего особенного, но смотрите, что дает нам эта схема:

a) Возможность комбинировать компоненты без каких либо ограничений

Опишу ситуацию на примере, который недавно наблюдал на работе. Допустим вы разработали некий BlazingFastControl. Он достаточно навороченный, у него 100 опций по управлению наворотами:
Прошло время. Надо развивать продукт, и вы решили написать вот такое:
Объединив BlazingFastControl и MegaDropDown, хотите получить некий BlazingFastLookup.

Но вот беда: надо дать пользователю возможность как-то настраивать внутренний компонент. И если настройки не были изолированы в отдельный объект BlazingFastConfig, то придется дублировать все 100 опций и писать глупый код по их копированию. Особенное зло ждет в .NET, где каждая property обычно обвешана десятком атрибутов.

b) Config-driven component assembly

По-русски это значит, что config-объекты – это готовый способ сериализации UI. То есть весь интерфейс или его куски можно описывать и хранить в JSON или в другом формате.
Вы скажете мол ничего удивительного – ASP.NET контролы тоже можно объявлять в разметке. Но есть нюанс:

c) Отложенное создание вложенных компонентов

Представьте себе следующий UI: tab-panel в тремя вкладками, и за каждой вкладкой скрывается целый мир компонентов.
Если это описать в виде config-объектов, то после запуска не будет нужды создавать тяжелые экземпляры всех компонентов. Достаточно будет создать только то, что видно на активной вкладке. А на остальные, может быть, никто и не переключится, и 2/3 компонентов не будут даже создаваться, а так и останутся в зачаточном состоянии. Это – большая экономия ресурсов!
Почитать еще можно на stackoverflow.

2. Layouts

В ExtJS есть контейнеры (Container), которые содержат элементы (items). То, как элементы будут отображаться внутри контейнера, диктует специальный объект Layout.
Объясню на абстрактном примере. Объявим контейнер и элемент так:
class Container {
    public Collection<IItem> Items { get; }
    public ILayout Layout { get;  }
}


interface IItem {
    string Text { get; }
}
Допустим, наше приложение имеет дело с документами:
class MyDocument : IItem {
    public string Title { get; set; }
    public string Content { get; set; }
    // ...

    string IItem.Text { get { return Title; } }
}
Можно создать Container, заполнить его MyDocument-ами. И с помощью разных реализаций ILayout получить меню, табы, nav bar, форму или что-то свое невообразимое.

Заключение

ExtJS демонстрирует удачное применение паттернов проектирования для обеспечения повторного использования, оптимизации и кастомизации. Если вы пишете компоненты, и еще не поздно принимать архитектурные решения, обратите внимание на описанные тут подходы!