Пример 9.13. Вызов из подкласса конструктора и метода базового суперкласса
<b>/*</b>
<b> * NonNullSet - подкласс класса Set, который не может содержать элементы</b>
<b> * со значениями null и undefined.</b>
<b>*/</b>
<b>function NonNullSet() {</b>
<b> // Простое обращение к суперклассу.</b>
<b> // Вызвать конструктор суперкласса как обычную функцию для инициализации</b>
<b> // объекта, который был создан вызовом этого конструктора.</b>
<b> Set.apply(this, arguments);</b>
<b>}</b>
<b>// Сделать класс NonNullSet подклассом класса Set:</b>
<b>NonNullSet.prototype = inherit(Set.prototype);</b>
<b>NonNullSet.prototype.constructor = NonNullSet;</b>
<b>// Чтобы исключить возможность добавления значений null и undefined,</b>
<b>// достаточно переопределить метод add()</b>
<b>NonNullSet.prototype.add = function() {</b>
<b> // Проверить наличие аргументов со значениями null и undefined</b>
<b> for(var і = 0; і < arguments.length; i++)</b>
<b> if (arguments[i] == null)</b>
<b> throw new Еrror("Нельзя добавить null или undefined в NonNullSet”);</b>
<b> // Вызвать метод базового суперкласса, чтобы фактически добавить элементы</b>
<b> return Set.prototype.add.apply(this, arguments);</b>
<b>};</b>
Теперь обобщим понятие "множество без пустых элементов" до понятия "фильтрованное множество": множество, элементы которого должны пропускаться через функцию-фильтр перед добавлением. Определим фабричную функцию (подобную функции
<b>enumeration</b>
) из примера 9.7), которая будет получать функцию-фильтр и возвращать новый подкласс класса
<b>Set</b>
. В действительности можно пойти еще дальше по пути обобщений и определить фабричную функцию, принимающую два аргумента: наследуемый класс и функцию-фильтр, применяемую к методу
<b>add().</b>
Новой фабричной функции можно было бы дать имя
<b>filteredSetSubclass()</b>
и использовать ее, как показано ниже:
<b>// Определить класс множеств, которые могут хранить только строки</b>
<b>var StringSet = filteredSetSubclass(Set,</b>
<b> function(x) {return typeof x===*'string";});</b>
<b>// Определить класс множеств, которые не могут содержать значения null,</b>
<b>// undefined и функции</b>
<b>var MySet = filteredSetSubclass(NonNullSet,</b>
<b> function(x) {return typeof x !== "function";});</b>
Реализация этой фабричной функции приводится в примере 9.14. Обратите внимание, что эта функция вызывает метод и конструктор базового класса подобно тому, как это реализовано в классе
<b>NonNullSet</b>
.
Пример 9.14. Вызов конструктора и метода базового класса
<b>/*</b>
<b>* Эта функция возвращает подкласс указанного класса Set и переопределяет</b>
<b>* метод add() этого класса, применяя указанный фильтр.</b>
<b>*/</b>
<b>function filteredSetSubclass(superclass, filter) {</b>
<b> var constructor = function() { // Конструктор подкласса</b>
<b> superclass.apply(this, arguments); // Вызов конструктора базового класса</b>
<b> };</b>
<b> var proto = constructor.prototype = inherit(superclass.prototype);</b>
<b> proto.constructor = constructor; proto.add = function() {</b>
<b> // Примерить фильтр ко всем аргументам перед добавлением</b>
<b> for(var і = 0; і < arguments.length; i++) {</b>
<b> var v = arguments[i];</b>
<b> if (!filter(v)) throw("значение + v + отвергнуто фильтром");</b>
<b> }</b>
<b> // Вызвать реализацию метода add из базового класса</b>
<b> superclass.prototype.add.apply(this, arguments);</b>
<b> };</b>
<b> return constructor;</b>
<b>}</b>
В примере 9.14 есть один интересный момент, который хотелось бы отметить. Он заключается в том, что, обертывая операцию создания подкласса функцией, мы получаем возможность использовать аргумент
<b>superclass</b>
в вызовах конструктора и метода базового класса и избежать указания фактического имени суперкласса. Это означает, что в случае изменения имени суперкласса достаточно будет изменить имя в одном месте, а не отыскивать все его упоминания в программном коде. Такого способа стоит придерживаться даже в случаях, не связанных с определением фабричных функций. Например, с помощью функции-обертки можно было бы переписать определение класса
<b>NonNullSet</b>
и метода
<b>Function.prototype.extend()</b>
(пример 9.11), как показано ниже: