Решение этой проблемы в классических объектно-ориентированных языках, а также в языке JavaScript
заключается в том, чтобы отделить интерфейс от реализации. Представьте, что мы определили класс AbstractSet
, реализующий вспомогательные методы, такие как toString(),
в котором отсутствуют реализации базовых методов, таких как foreach().
Тогда все наши реализации множеств - Set, SingletonSet
и FilteredSet
- могли бы наследовать класс AbstractSet
. При этом классы FilteredSet
и SingletonSet
больше не наследовали бы ненужные им реализации.
Пример 9.16 развивает этот подход еще дальше и определяет иерархию абстрактных классов множеств. Класс AbstractSet
определяет только один абстрактный метод, contains().
Любой класс, который претендует на роль множества, должен будет определить хотя бы один этот метод. Далее в примере определяется класс AbstractEnumerableSet
, наследующий класс AbstractSet
. Этот класс определяет абстрактные методы size()
and foreach()
и реализует конкретные вспомогательные методы ( toString(), toArray(), equals()
и т.д.). AbstractEnumerableSet
не определяет методы add()
или remove()
и представляет класс множеств, доступных только для чтения. Класс SingletonSet
может быть реализован как конкретный подкласс. Наконец, в примере определяется класс AbstractWritableSet
, наследующий AbstractEnumerableSet
. Этот последний абстрактный класс определяет абстрактные методы add()
и remove()
и реализует конкретные методы, такие как union()
и intersection(),
использующие их. Класс AbstractWritableSet
отлично подходит на роль суперкласса для наших классов Set
и FilteredSet
. Однако они не были добавлены в пример, а вместо них была включена новая конкретная реализация с именем ArraySet
.
Пример 9.16 довольно объемен, но он заслуживает детального изучения. Обратите внимание, что для простоты создания подклассов в нем используется функция
Function.prototype.extend().
Пример 9.16. Иерархия абстрактных и конкретных классов множеств
// Вспомогательная функция, которая может использоваться для определения
// любого абстрактного метода
function abstractmethod() { throw new Error("абстрактный метод"); }
/*
* Класс AbstractSet определяет единственный абстрактный метод, contains().
*/
function AbstractSet() {
throw new Error("Нельзя создать экземпляр абстрактного класса");
}
AbstractSet.prototype.contains = abstractmethod;
/*
* NotSet - конкретный подкласс класса AbstractSet.
* Элементами этого множества являются все значения, которые не являются
* элементами некоторого другого множества. Поскольку это множество
* определяется в терминах другого множества, оно не доступно для записи,
* а так как оно имеет бесконечное число элементов, оно недоступно для перечисления.
* Все, что позволяет этот класс, - это проверить принадлежность к множеству.
* Обратите внимание, что для определения этого подкласса используется метод
* Function.prototype.extendO, объявленный выше.
*/
var NotSet = AbstractSet.extend(
function NotSet(set) { this.set = set; },
{
contains: function(x) { return !this.set.contains(x); },
toString: function(x) { return "~" + this.set.toString(); },
equals: function(that) {
return that instanceof NotSet && this.set.equals(that.set);
}
}
);
/*
* AbstractEnumerableSet - абстрактный подкласс класса AbstractSet.
* Определяет абстрактные методы size() и foreach() и реализует конкретные
* методы isEmptyO. toArrayO, to[Locale]String() и equals().
* Подклассы, реализующие методы contains(), size() и foreach(),
* получают эти пять конкретных методов даром.
*/
var AbstractEnumerableSet = AbstractSet.extend(
function() {
throw new Error("Нельзя создать экземпляр абстрактного класса");
Читать дальше
Конец ознакомительного отрывка
Купить книгу