Дэвид Флэнаган - JavaScript. Подробное руководство, 6-е издание
- Название:JavaScript. Подробное руководство, 6-е издание
- Автор:
- Жанр:
- Издательство:Символ-Плюс
- Год:2012
- Город:СПб
- ISBN:978-5-93286-215-5
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Дэвид Флэнаган - JavaScript. Подробное руководство, 6-е издание краткое содержание
Эта книга - одновременно и руководство программиста, и полноценный справочник по базовому языку JavaScript и клиентским прикладным интерфейсам, предоставляемым веб-броузерами.
JavaScript. Подробное руководство, 6-е издание - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
for(var і = 0; і < arguments.length; i++)
if (arguments[i] == null)
throw new Еrror("Нельзя добавить null или undefined в NonNullSet”);
// Вызвать метод базового суперкласса, чтобы фактически добавить элементы
return Set.prototype.add.apply(this, arguments);
};
Теперь обобщим понятие "множество без пустых элементов" до понятия "фильтрованное множество": множество, элементы которого должны пропускаться через функцию-фильтр перед добавлением. Определим фабричную функцию (подобную функции enumeration
) из примера 9.7), которая будет получать функцию-фильтр и возвращать новый подкласс класса Set
. В действительности можно пойти еще дальше по пути обобщений и определить фабричную функцию, принимающую два аргумента: наследуемый класс и функцию-фильтр, применяемую к методу add().
Новой фабричной функции можно было бы дать имя filteredSetSubclass()
и использовать ее, как показано ниже:
// Определить класс множеств, которые могут хранить только строки
var StringSet = filteredSetSubclass(Set,
function(x) {return typeof x===*'string";});
// Определить класс множеств, которые не могут содержать значения null,
// undefined и функции
var MySet = filteredSetSubclass(NonNullSet,
function(x) {return typeof x !== "function";});
Реализация этой фабричной функции приводится в примере 9.14. Обратите внимание, что эта функция вызывает метод и конструктор базового класса подобно тому, как это реализовано в классе NonNullSet
.
Пример 9.14. Вызов конструктора и метода базового класса
/*
* Эта функция возвращает подкласс указанного класса Set и переопределяет
* метод add() этого класса, применяя указанный фильтр.
*/
function filteredSetSubclass(superclass, filter) {
var constructor = function() { // Конструктор подкласса
superclass.apply(this, arguments); // Вызов конструктора базового класса
};
var proto = constructor.prototype = inherit(superclass.prototype);
proto.constructor = constructor; proto.add = function() {
// Примерить фильтр ко всем аргументам перед добавлением
for(var і = 0; і < arguments.length; i++) {
var v = arguments[i];
if (!filter(v)) throw("значение + v + отвергнуто фильтром");
}
// Вызвать реализацию метода add из базового класса
superclass.prototype.add.apply(this, arguments);
};
return constructor;
}
В примере 9.14 есть один интересный момент, который хотелось бы отметить. Он заключается в том, что, обертывая операцию создания подкласса функцией, мы получаем возможность использовать аргумент superclass
в вызовах конструктора и метода базового класса и избежать указания фактического имени суперкласса. Это означает, что в случае изменения имени суперкласса достаточно будет изменить имя в одном месте, а не отыскивать все его упоминания в программном коде. Такого способа стоит придерживаться даже в случаях, не связанных с определением фабричных функций. Например, с помощью функции-обертки можно было бы переписать определение класса NonNullSet
и метода Function.prototype.extend()
(пример 9.11), как показано ниже:
var NonNullSet = (function() { // Определить и вызвать функцию
var superclass = Set; // Имя суперкласса указывается в одном месте,
return superclass.extend(
function() { superclass.apply(this, arguments); }, // конструктор
{ // методы
add: function() {
// Проверить аргументы на равенство null или undefined
for(var і = 0; і < arguments.length; i++)
if (arguments[i] == null)
throw new Еrror("Нельзя добавить null или undefined");
// Вызвать метод базового класса, чтобы выполнить добавление
return superclass.prototype.add.apply(this, arguments);
}
});
}());
В заключение хотелось бы подчеркнуть, что возможность создания подобных фабрик классов обусловлена динамической природой языка JavaScript. Фабрики классов представляют собой мощный и гибкий инструмент, не имеющий аналогов в языках, подобных Java и C++.
9.7.3. Композиция в сравнении с наследованием
В предыдущем разделе мы решили определить класс множеств, накладывающий ограничения на свои элементы в соответствии с некоторыми критериями, а для достижения поставленной цели использовали механизм наследования, определив функцию создания специализированного подкласса указанной реализации множества, использующего указанную функцию-фильтр для ограничения круга допустимых элементов множества. При таком подходе для каждой комбинации суперкласса и функции-фильтра необходимо создавать новый класс.
Однако существует более простой путь решения этой задачи. В объектно-ориентированном программировании существует известный принцип «предпочтения композиции перед наследованием». [18] См. Э. Гамма и др. «Приемы объектно-ориентированного проектирования. Паттерны проектирования» или Дж. Блох «Java. Эффективное программирование».
В данном случае применение приема композиции могло бы выглядеть как реализация нового множества, «обертывающего» другой объект множества и делегирующего ему все обращения после фильтрации нежелательных элементов. Пример 9.15 демонстрирует, как это реализовать.
Пример 9.15. Композиция множеств вместо наследования
/*
* Объект FilteredSet обертывает указанный объект множества и применяет
* указанный фильтр в своем методе add(). Обращения ко всем остальным базовым
* методам просто передаются обернутому экземпляру множества.
*/
var FilteredSet = Set.extend(
function FilteredSet(set, filter) { // Конструктор
this.set = set; this.filter = filter;
},
{ // Методы экземпляров
add: function() {
// Если фильтр был указан, применить его
if (this.filter) {
for(var і = 0; і < arguments.length; i++) {
var v = arguments[i];
if (!this.filter(v))
throw new Error("FilteredSet: значение " + v + " отвергнуто фильтром");
}
}
// Затем вызвать метод add() объекта
this.set.add() this.set.add.apply(this.set, arguments);
return this;
},
// Остальные методы просто вызывают соответствующие
Интервал:
Закладка: