JavaScript. Подробное руководство, 6-е издание, стр. 128

Решение этой проблемы в классических объектно-ориентированных языках, а также в языке

<b>JavaScript</b>
заключается в том, чтобы отделить интерфейс от реализации. Представьте, что мы определили класс
<b>AbstractSet</b>
, реализующий вспомогательные методы, такие как
<b>toString(),</b>
в котором отсутствуют реализации базовых методов, таких как
<b>foreach().</b>
Тогда все наши реализации множеств -
<b>Set, SingletonSet</b>
и
<b>FilteredSet</b>
- могли бы наследовать класс
<b>AbstractSet</b>
. При этом классы
<b>FilteredSet</b>
и
<b>SingletonSet</b>
больше не наследовали бы ненужные им реализации.

Пример 9.16 развивает этот подход еще дальше и определяет иерархию абстрактных классов множеств. Класс

<b>AbstractSet</b>
определяет только один абстрактный метод,
<b>contains().</b>
Любой класс, который претендует на роль множества, должен будет определить хотя бы один этот метод. Далее в примере определяется класс
<b>AbstractEnumerableSet</b>
, наследующий класс
<b>AbstractSet</b>
. Этот класс определяет абстрактные методы
<b>size()</b>
and
<b>foreach()</b>
и реализует конкретные вспомогательные методы (
<b>toString(), toArray(), equals()</b>
и т.д.).
<b>AbstractEnumerableSet</b>
не определяет методы
<b>add()</b>
или
<b>remove()</b>
и представляет класс множеств, доступных только для чтения. Класс
<b>SingletonSet </b>
может быть реализован как конкретный подкласс. Наконец, в примере определяется класс
<b>AbstractWritableSet</b>
, наследующий
<b>AbstractEnumerableSet</b>
. Этот последний абстрактный класс определяет абстрактные методы
<b>add()</b>
и
<b>remove()</b>
и реализует конкретные методы, такие как
<b>union()</b>
и
<b>intersection(),</b>
использующие их. Класс
<b>AbstractWritableSet</b>
отлично подходит на роль суперкласса для наших классов
<b>Set</b>
и
<b>FilteredSet</b>
. Однако они не были добавлены в пример, а вместо них была включена новая конкретная реализация с именем
<b>ArraySet</b>
.

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

<b>Function.prototype.extend().</b>

Пример 9.16. Иерархия абстрактных и конкретных классов множеств

<b>// Вспомогательная функция, которая может использоваться для определения</b>

<b>// любого абстрактного метода</b>

<b>function abstractmethod() { throw new Error(&quot;абстрактный метод&quot;); }</b>

<b>/*</b>

<b> * Класс AbstractSet определяет единственный абстрактный метод, contains().</b>

<b>*/</b>

<b>function AbstractSet() {</b>

<b>  throw new Error(&quot;Нельзя создать экземпляр абстрактного класса&quot;);</b>

<b>}</b>

<b>AbstractSet.prototype.contains = abstractmethod;</b>

<b>/*</b>

<b>  * NotSet - конкретный подкласс класса AbstractSet.</b>

<b>  * Элементами этого множества являются все значения, которые не являются</b>

<b>  * элементами некоторого другого множества. Поскольку это множество</b>

<b>  * определяется в терминах другого множества, оно не доступно для записи,</b>

<b>  * а так как оно имеет бесконечное число элементов, оно недоступно для перечисления.</b>

<b>  * Все, что позволяет этот класс, - это проверить принадлежность к множеству.</b>

<b>  * Обратите внимание, что для определения этого подкласса используется метод</b>

<b>  * Function.prototype.extendO, объявленный выше.</b>

<b>*/</b>

<b>var NotSet = AbstractSet.extend(</b>

<b>  function NotSet(set) { this.set = set; },</b>

<b>  {</b>

<b>    contains: function(x) { return !this.set.contains(x); },</b>

<b>    toString: function(x) { return &quot;~&quot; + this.set.toString(); },</b>

<b>    equals: function(that) {</b>

<b>      return that instanceof NotSet &amp;&amp; this.set.equals(that.set);</b>

<b>    }</b>

<b>  }</b>

<b>);</b>

<b>/*</b>

<b> * AbstractEnumerableSet - абстрактный подкласс класса AbstractSet.</b>

<b> * Определяет абстрактные методы size() и foreach() и реализует конкретные</b>

<b> * методы isEmptyO. toArrayO, to[Locale]String() и equals().</b>

<b> * Подклассы, реализующие методы contains(), size() и foreach(),</b>

<b> * получают эти пять конкретных методов даром.</b>