Принципы проектирования класса
Ключевые концепции
- Класс должен быть известен своим интерфейсом, специфицируя предлагаемые службы независимо от их реализации.
- Разработчики класса должны стараться предоставить простые согласованные интерфейсы.
- Одна из ключевых проблем при проектировании класса состоит в правильном разделении экспортируемых и закрытых компонентов.
- Проектирование повторно используемых модулей не является первоочередной задачей - интерфейс должен стабилизироваться после некоторого периода использования. Если этого не происходит, то это свидетельствует об изъяне в проекте. Механизм устаревших классов и компонентов делает возможным сгладить переход к лучшей версии проекта.
- Зачастую полезно рассматривать некоторые структуры данных как активные машины с внутренним состоянием, запоминаемым между вызовами компонентов.
- Правильное использование утверждений - предусловий, постусловий, инвариантов - является основой документирования интерфейсов.
- Для разбора особых случаев лучше применять стандартные управляющие структуры, применяя либо априорную, либо апостериорную схему. Механизм дисциплинированных исключений остается необходимым в тех случаях, когда выполнение должно быть прервано из-за потенциальных угроз, связанных с некорректным выполнением операции.
Библиографические замечания
В работах Парнаса ([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 Самодокументированное ПО
Подход к самодокументированию, защищаемый в этой книге, предполагает лаконичность и не поддерживает долгих объяснений проектных решений. Стиль "грамотного программирования" Кнута комбинирует методы программирования и текстовой обработки так, чтобы полная документация проекта и его история содержались в одном документе. Метод основан на классической парадигме разработки проекта сверху вниз. Отталкиваясь от работы Кнута, обсудите, как его метод может быть транспонирован при объектном подходе.