Опубликован: 23.10.2005 | Доступ: свободный | Студентов: 4087 / 201 | Оценка: 4.44 / 4.19 | Длительность: 33:04:00
Специальности: Программист
Лекция 6:

Используйте наследование правильно

Итоговый обзор: используйте наследование правильно

Наследование никогда не перестанет удивлять нас своей мощью и универсальностью. В этой лекции мы пытались как можно лучше описать, что реально означает наследование и как можно его использовать для получения всех преимуществ. Перечислю несколько центральных выводов.

Прежде всего не следует бояться разнообразия способов использования наследования. Запрет множественного или льготного наследования не достигает никакой другой цели, кроме нанесения вреда самому себе. Механизмы должны помогать нам, используйте их правильно, но используйте их!

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

Конечно, все ПО проектируется для клиентов, клиентские потребности управляют процессом. Множество классов хорошо тогда, когда оно предоставляет замечательные службы клиентам: интерфейсы и ассоциированные с ними реализации, являющиеся полными, свободными от неприятных сюрпризов (таких как неожиданные потери производительности), просты в использовании, легки в обучении, запоминании, допускают расширяемость. Для достижения этих целей проектировщик свободен в использовании наследования и других ОО-методов любым способом, который ему нравится. Победителей не судят.

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

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

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

Ключевые концепции

  • Каждое использование наследования должно отражать некоторую форму отношения "is a" между двумя категориями объектов, либо из внешней моделируемой области, либо из мира самого ПО.
  • Не используйте наследование для моделирования отношения "has"; это область клиентских отношений. (Помните о CAR_OWNER.)
  • Когда наследование применимо, часто потенциально применим и клиент. Если соответствующая точка зрения может измениться, используйте клиентское отношение. Если предвидится полиморфное использование, используйте наследование.
  • Не вводите промежуточных узлов наследования, если только они не задают хорошо определенные абстракции со своими специфическими компонентами.
  • Данная классификация наследования основана на двенадцати видах, разделенных на три общие категории: наследование модели (описывающее отношения, существующие в моделируемой области), программное наследование (описывающее отношения, существующие в самом ПО) и наследование вариаций (для адаптации класса либо в модели, либо в ПО).
  • Мощь наследования происходит из комбинации специализации типа и механизма расширения модуля. Было бы немудро использовать различные механизмы языка.
  • Наследование реализации и льготное наследование требуют особой тщательности, но представляют мощную технику, которую следует использовать на стороне поставщика.
  • Наследование видов - это тонкая техника, включающая дублирующее наследование, позволяющая классифицировать типы объектов по нескольким конкурирующим критериям. Оно полезно при разработке профессиональных библиотек. Во многих случаях простая техника описателей предпочтительнее.
  • Хотя и нет теоретического идеала, фактический процесс проектирования иерархии наследования идет в двух направлениях - от абстрактного к конкретному и наоборот.
  • Наследование является главным образом техникой поставщика.

Библиографические замечания

Основным учебником по таксономии наследования является [Girod 1991]. Книга по ОО-методологии [Page-Jones 1995] - одна из немногих, где даются полезные методологические советы, включая ценные рекомендации, когда следует и когда не следует использовать наследование. Еще один полезный учебник Джона Мак-Грегора [McGregor 1992] исследует технику, названную в этой лекции наследованием видов.

В [Breu 1995] также содержатся полезные концепции, основанные на более ограничительном подходе к наследованию, чем в рассматриваемой лекции.

Техника, подобная "описателям" этой лекции, рассмотрена в [Gil 1994].

При подготовке этой статьи использовались комментарии нескольких биологов, ведущих доступные в Интернете Web-ресурсы по таксономии живых существ, в частности "Древо жизни" (http://www.phylogeny.arizona.edu/tree/life.html), учтиво предоставленные профессорами Дэвидом Мэдисоном, а для птиц Майклом Лореном. Помощь профессора Эдвина Эверхама из университета Рэдфорда была весьма полезной.

Руководства по теории классификации или систематике приведены в следующем разделе.

Приложение: история таксономии

Данное приложение является дополнительным материалом и не используется в остальной части курса. Изучение усилий по таксономии в других дисциплинах позволяет извлечь полезные уроки разработчикам ПО. Я надеюсь, что оно послужит толчком к дальнейшим исследованиям в этой интересной области, возможно, станет темой магистерской или диссертации кандидата наук.

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

Вероятно, подобно эволюционным таксономистам, хотелось бы взять понемногу от тех и от других.

Упражнения

У6.1 Стек, основанный на массиве

Напишите полностью класс STACK и его потомка ARRAYED_STACK, набросок которого дан в этой лекции, используя технику "брака по расчету".

У6.2 Метатаксономия

Представьте себе, что введенная классификация форм наследования представляла бы собой иерархию наследования. Какие виды наследования были бы включены?

У6.3 Стеки Ханоя

(Это упражнение пришло из примера Филиппа Дрикса.)

Рассмотрим отложенный класс STACK с процедурой put для вталкивания элемента на вершину с предусловием, включающим булеву функцию full (которая также может быть названа extendible ; когда вы ознакомитесь с упражнением, то заметите, что выбор имени может влиять на возможные решения).

Задача о Ханойских башнях, известная по многим учебникам как пример рекурсивной процедуры, идет от работы Эдварда Лукаса, Париж, 1883 г.

Рассмотрим теперь известную задачу о Ханойских башнях, где нужно перенести пирамиду (башню), составленную из отдельных дисков разного размера, с одного стержня на другой, используя третий, соблюдая правило: диск может быть переложен только на диск большего размера.

Можно ли определить класс HANOI_STACK, представляющий такие пирамиды, как наследника класса STACK? Если да, каким должен быть этот класс? Если нет, может ли HANOI_STACK как-то использовать STACK? Напишите класс полностью для различных возможных решений, обсудите все "за и против" каждого решения. Установите, какое из них предпочтительнее и объясните ваш выбор.

У6.4 Являются ли многоугольники списками?

Реализация нашего примера наследования класса POLYGON использовала атрибут связного списка vertices для представления числа сторон многоугольника. Следует ли вместо этого наследовать POLYGON от LINKED_LIST [POINT]?

У6.5 Наследование функциональной вариации

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

У6.6 Примеры классификации

Для каждого из следующих случаев укажите, к какому виду наследования он относится:

  • SEGMENT от OPEN_FIGURE ;
  • COMPARABLE (объекты, поставляемые с отношением полного порядка), наследуемые от PART_COMPARABLE (объекты с отношением частичного порядка);
  • некоторые классы EXCEPTIONS.
У6.7 Кому принадлежат итераторы?

Разумно ли компоненты итератора ( while_do и ему подобные) включать в классы, описывающие структуры данных, которые они итерируют, такие как LIST? Рассмотрите следующие аргументы:

  • простоту применения в процессе итерирования подпрограмм action и test, выбираемых приложением;
  • расширяемость: возможность добавления новых схем итерирования;
  • общность: выполнение ОО-принципов, в частности той идеи, что операции не существуют сами по себе, но связаны с некоторой абстракцией данных.
У6.8 Наследование типа и модуля

Предположим, мы разрабатываем язык с двумя типами наследования: расширением модуля и подтипами. К какому из этих типов следует отнести категории наследования, идентифицируемые в этой лекции?

У6.9 Наследование и полиморфизм

Из рассмотренных видов наследования этой лекции между родителем A и наследником B, для каких на практике характерно использование полиморфного присоединения, другими словами присваивание x := y или соответствующая передача аргументов x типа A и y типа B?