Опубликован: 23.10.2005 | Уровень: специалист | Доступ: свободно
Лекция 5:

Принципы проектирования класса

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

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

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

В работах Парнаса ([Parnas 1972], [Parnas 1972a]) вводится много похожих идей по проектированию интерфейса.

Различение операндов и опций и результирующий принцип взяты из работы [M1982a].

Понятие активной структуры данных поддерживается в некоторых языках программирования управляющими абстракциями, называемыми итераторами. Итератор представляет механизм, определенный совместно со структурой данных, описывающий, как применять некую операцию к каждому элементу структуры. Например, итератор, ассоциированный со списком, описывает циклический механизм прохода по списку, применяя данную операцию к каждому элементу списка, итератор дерева задает некую стратегию обхода дерева. В работах [Liskov 1981], [Liskov 1986] содержится подробное обсуждение концепции итераторов, доступных в языке программирования CLU. В объектной технологии итераторы могут быть определены в классах, не встраивая их в виде конструкций языка программирования [M 1994a].

Пример адаптивной реализации комплексных чисел взят из [M1979], где он описан на Simula.

Грамотное программирование (Literate programming) [Knuth 1984] утверждает, как и данная лекция, что программы должны содержать свою документацию. Его концепции, однако, существенно отличаются от концепций объектной технологии. Одно из упражнений предлагает сравнить два подхода.

Статьи Джеймса Мак-Кима и Ричарда Билака [Bielak 1993], [McKim 1992a] дают полезные советы по проектированию интерфейса, основанные на понятии Проектирования по Контракту.

Упражнения

У5.1 Функция с побочным эффектом

Пример управления памятью на уровне компонентов (см. лекцию 9 курса "Основы объектно-ориентированного программирования") для связных списков имеет функцию fresh, вызывающую процедуру remove для стеков, следовательно, имеющую побочный эффект. Обсудите, является ли это приемлемым.

У5.2 Операнды и опции

Исследуйте класс или доступную библиотеку и определите, какие аргументы подпрограмм являются операндами и какие - опциями.

У5.3 Возможные аргументы

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

У5.4 Число элементов как функция

Адаптируйте определение класса LINKED_LIST [G] так, чтобы count стал функцией, а не атрибутом, оставив неизменным интерфейс класса.

У5.5 Поиск в связных списках

Напишите процедуру search (x: G) для класса LINKED_LIST, разыскивающую следующее вхождение x.

У5.6 Теоремы в инварианте

Докажите истинность трех утверждений из первой части инварианта класса LINKED_LIST, отмеченных как теоремы (см. "Принципы проектирования класса" ).

У5.7 Двунаправленные списки

Напишите класс, задающий двунаправленные списки с интерфейсом LINKED_LIST, но более эффективной реализацией таких операций, как back, go и finish.

У5.8 Альтернативный проект связного списка

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

У5.9 Вставка в связный список

Глядя на remove, напишите процедуры put_left и put_right для вставки элементов слева и справа от позиции курсора.

У5.10 Циклические списки

Объясните, почему класс LINKED_LIST не может использоваться для циклических списков. (Подсказка: покажите, что утверждения будут нарушаться.) Напишите класс CIRCULAR_LINKED, реализующий циклические списки.

У5.11 Функции ввода, свободные от побочных эффектов

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

У5.12 Документация

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

У5.13 Самодокументированное ПО

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

Сергей Дмитриев
Сергей Дмитриев
Россия, Москва