Основы объектно-ориентированного проектирования

         

Как это делается без наследования


Давайте проверим, как можно выполнить эту работу без использования наследования. Для нашего примера это было уже сделано в классе STACK2 из предыдущих лекций. Он имеет атрибут representation типа ARRAY [G] и процедуры стека, реализованные в следующем виде (утверждения опущены):

put (x: G) is -- Добавляет x на вершину require ... do count := count + 1 representation.put (count, x) ensure ... end

Каждая манипуляция с представлением требует вызова компонента ARRAY с representation как цели. Платой являются потери производительности: минимальные по памяти (атрибут representation), более серьезные по времени (связанные с representation, накладные расходы, добавляемые при вызове каждой операции).

Предположим, что проблемы эффективности можно игнорировать. Остается еще одна утомительная необходимость выписывать перед каждой операцией префикс "representation". Это придется делать для всех классов, реализующих различные структуры данных - стеки, списки, очереди, все, что реализуется массивами.

ОО-разработчики ненавидят утомительные, повторяющиеся операции. "Встроенное повторение" - вот наш девиз. Если некий образец повторно встречается в множестве классов, естественной и здоровой реакцией является попытка понять общую абстракцию и встроить ее в класс. Абстракция здесь нечто подобное "структуре данных, имеющей доступ к массиву и его операциям".

indexing description: "Объекты, имеющие доступ к массиву и его операциям" class ARRAYED [G] feature -- Access item (i: INTEGER): G is -- Элемент представления с индексом i require ... do Result := representation.item (i) ensure ... end feature -- Element change put (x: G; i: INTEGER) is -- Замена на x элемента с индексом i require ... do representation.put (x, i) ensure ... end feature {NONE} -- Implementation representation: ARRAY [G] end

Компоненты item и put экспортированы. Так как ARRAYED описывает только внутренние свойства структуры данных, нет реальной необходимости в экспортируемых компонентах. Так что тот, кто не согласен с самой идей разрешения потомкам скрывать некоторые из экспортируемых компонентов, может предпочесть сделать закрытыми все компоненты ARRAYED. По умолчанию они тогда будут скрытыми и у потомков.

При таком определении класса не вызывает споров, что классы, такие как ARRAYED_STACK или ARRAYED_LIST, становятся наследниками ARRAYED: они действительно описывают структуры на массивах. Эти классы могут теперь использовать item вместо representation.item и так далее; мы избавились от утомительного повторения.

Но минуточку! Если наследовать от ARRAYED представляется правильным, почему же нельзя непосредственно наследовать от ARRAY? Никакой выгоды от введения еще одного слоя, надстроенного над ARRAY. Введение ARRAYED позволило убедить себя, что наследование реализации не используется, но по соображениям практики мы пошли на это, сделав систему более сложной и менее эффективной.

На самом деле нет никаких причин для введения класса ARRAYED. Прямое наследование реализации от классов, подобных ARRAY, проще и легитимнее.



Содержание раздела