4.2. Идентификация классов и объектов
4.2. Идентификация классов и объектов
Классический и современный подходы
Со времен Платона проблема классификации занимала умы бесчисленных философов, лингвистов, когнитивистов, математиков. Поэтому было бы разумно изучить накопленный опыт и применить его в объектно-ориентированном проектировании. Исторически известны только три подхода:
• классическая категоризация;
• концептуальная кластеризация;
• теория прототипов [17].
Классическая категоризация. В классическом подходе "все вещи, обладающие данным свойством или совокупностью свойств, формируют некоторую категорию. Причем наличие этих свойств является необходимым и достаточным условием, определяющим категорию" [18]. Например, холостые люди - это категория: каждый человек или холост, или женат, и этот признак достаточен для решения вопроса, к какой категории принадлежит тот или иной индивидуум. С другой стороны, высокие люди не определяют категории, если, конечно, мы специально не уточним критерий, позволяющий четко отличать высоких людей от невысоких.
Классическая категоризация пришла к нам от Платона и Аристотеля. Последний в своей классификации растений и животных пользовался техникой рассуждений, напоминающей современную детскую игру в 20 вопросов (Это минерал, животное или растение? Это покрыто мехом или перьями? Может ли оно летать? Пахнет ли оно?) [20]. Такой подход нашел последователей, наиболее выдающимися из которых были: Фома Аквинский, Декарт, Локк. По утверждению Фомы Аквинского: "Мы можем именовать вещи согласно нашим знаниям об их природе, получаемым через познание их свойств и действий" [21].
Принципы классической категоризации отражены в современной теории развития ребенка. Пьяже утверждает, что после первого года жизни ребенок осознает существование объектов и затем начинает приобретать навыки их классификации, вначале пользуясь базовыми категориями, такими, как собаки, кошки и игрушки [22]. Позднее ребенок осознает, с одной стороны более общие (животные), а с другой стороны, более частные категории (колли, доги, овчарки) [23].
Таким образом, классический подход в качестве критерия похожести объектов использует родственность их свойств. В частности, объекты можно разбивать на непересекающиеся множества в зависимости от наличия или отсутствия некоторого признака. Мински предположил, что "лучшими являются такие наборы свойств, элементы которых мало взаимодействуют между собой. Этим объясняется всеобщая любовь к таким критериям как размер, цвет, форма и материал. Так как эти критерии не пересекаются, про какой-нибудь предмет можно утверждать, что он большой, серый, круглый и деревянный" [24]. Вообще говоря, свойства не обязательно должны быть измеряемыми, в качестве их можно использовать наблюдаемое поведение. То обстоятельство, что птицы летают, а рыбы нет, позволяет отличить орла от форели.
Проблема классификации
На рис. 4-1 показаны 10 поездов, обозначенных буквами от А до J. Каждое изображение состоит из паровоза и нескольких вагонов. Прежде чем продолжать чтение, попытайтесь за 10 минут определить несколько групп изображений, составленных по какому-то логическому признаку. Например, изображения можно разбить на три группы: в одной группе поезда имеют черные колеса, в другой группе - белые, а в третьей - и белые, и черные.
Этот пример взят из работы Степпа и Михальски о концептуальном объединении [19]. Очевидно, "правильного" разбиения на группы не существует. Наши изображения были классифицированы 93 различными способами. Наиболее распространенный способ классификаций по длине состава: были выделены три группы: составы с двумя, тремя и четырьмя вагонами. Второй по популярности вид классификации - по цвету колес поезда. Сорок из девяносто трех видов классификации были уникальными (то есть вид содержал только один экземпляр).
Экспериментируя с этим рисунком, мы убедились в правоте Степпа и Михальски. Большинство опрошенных нами предлагали один из двух наиболее популярных видов классификации (по длине состава и цвету колес поезда). Один опрошенный предложил следующее: в одной группе составы помечены буквами, нарисованными с помощью только прямых линий (A, Е, F, H и I), в другой - буквами с кривыми линиями. Вот уж, действительно, пример нетривиального мышления.
Если вы уже справились с заданием, давайте изменим условия. Представим, что круги обозначают груз с токсичными веществами, прямоугольники - лесоматериалы, все остальные знаки обозначают пассажиров. Попытайтесь теперь классифицировать изображения и заметьте, как дополнительная информация влияет на вашу точку зрения.
В наших опытах большинство опрошенных классифицировало поезда по тому, содержит состав токсичный груз или нет. Мы заключили, что новые сведения о реальной ситуации облегчают и улучшают классификацию.
Какие конкретно свойства надо принимать во внимание? Это зависит от обстановки. Например, цвет автомобиля надо зафиксировать в задаче учета продукции автомобилестроительного завода, но он не интересен программе, управляющей уличным светофором. Вот почему мы говорим, что нет абсолютного критерия классификации, одна и та же структура классов может подходить для одной задачи и не годиться для другой. Джеймс пишет: "Нельзя утверждать, что некоторая схема классификации лучше других отражает структуру и порядок вещей в природе. Природе безразличны наши попытки в ней разобраться. Некоторые классификации действительно важнее других, но только в связи с нашими интересами, а не потому, что они вернее или полнее отражают реальность" [25].
Современное западное мышление по большей части насквозь пропитано классической категоризацией, однако, как показывает пример с высокими и низкими людьми, этот подход не всегда работает. Косок отмечает, что "естественные категории не четко отграничены друг от друга. Большинство птиц летает, но не все. Стул может быть деревянным, металлическим или пластмассовым, а количество ног у него целиком зависит от прихоти конструктора. Практически невозможно перечислить определяющие свойства естественной категории, так, чтобы не было исключений" [26]. Это, действительно, коренные пороки классической категоризации, которые и попытались исправить в современных подходах. Ими мы сейчас займемся.
Рис. 4-1. Проблема классификации.
Концептуальная кластеризация. Это более современный вариант классического подхода. Он возник из попыток формального представления знаний. Степп и Михальски пишут: "При таком подходе сначала формируются концептуальные описания классов (кластеров объектов), а затем мы классифицируем сущности в соответствии с этими описаниями" [27]. Например, возьмем понятие "любовная песня". Это именно понятие, а не признак или свойство, поскольку степень любовности песни едва ли можно измерить. Но если можно утверждать, что песня скорее про любовь, чем про что-то другое, то мы помещаем ее в эту категорию.
Концептуальную кластеризацию можно связать с теорией нечетких (многозначных) множеств, в которой объект может принадлежать к нескольким категориям одновременно с разной степенью точности. Концептуальная кластеризация делает в классификации абсолютные суждения, основываясь на наилучшем согласии.
Теория прототипов. Классическая категоризация и концептуальная кластеризация - достаточно выразительные методы, вполне пригодные для проектирования сложных программных систем. Но все же есть ситуации, в которых эти методы не работают. Рассмотрим более современный метод классификации, теорию прототипов, предпосылки которой можно найти в книге по психологии восприятия Рош и ее коллег [28].
Существуют некоторые абстракции, которые не имеют ни четких свойств, ни четкого определения. Лакофф объясняет эту проблему так: "По утверждению Виттгенстейна (Wittgenstein), существуют категории (например, игры), которые не соответствуют классически образцам, так как нет признаков, свойственных всем играм... По этой причине их можно объединить так называемой семейной схожестью... Виттгенстейн утверждает, что у категории игр нет четкой границы. Категорию можно расширить и включить новые виды игр при условии, что они напоминают уже известные игры" [29]. Вот почему этот подход называется теорией прототипов: класс определяется одним объектом-прототипом, и новый объект можно отнести к классу при условии, что он наделен существенным сходством с прототипом.
Лаков и Джонсон применяют классификацию на основе прототипов к упомянутой выше проблеме стульев. Они замечают, что "мы считаем мягкий пуф, парикмахерское кресло и складной стул стульями не потому, что они удовлетворяют некоторому фиксированному набору признаков прототипа, но потому, что они имеют достаточное фамильное сходство с прототипом... Не требуется никакого общего набора свойств прототипа, которое годилось бы и для пуфика и для парикмахерского кресла, но они оба - стулья, так как каждый из них в отдельности похож на прототипный стул, пусть даже каждый по-своему. Свойства, определяемые при взаимодействии с объектом (свойства взаимодействия), являются главными при определении семейного сходства" [30].
Понятие свойств взаимодействия - центральное для теории прототипов. В концептуальной кластеризации мы группируем в соответствии с различными концепциями. В теории прототипов классификация объектов производится по степени их сходства с конкретным прототипом.
Применение классических и новых теорий. Разработчику, озабоченному постоянно меняющимися требованиями к системе и вечно сражающемуся с напряженным планом при ограниченных ресурсах, предмет нашего обсуждения может показаться далеким от реальности. В действительности, три рассмотренных подхода к классификации имеют непосредственное отношение к объектно-ориентированному проектированию.
На практике мы идентифицируем классы и объекты сначала по свойствам, важным в данной ситуации, то есть стараемся выделить и отобрать структуры и типы поведения с помощью словаря предметной области. "Потенциально возможных абстракций, как правило, очень много" [31]. Если таким путем не удалось построить удобоваримой структуры классов, мы пробуем концептуальный подход. В этом случае в центре внимания уделяется поведение объектов, когда они взаимодействуют друг с другом. Наконец, мы пробуем выделить прототипы и ассоциировать с ними объекты.
Эти три способа классификации составляют теоретическую основу объектно-ориентированного подхода к анализу, предлагающего много практических советов и правил, которые можно применить для идентификации классов и объектов при проектировании сложной программной системы.
Объектно-ориентированный анализ
Границы между стадиями анализа и проектирования размыты, но решаемые ими задачи определяются достаточно четко. В процессе анализа мы моделируем проблему, обнаруживая классы и объекты, которые составляют словарь проблемной области. При объектно-ориентированном проектировании мы изобретаем абстракции и механизмы, обеспечивающие поведение, требуемое моделью [Обозначения и процессы, описанные в этой книге, в равной степени относятся к фазам и анализа и проектирования (в традиционном понимании), как мы увидим в главе 6. Именно по этой причине мы сменили во втором издании название книги на "Объектно-ориентированный анализ и проектирование"].
Теперь мы рассмотрим несколько проверенных практикой подходов к анализу объектно-ориентированных систем.
Классические подходы. Разные ученые находят различные источники классов и объектов, согласующихся с требованиями предметной области. Мы называем эти подходы классическими, поскольку они опираются на классическую категоризацию.
Например, Шлаер и Меллор предлагают следующих кандидатов в классы и объекты [32]:
? Осязаемые предметы Автомобили, телеметрические данные, датчики давления
? Роли Мать, учитель, политик
? События Посадка, прерывание, запрос
? Взаимодействие Заем, встреча, пересечение
Что-то в этом роде предлагает Росс, исходя из перспектив моделирования баз данных [33]:
? Люди Человеческие существа, выполняющие некоторые функции
? Места Области, связанные с людьми или предметами
? Предметы Осязаемый материальный объект или группа объектов
? Организации Формально организованная совокупность людей, ресурсов, оборудования, которая имеет определенную цель и существование которой в целом не зависит от индивидуумов
? Концепции Принципы и идеи, сами по себе неосязаемые, но предназначенные для организации деятельности и/или общения, или же для наблюдения за ними
? События Нечто случающееся с чем-то в заданное время или последовательно
Коад и Иордан предложили свой список [34]:
? Структуры Отношения "целое-часть" и "общее-частное"
? Другие системы Внешние системы, с которыми взаимодействует приложение
? Устройства Устройства, с которыми взаимодействует приложение
? События Происшествия, которые должны быть запомнены
? Разыгрываемые роли Роли, которые исполняют пользователи, работающие с приложением
? Места Здания, офисы и другие места, существенные для работы приложения
? Организационные единицы Группы, к которым принадлежат пользователи
На более высоком уровне абстракции Коад вводит понятие предметной области, которая в сущности является логически связанной группой классов, относящейся к высокоуровневым функциям системы.
Анализ поведения. В то время как классические подходы концентрируют внимание на осязаемых элементах предметной области, другая школа мысли объектно-ориентированного анализа сосредотачивается на динамическом поведении как на первоисточнике объектов и классов [Шлаер и Меллор дополнили свою более раннюю работу, обратив внимание также и на поведение. В частности, они изучали жизненный цикл объекта как средство понимания границ]. Это напоминает концептуальную кластеризацию, рассмотренную выше: мы формируем классы, основываясь на группах объектов, демонстрирующих сходное поведение.
Вирфс-Брок предлагает понятие ответственности объекта, под которыми следует понимать "его знания и умения. Ответственность - это способ выразить цель объекта и его место в системе. Ответственность объекта есть совокупность всех услуг, которые он может предоставлять по всем его контрактам" [36]. То есть, мы объединяем вместе те объекты, которые имеют сходные ответственности и строим иерархию классов, в которой каждый подкласс, выполняя обязательства суперкласса, привносит свои дополнительные услуги.
Рубин и Гольдберг предлагают идентифицировать классы и объекты, анализируя функционирование системы: "Наш подход основан на изучении поведения системы. Мы сопоставляем формы поведения с частями системы и пытаемся понять, какая часть инициирует поведение и какие части в нем участвуют... Инициаторы и участники, играющие существенные роли, опознаются как объекты и делаются ответственными за эти роли" [37].
Идеи Рубина тесно связаны с предложенным в 1979 году Альбрехтом подходом с точки зрения функций. По его определению, функция "определяется как отдельное бизнес-действие конечного пользователя" [38], то есть: ввод/вывод, запрос, файл или интерфейс. Очевидно, что эта концепция происходит из области информационных систем. Однако, она может быть применена к любой автоматизированной системе. По существу, функция - это любое достоверно видимое извне и имеющее отношение к делу поведение системы.
Анализ предметной области. До сих пор мы неявно имели в виду единственное разрабатываемое нами приложение. Но иногда в поисках полезных и уже доказавших свою работоспособность идей полезно обратиться сразу ко всем приложениям в рамках данной предметной области, как, например, ведение историй болезни пациентов, торговля ценными бумагами, разработка компиляторов или системы управления ракетами. Если вы находитесь в середине разработки и застряли, анализ какой-нибудь узкой предметной области может помочь, указав вам на ключевые абстракции, оказавшиеся полезными в сходных системах. Анализ предметной области работает очень хорошо, исключая разве что лишь очень специальные ситуации, так как уникальные программные системы встречаются крайне редко.
Идею анализа предметной области впервые предложил Нейборс. Мы определим такой анализ как "попытку выделить те объекты, операции и связи, которые эксперты данной области считают наиболее важными" [39]. Мур и Байлин определяют следующие этапы в анализе области:
• "Построение скелетной модели предметной области при консультациях с экспертами в этой области.
• Изучение существующих в данной области систем и представление результатов в стандартном виде.
• Определение сходства и различий между системами при участии экспертов.
• Уточнение общей модели для приспособления к нуждам конкретной системы" [40].
Анализ области можно вести относительно аналогичных приложений (вертикально) или относительно аналогичных частей одного и того же приложения (горизонтально). Например, начиная проектировать систему учета пациентов, имеет смысл рассмотреть уже имеющиеся подобные системы, чтобы понять, какие ключевые абстракции и механизмы, использованные в них, будут вам полезны, а какие нет. Аналогично система бухгалтерского учета должна представлять различные виды отчетов. Если считать отчеты некой предметной областью, ее анализ может привести разработчика к пониманию ключевых абстракций и механизмов, которые обслуживают все виды отчетов. Полученные таким образом классы и объекты представляют собой множество ключевых абстракций и механизмов, отобранных с учетом цели исходной задачи: создания системы отчетов. Поэтому окончательный проект будет проще.
Определим теперь, кто такой эксперт? В роли эксперта часто выступает просто пользователь системы, например, инженер или диспетчер. Он не обязательно должен быть программистом, но должен быть близко знаком с исследуемой проблемой и разговаривать на языке этой проблемы.
Менеджеры проектов заинтересованы в непосредственном сотрудничестве пользователей и разработчиков системы. Но для очень сложных систем прикладной анализ является формальным процессом, для которого требуется большое число экспертов и разработчиков на длительный период времени. На практике такой формальный анализ требуется редко. Обычно для начального уяснения проблемы достаточно короткой встречи экспертов и разработчиков. Удивительно, как мало информации требуется для продуктивной работы разработчика. Однако мы считаем чрезвычайно полезными такие встречи в течение всей разработки. Анализ прикладной области лучше всего вести шаг за шагом - немного поанализировать, потом немного попроектировать и т.д.
Анализ вариантов. По отдельности классический подход, поведенческий подход и изучение предметной области, рассмотренные выше, сильно зависят от индивидуальных способностей и опыта аналитика. Для большинства реальных проектов одновременное применение всех трех подходов неприемлемо, так как процесс анализа становится недетерминированным и непредсказуемым.
Анализ вариантов - это подход, который можно успешно сочетать с первыми тремя, делая их применение более упорядоченным. Впервые его формализовал Джекобсон, определивший вариант применения, как "частный пример или образец использования, сценарий, начинающийся с того, что пользователь системы инициирует операцию или последовательность взаимосвязанных событий" [41].
Коротко говоря, этот вид анализа можно начинать вместе с анализом требований. В этот момент пользователи, эксперты и разработчики перечисляют сценарии, наиболее существенные для работы системы (пока не углубляясь в детали). Затем они тщательно прорабатывают сценарии, раскладывая их по кадрам, как делают телевизионщики и кинематографисты [42]. При этом они устанавливают, какие объекты участвуют в сценарии, каковы обязанности каждого объекта и как они взаимодействуют в терминах операций. Тем самым группа разработчиков вынуждена четко распределить области влияния абстракций. Далее набор сценариев расширяется, чтобы учесть исключительные ситуации и вторичное поведение (Гольдстейн и Алджер называют это периферийными аспектами [43]). В результате появляются новые или уточняются существующие абстракции. Позже, в главе 6, мы покажем, как сценарии используются для тестирования. CRC-карточки. CRC обозначает Class-Responsibilities-Collaborators (Класс/Ответственности/Участники). Это простой и замечательно эффективный способ анализа сценариев. Карты CRC впервые предложили Бек и Каннингхэм для обучения объектно-ориентированному программированию, но такие карточки оказались отличным инструментом для мозговых атак и общения разработчиков между собой.
Собственно, это обычные библиографические карточки 3х5 дюйма (если позволяет бюджет вашего проекта, купите 5х7; очень хорошо, если карточки будут линованными, а разноцветные - просто мечта). На карточках вы пишите (обязательно карандашом) сверху - название класса, снизу в левой половине - за что он отвечает, а в правой половине - с кем он сотрудничает. Проходя по сценарию, заводите по карточке на каждый обнаруженный класс и дописывайте в нее новые пункты. При этом каждый раз обдумывайте, что из этого получается, и "выделяйте излишек ответственности" в новый класс или, что случается чаще всего, перенесите ответственности с одного большого класса на несколько более детальных классов, или, возможно, передайте часть обязанностей другому классу.
Карточки можно раскладывать так, чтобы представить формы сотрудничества объектов. С точки зрения динамики сценария, их расположение может показать поток сообщений между объектами, с точки зрения статики они представляют иерархии классов.
Неформальное описание. Радикальная альтернатива классическому анализу была предложена в чрезвычайно простом методе Аббота. Согласно этому методу надо описать задачу или ее часть на простом английском языке, а потом подчеркнуть существительные и глаголы [45]. Существительные - кандидаты на роль классов, а глаголы могут стать именами операций. Метод можно автоматизировать, и такая система была построена в Токийском технологическом институте и в Fujitsu [46].
Подход Аббота полезен, так как он прост и заставляет разработчика заниматься словарем предметной области. Однако он весьма приблизителен и непригоден для сколько-нибудь сложных проблем. Человеческий язык - ужасно неточное средство выражения, потому список объектов и операций зависит от умения разработчика записывать свои мысли. Тем более, что для многих существительных можно найти соответствующую глагольную форму и наоборот.
Структурный анализ. Вторая альтернатива классической технике объектно-ориентированного анализа использует структурный анализ как основу для объектно-ориентированного проектирования. Такой подход привлекателен потому, что много аналитиков применяют этот подход и имеется большое число программных CASE-средств, поддерживающих автоматизацию этих методов. Нам лично не нравится использовать структурный анализ как основу для объектно-ориентированного проектирования, но для некоторых организаций такой прагматический подход не имеет альтернативы.
После проведения структурного анализа мы уже имеем модель системы, описанную диаграммами потоков данных и другими продуктами структурного анализа. Эти диаграммы дают нам формальную модель проблемы. Исходя из модели, мы можем приступить к определению осмысленных классов и объектов тремя различными способами.
МакМенамин и Палмер предлагают сначала приступить к формированию словаря данных и затем к анализу контекстных диаграмм модели. Они говорят:
"рассматривая список основных структур данных, следует подумать, о чем они говорят или что описывают. Например, если они прилагательные, то какие существительные они описывают? Ответы на такие вопросы могут пополнить ваш список объектов" [47]. Эти кандидаты в объекты происходят из окружающей среды, из существенных входных и выходных данных, а также продуктов, услуг и других ресурсов, которыми она управляет.
Следующие два способа основаны на анализе отдельных диаграмм потоков данных. Если взять какую-нибудь диаграмму потоков (в терминологии Барда и Меллора [48]), то кандидаты в объекты это:
• внешние сущности;
• хранилища данных;
• хранилища управляющих сущностей;
• управляющие преобразования.
Кандидаты в классы:
• потоки данных;
• потоки управления.
Остается преобразование данных, которое мы можем рассматривать как операции над существующими объектами или как поведение некоторого объекта, который мы создали специально для выполнения нужного преобразования.
Зайдевиц и Старк предлагают еще один метод, который они называют анализом абстракций. Метод базируется на идентификации основных сущностей, которые по своей природе аналогичны основным преобразованиям в структурном проектировании. Как они говорят, "в структурном анализе входные и выходные данные изучаются до тех пор, пока не достигнут высшего уровня абстракции. Процесс преобразования входных данных в выходные есть основное преобразование. В абстрактном анализе разработчик делает то же самое, а также изучает основное преобразование для того, чтобы определить, какие процессы и состояния представляют наилучшую абстрактную модель системы" [49]. После определения основной сущности в диаграмме потоков данных аналитик приступает к изучению всей инфраструктуры, прослеживая входящие и исходящие потоки данных из центра, группируя процессы и состояния, встречающиеся по пути. Для практического использования авторы нашли анализ абстракций слишком сложным и в качестве альтернативы предлагают объектно-ориентированный анализ [50].
Необходимо отметить, что принципы структурного проектирования, которое, естественно, следует за структурным анализом, полностью ортогональны принципам объектно-ориентированного проектирования. Наш опыт показывает, что использование структурного анализа в процессе объектно-ориентированного проектирования часто приводит к полному провалу, если разработчик не способен сопротивляться желанию свалиться в структурную пропасть. Другая очень серьезная опасность заключается в том, что многие аналитики любят рисовать диаграммы потоков данных, которые представляют собой скорее описание проекта, чем модель существа системы. Очень трудно построить объектно-ориентированную систему, если модель столь очевидно ориентирована на алгоритмическую декомпозицию. Поэтому мы предпочитаем объектно-ориентированный анализ и анализ проблемной области как подготовительный этап для объектно-ориентированного проектирования. При этом уменьшается риск замусорить проект элементами алгоритмического анализа.
Если же по каким-либо уважительным причинам [Политические и исторические причины в качестве уважительных не принимаются] приходится взять за основу структурный анализ, прекратите строить диаграммы, как только они начинают смахивать на проект программы, а не на модель предметной области. Помните, что материалы проектирования, такие, как диаграммы потоков данных, это не конечный продукт, а инструмент разработчиков. Обычно строятся диаграммы, а затем разрабатываются механизмы, обеспечивающие необходимое поведение системы, то есть сам акт проектирования видоизменяет начальную модель. Поддержание соответствия модели и развивающегося проекта - дело трудное, и, честно говоря, бесполезное. Имеет смысл сохранять только продукт структурного анализа высокого уровня абстракции. Он отражает существенные черты и достаточно независим от проекта системы.