Другие примеры обобщения (наследования)



Другие примеры обобщения (наследования)


Необходимо отметить, что, как показывает опыт практического проектирования систем, следует избегать обширных многоуровневых классификаций, так как поведение подклассов низших уровней многоуровневой классификации бывает трудно понять: большая часть (а нередко и все) атрибутов и операций таких классов определена в их суперклассах различных уровней. Если количество уровней классификации стало непомерно большим, нужно слегка изменить структурирование системы. Чтобы понять, какое число уровней является непомерно большим, можно руководствоваться следующими оценками: два-три уровня наследования, как правило, приемлемы всегда (мне известна одна фирма, разрабатывающая программные системы, в которой издан стандарт фирмы, запрещающий более чем трехуровневые классификации в программах); десятиуровневая классификация почти всегда неприемлема; пять-шесть уровней, как правило, достаточно для программистов и не слишком обременяет администрацию.

Обобщение и наследование широко применяются не только при анализе требований к программным системам и их предварительном проектировании, но и при их реализации.

Иногда в подклассе бывает необходимо переопределить операцию, определенную в одном из его суперклассов. Для этого операция, которая может быть получена из суперкласса в результате наследования, определяется и в подклассе; это ее повторное определение "заслоняет" ее определение в суперклассе, так что в подклассе применяется не унаследованная, а переопределенная в нем операция. Напомним, что каждая операция определяется своей сигнатурой; следовательно, сигнатура переопределения операции должна совпадать с сигнатурой операции из суперкласса, которая переопределяется данной операцией. Так, в примере, изображенном на рисунке 2.17, в классе круг переопределяется операция вращение его суперкласса фигура (при повороте круга его изображение не меняется, что позволяет сделать операцию вращение в классе круг пустой).

Переопределение может преследовать одну из следующих целей:

  • расширение: новая операция расширяет унаследованную операцию, учитывая влияние атрибутов подкласса;
  • ограничение: новая операция ограничивается выполнением лишь части действий унаследованной операции, используя специфику объектов подкласса;
  • оптимизация: использование специфики объектов подкласса позволяет упростить и ускорить соответствующий метод (например, переопределяя операцию max класса IntegerSet в его подклассе SortedIntegerSet, мы можем резко упростить ее, используя специфические свойства упорядоченных множеств);
  • удобство.

Целесообразно придерживаться следующих семантических правил наследования:

  • все операции-запросы (операции, которые используют значения атрибутов, но не изменяют их) должны наследоваться всеми подклассами;
  • все операции, изменяющие значения атрибутов, должны наследоваться во всех их расширениях;
  • все операции, изменяющие значения ограниченных атрибутов, или атрибутов, определяющих зависимости, должны блокироваться во всех их расширениях (например, операция размер_по_оси_x естественна для класса эллипс, но должна быть заблокирована (заменена пустой операцией) в его подклассе круг);
  • операции не следует переопределять коренным образом; все методы, реализующие одну и ту же операцию, должны осуществлять сходное преобразование атрибутов;
  • унаследованные операции можно уточнять, добавляя дополнительные действия.

Следуя этим правилам, которые, к сожалению, редко поддерживаются объектно-ориентированными языками программирования, можно сделать разрабатываемую программу более понятной, легче модифицируемой, менее подверженной влиянию различных ошибок и недосмотров.



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