Выбор реализации
Внутреннее строение (реализация) классов и объектов разрабатывается только после завершения проектирования их внешнего поведения. При этом необходимо принять два проектных решения: выбрать способ представления класса или объекта и способ размещения их в модуле.
Представление. Представление классов и объектов почти всегда должно быть инкапсулировано (скрыто). Это позволяет вносить изменения (например, перераспределение памяти и временных ресурсов) без нарушения функциональных связей с другими классами и объектами. Как мудро отметил Вирт: "выбор способа представления является нелегкой задачей и не определяется одними лишь техническими средствами. Он всегда должен рассматриваться с точки зрения операций над данными" [60]. Рассмотрим, например, класс, соответствующий расписаниям полетов самолетов. Как его нужно оптимизировать - по эффективности поиска или по скорости добавления/удаления рейса? Поскольку невозможно реализовать и то, и другое одновременно, нужно сделать выбор, исходя из целей системы. Иногда такой выбор сделать непросто, и тогда создается семейство классов с одинаковым интерфейсом, но с принципиально разной реализацией для обеспечения вариативности поведения.
Одним из наиболее трудных решений является выбор между вычислением элементов состояния объекта и хранением их в виде полей данных. Рассмотрим, например, класс Cone (конус) с соответствующим ему методом volume (объем). Этот метод возвращает значение объема объекта. В структуре конуса в виде отдельных полей хранятся данные о его высоте и радиусе основания. Следует ли еще создать поле данных для объема или следует вычислять его по мере необходимости внутри метода volume [60]? Если мы хотим получать значение объема максимально быстро, нужно создавать соответствующее поле данных. Если важнее экономия памяти, лучше вычислить это значение. Оптимальный способ представления объекта всегда определяется характером решаемой задачи. В любом случае этот выбор не должен влиять на интерфейс класса.
Модульная структура. Аналогичные вопросы возникают при распределении деклараций классов и объектов по модулям.
В языке Smalltalk эта проблема отсутствует, здесь модульный механизм не реализован. В языках Object Pascal, C++, CLOS и Ada существует понятие модуля как отдельной языковой конструкции. Решение о месте декларирования классов и объектов в этих языках является компромиссом между требованиями видимости и скрытия информации. В общем случае модули должны быть функционально связными внутри и слабо связанными друг с другом. При этом следует учитывать ряд нетехнических факторов, таких, как повторное использование, безопасность, документирование. Проектирование модулей - не более простой процесс, чем проектирование классов и объектов. О скрытии информации Парнас, Клеменс и Вейс говорят следующее: "Применение этого принципа не всегда очевидно. Принцип нацелен на минимизацию стоимости программных средств (в целом за время эксплуатации), для чего от проектировщика требуется способность оценивать вероятность изменений. Такие оценки основываются на практическом опыте и знаниях предметной области, включая понимание технологии программирования и аппаратных особенностей" [61].
Выводы
Объект характеризуется состоянием, поведением и идентичностью.
Структура и поведение одинаковых объектов описывается в общем для них классе.
Состояние объекта определяет его статические и динамические свойства.
Поведение объекта характеризуется изменением его состояния в процессе взаимодействия (посредством передачи сообщений) с другими объектами.
Идентичность объекта - это его отличия от всех других объектов.
Иерархия объектов может строиться на принципах связи или агрегации.
Множество объектов с одинаковой структурой и поведением является классом.
Шесть типов иерархий классов включают: ассоциирование, наследование, агрегация, использование, инстанцирование и метаклассирование.
Классы и объекты, образующие словарь предметной области, называются ключевыми абстракциями.
Структура, объединяющая множество объектов и обеспечивающая их совместное целенаправленное функционирование, называется механизмом.
Качество абстракций измеряется их зацеплением, связностью, достаточностью, полнотой и примитивностью.
Дополнительная литература
МакЛеннан (MacLennan) [G 1982] обсуждал различие между значениями и объектами. Работа Мейера (Meyer) [F 1987] предлагает контрактный подход к программированию.
По поводу иерархии классов было написано много, особое внимание уделялось наследованию и полиморфизму. Работы Альбано (Albano) [G 1983], Аллена (Allen) [A 1982], Брахмана (Brachman) [J 1983], Хайлперна и Нгуена (Hailpern and Nguyen) [G 1987], и Вегнера и Здоника (Wegner and Zdonik) [J 1988] создали блестящее теоретическое обоснование всех основных вопросов и концепций. Кук и Палсберг (Cook and Palsberg) [I 1989] и Турецкий (Touretzky) [G 1986] дали формальное истолкование семантики наследования. Вирт (Wirth) [G 1987] предложил сходные решения для обобщенных структурных типов в Oberon. Ингалс (Ingalls) [G 1986] дал полезное обсуждение вопроса множественного полиморфизма. Грогоно (Grogono) [G 1989] изучает взаимодействие полиморфизма и проверки типов, а Пондер и Бач (Ponder and Buch) [G 1992] предупреждают об опасностях безграничного полиморфизма. Практические рекомендации по эффективному использованию наследования предложили Мейер (Meyer) [G 1988] и Халберд и О'Брайан (Halberd and O'Brien) [G 1988]. ЛаЛонд и Пух (LaLonde and Pugh) [I 1985] изучали задачи обучения эффективному использованию специализации и обобщения.
Природа ролей и обязанностей абстракции подробна рассмотрена в работе Рубина и Голдберга (Rubin and Goldberg) [В 1992], а также Вирфс-Брока, Вилкерсона и Винера (Wirfs-Brock, Wilkerson and Wiener) [F 1990]. Качество классов рассматривал также Коад (Coad) [F 1991].
Мейер (Meyer) [G 1986] изучал связи между обобщенными функциями и наследованием применительно к языку Eiffel. Страуструп (Stroustrup) [G 1988] предложил механизм параметризованных типов в C++. Протокол метаобъектов в CLOS описали в деталях Кишалец, Ривьерес и Бобров (Kiczales, Rivieres, and Bobrow) [G 1991].
Альтернативу иерархии, основанной на классах, предоставляет делегирование, использующее только экземпляры. Этот подход детально рассмотрел Стейн (Stein) [G 1987].