Объектно-ориентированный дизайн ExtJS
Одна из задач, которую ставили себе разработчики библиотеки ExtJS при ее проектировании, была возможность сосуществования с другими JavaScript-библиотеками и фреймворками. Для этого были добавлены удобные методы, помогающие разработчику создавать, наследовать и сопровождать код различных классов. Несмотря на то, что все они были специально созданы для работы с компонентами ExtJS, их можно использовать с любыми объектами JavaScript, в том числе в проектах, не задействующих визуальные компоненты.
Ext.namespace
Синтаксис: Ext.namespace( String namespace1, String namespace2, String etc ) : void
В языке JavaScript пространство имен само является объектом, содержащим список определений других объектов. Правильное использование пространств имен гарантирует, что ваш код не будет конфликтовать с другими библиотеками. Однако, чем больше становится кода в библиотеке, тем глубже иерархия имен и такие классы как Ext.ux.graphing.GraphPanel перестают быть редкостью. В ExtJS существует метод Ext.namespace, который позволяет определить все пространства имен за один вызов, уменьшая не только затраты на поддержку кода, но и сокращая его размер.
Традиционный код JavaScript, создающий иерархию пространства имен, выглядит следующим образом:
var Ext = Ext || {}; Ext.ux = Ext.ux || {}; Ext.ux.graphing = Ext.ux.graphing || {}; Ext.ux.soundFx = Ext.ux.soundFx || {}; Ext.ux.maps = Ext.ux.maps || {};
Того же результата можно достичь вызовом всего одного метода:
Ext.namespace("Ext.ux.graphing", "Ext.ux.soundFx", "Ext.ux.maps");
Ext.override
Синтаксис: Ext.override( Object origclass, Object overrides ) : void
JavaScript очень гибкий язык, и переопределение метода в нем выполняется простым переназначением его объекта другой функции:
// создаем функцию function doesStuff() { alert("Функция один!"); }; // теперь ее перекрываем doesStuff = function() { alert("Функция два!"); };
Перекрытие метода класса осуществляется путем переопределения его прототипа:
function MyClass() { // конструктор класса } MyClass.prototype.myMethod = function() { alert("привет"); }; var x = new MyClass(); x.myMethod(); // отобразит "привет" MyClass.prototype.myMethod = function() { alert("пока"); }; x.myMethod(); // отобразит "пока"
Используя тот же способ, разработчик может добавить новые методы в существующий класс.
Ext.override – это удобный способ переопределения методов класса за один присест. Следующий пример перекрывает два метода класса Ext.ux.graphing.GraphPanel. Следуя общепринятым правилам языка JavaScript, если в оригинальном классе не содержатся определений методов, то они добавляются. Первым параметром идет класс, который будет изменен, а вторым – JSON объект с методами, подлежащими переопределению:
Ext.override(Ext.ux.graphing.GraphPanel, { clearAllPoints: function() { // выполнить другие действия }, loadData: function(newData) { // загрузка новых данных } });
Этот простой метод имеет огромное значение при поддержке собственных изменений в чужом коде. Опытные JavaScript-разработчики прекрасно знают, как бывает тяжело использовать модифицированные сторонние библиотеки: каждый их новый релиз порождает необходимость проводить сверку измененных частей и внесения исправлений в собственный код. Используя метод Ext.override, разработчики могут поместить свой код в отдельный файл, связать его со сторонней библиотекой и быть уверенными, что в новом релизе ничего не сломается.
Ext.extend и соглашения о параметрах конструкторов
Синтаксис: Ext.extend( Function superclass, Object overrides ) : Function
или Ext.extend( Function subclass, Function superclass, [Object overrides] ) : Function
Рассмотренный ранее метод Ext.override выполняет замену реализации на новый метод целиком, а Ext.extend позволяет осуществить полноценное наследование согласно парадигме ООП. Имеются два варианта вызова этого метода, каждый из которых возвращает определение нового класса, содержащего объекты, унаследованные от родительского, определенного в параметре superclass, и перекрывает методы, заданные в параметре overrides. Также он добавляет к новому классу служебный метод override(), который дает возможность перекрывать методы экземпляра класса, и такое же служебное свойство superclass, позволяющее получить доступ к родительскому объекту.
Библиотека ExtJS имеет ряд соглашений о стиле кодирования, которые позволяют улучшить поддержку и читаемость написанного кода. Одно из них: соглашение о виде конструкторов классов. Практически все конструкторы ExtJS принимают единственный параметр, являющийся объектом нотации в формате JSON и содержащий конфигурацию того или иного компонента. Для приверженцев традиционных объектно-ориентированных языков программирования на первый взгляд это может показаться диким, так как обычно сигнатуры методов (включая конструкторы классов) ожидают явно заданные параметры с жестко определенным типом. Но из-за того, что JavaScript динамически типизированный язык, создавать таким образом объекты с помощью классов не всегда удобно.
Давайте рассмотрим на примере, как такое обычное определение будет порождать проблемы при работе с JavaScript. Допустим, что существует версия 1 класса classA, конструктор которого принимает параметр типа String. Спустя три месяца выходит версия 2 этого же класса, в которой первый параметр должен быть типа Date. Без использования компилятора разработчику только и остается перелопачивать весь свой код и менять его, надеясь, что не будут пропущены какие-либо его части, подлежащие изменению. Это наиболее важная и актуальная проблема программирования на JavaScript, и для ее преодоления в ExtJS выбран простой, но эффективный способ: передача в конструкторе единственного параметра, содержащего необходимую конфигурацию. Другим преимуществом такого подхода является возможность простого расширения функциональности – разработчики могут безбоязненно работать с объектами ExtJS, не сомневаясь, что в следующих версиях конструктор будет изменен, и код станет нежизнеспособным.
Первый вариант вызова Ext.extend принимает два параметра: первый – superclass, указывающий на класс-родитель, второй – overrides, объект содержащий методы, подлежащие добавлению или перекрытию. В том случае, если параметр overrides содержит определение объекта constructor, то он будет использован в качестве конструктора нового класса. Если же объект constructor не задан, то метод Ext.extend создаст новый конструктор, который будет вызывать с теми же параметрами родительский.
Ниже приведен пример использования Ext.extend. Обратите внимание, что имеются два способа наследования. Первый:
var newClass = Ext.extend(Ext.form.ComboBox, { constructor: function(config) { // явное задание конструктора config.emptyText = "'5' не допускается"; newClass.superclass.constructor.call(this, config); }, setValue: function(v) { if (v == 5) { // если v равно 5, сообщить пользователю alert("'5' не допускается"); } else { // иначе вызываем метод родителя newClass.superclass.setValue.call(this, v); } } });
Второй:
function newClass(config) { // конструктор config.emptyText = "'5' не допускается"; newClass.superclass.constructor.call(this, config); }; Ext.extend(newClass, Ext.form.ComboBox, { setValue: function(v) { if (v == 5) { // если v равно 5, сообщить пользователю alert("'5' не допускается"); } else { // иначе вызываем метод родителя newClass.superclass.setValue.call(this, v); } } });
Так как все объекты ExtJS имеют одну и ту же форму указания параметров в конструкторе, это позволяет работать с ними без особых усилий: достаточно скопировать все значения объекта config. ExtJS имеет два встроенных метода для выполнения этих действий, которые далее мы и рассмотрим.
Ext.apply
Синтаксис: Ext.apply( Object obj, Object config, [Object defaults] ) : Object
Метод Ext.apply производит копирование каждого члена объекта config в объект obj. Опционально может быть задан параметр defaults, содержимое которого также копируется в объект obj, но перед выполнением копирования объекта config.
Ext.applyIf
Синтаксис: Ext.applyIf( Object obj, Object config ) : Object
Метод Ext.applyIf производит копирование каждого члена объекта config в объект obj, но только в том случае, когда этот член еще не существует в объекте назначения.
Обычно разработчики создают дочерние классы с целью расширить функциональность элементов. Однако эта новая функциональность может потребовать дополнительной настройки, которая при помощи использования рассмотренных нами методов превращается в простейшую задачу:
function MyClass(config) { // конструктор, который принимает конфигурацию в параметре 'config' // присвоение некоторых значений по умолчанию, если они не заданы в конфигурации config = Ext.applyIf(config, { someValue: defaultValue }); // а здесь присваиваются значения по умолчанию, игнорирующие параметры конструктора config = Ext.apply(config, { someOtherValue: defaultValue }); // вызов конструктора класса-родителя с передачей новой конфигурации MyClass.superclass.constructor.call(this, config); } // не забудьте унаследовать родительский класс Ext.extend(MyClass, MyBase, { // ... });
Начинающие разработчики часто не думают использовать эти приемы, а изобретают свой велосипед. Однако нашей целью как раз является показать простоту и удобство встроенных методов.
Продолжение следует…
Перейти к содержанию Учебника по ExtJS
Мы помогли Вам? Вы можете поддержать дальнейшее создание учебника по ExtJS.
Большое спасибо за материал. Продолжайте в том же духе, а то информации на русском языке очень мало…. Думаю, такой учебник пригодится многим.
Большое спасибо за материал. Продолжайте в том же духе, а то информации на русском языке очень мало…. Думаю, такой учебник пригодится многим. Жду продолжения
Жаль что это материал попал в руки уже после того как бессонные ингулецкие ночи были потрачены на его пнимание =)