» Главная
eXcode.ru » Статьи » Другие » Основы объектно-ориентированного проектирования
» Новости
» Опросы
» Файлы
» Журнал



Пользователей: 0
Гостей: 15





Как найти классы




Изучение документа "технические требования"

Для понимания проблемы поиска классов, возможно, лучше всего начать с известного и широко опубликованного подхода.

Существительные и глаголы

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

Лифт закрывает дверь, прежде чем двигаться к следующему этажу (The elevator 
will close its door before it moves to another floor)

функционально-ориентированный разработчик извлечет необходимость создания функции "move", а ОО-разработчик увидит необходимость создания объектов трех типов: ELEVATOR, DOOR and FLOOR, приводящих к классам. Вот?!

Как было бы прекрасно, если бы жизнь была такой простой! Вы бы захватили документ с требованиями домой на вечерок, сыграли бы за обеденным столом в игру "Погоня За Объектами". Это был бы хороший способ отвлечь детей от телевизора, повторить заодно грамматику и помочь маме с папой в их важной работе по конструированию ПО.

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

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

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

Как избежать бесполезных классов

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

В примере с лифтовой системой door является существительным, но необходим ли класс DOOR? Может быть, да, может быть - нет. Возможно, что единственным свойством дверей лифта является их способность открываться и закрываться. Тогда проще включить это свойство в виде соответствующего запроса и команды в класс ELEVATOR:

door_open: BOOLEAN;
close_door is
    ...
    ensure
        not door_open
    end;
open_door is
    ...
    ensure
        door_open
    end

В другом варианте понятие двери может заслуживать отдельного класса. Единственной реальной основой является здесь теория АТД. Вот вопрос, на который действительно следует ответить:

Является ли "door" независимым типом данных с собственными четко определенными операциями или все они уже включены в операции других типов данных, таких как, например, ELEVATOR?

Только наша интуиция и опыт проектировщика даст нам правильный ответ. Грамматические правила анализа документа требований малосодержательны. Вместо них следует обращаться к теории АТД, помогающей задавать правильные вопросы заказчикам и будущим пользователям системы.

Мы уже встречались (см. лекцию 3) с подобной ситуацией при проектировании механизма откатов и возвратов. Речь шла о понятии commands и более общем понятии operation, включающем запросы, подобные Undo. Оба слова фигурировали в документе требований, однако, только COMMAND приводил к абстракции данных - важнейшему классу проекта.

Нужен ли новый класс?

Еще одним примером существительного в примере с лифтом является слово floor. В отличие от дверей с их единственной операцией, понятие этажа является разумным АТД, однако этого мало, чтобы появился класс FLOOR.

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

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

rights: SET [AUTHORIZATION]

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

floor_rights: ARRAY [SET [AUTHORIZATION]]

связывающий множество значений AUTHORIZATION с каждым этажом, идентифицируемым его номером (см. У4.1).

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

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

"Релевантность системе" является определяющим критерием. Цель анализа системы не в том, чтобы "моделировать мир", - об этом пусть заботятся философы. Создатели ПО не могут позволить себе этого, по крайней мере, в своей профессиональной деятельности, их задачей является моделирование мира лишь в той мере, которая касается создаваемого ПО. Подход АТД и соответственно ОО-метода основан на том, что объекты определяются только тем, что мы можем с ними делать, - это называлось (см. лекцию 6 курса "Основы объектно-ориентированного программирования") Принципом Разумного Эгоизма. Если операция или свойство объекта не отвечают целям системы, то они и не включаются в состав класса, хотя и могут представлять интерес для других целей. Понятие PERSON может включать такие компоненты, как mother и father, но для системы уплаты налогов они не нужны, здесь личность выступает сама по себе, подобно сироте.

Если все операции и свойства некоторого типа не связаны с целями системы или покрываются другими классами, то сам тип не должен рассматриваться как самостоятельный класс.

Пропуск важных классов

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

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

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

Запись базы данных должна создаваться всякий раз, когда лифт перемещается от одного этажа к другому (A database record must be created every time the elevator moves from one floor to another).

Существительное "record" предполагает класс DATABASE_RECORD; но при этом можно пропустить более важную абстракцию данных: понятие move, определяющее перемещение между этажами. Из смысла данного предложения скорее следует необходимость класса MOVE, например, в форме:

class MOVE feature
    initial, final: FLOOR;        -- Или INTEGER, если нет класса FLOOR
    record (d: DATABASE) is ...
    ... Другие компоненты...
end

Этот важный класс вполне мог быть пропущен при простом грамматическом разборе предложения. Правда, наше предложение могло появиться и в другой форме:

Каждое перемещение лифта приводит к созданию записи в базе данных (A database record must be created for every move of the elevator from one floor to another).

Здесь "move" из глагола переходит в разряд существительных, претендуя на класс в соответствии с грамматическим критерием. Угрозы и абсурдность подхода, основанного на анализе документа, написанного на естественном языке, очевидны. Такое серьезное дело, как проектирование системы, в частности ее модульная структура, не может зависеть от причуд стиля и настроения автора документа.

Другая важная причина в пропуске критически важных абстракций состоит в том, что они могут не выводиться непосредственно из документа с требованиями. Примерами подобных ситуаций изобилует данная книга. Вполне возможно, что в документе, определяющем требования к системе, управляемой панелями (см. лекцию 2) ни слова нет о понятиях состояние или приложение (State, Application), задающих ключевые абстракции нашего заключительного проекта. Ранее уже отмечалось, что некоторые понятия внешнего мира могут не иметь двойников среди классов системы ПО. Имеет место и обратная ситуация: классы ПО могут не соответствовать никаким объектам внешнего мира. Аналогично, если автор требований к текстовому редактору, включающему откаты, написал: "система должна поддерживать вставку и удаление строк" (the system must support line insertion and deletion), то нам повезло, и мы обратим внимание на существительные insertion и deletion. Но необходимость этих свойств точно также должна следовать из предложения в форме:

Редактор должен позволять пользователям вставлять и удалять строки в текущей позиции курсора (The editor must allow its users to insert or delete a line at the current cursor position).

Наивный разработчик в этом тексте может обратить внимание на тривиальные понятия курсора и позиции, пропустив абстракции команд: вставка и удаление строк.

Третья главная причина пропуска классов характерна для любого метода, использующего документ с требованиями как основу анализа, поскольку такая стратегия не учитывает повторного использования. Удивительно, но литература по ОО-анализу (см. лекцию 9) исходит из традиционного взгляда на разработку - все начинается с документа с техническими требованиями на систему и движется к поиску решения проблемы, описанной в документе. Один из главных уроков объектной технологии как раз состоит в том, что не существует четко выраженного различия между постановкой проблемы и ее решением. Существующее ПО может и должно влиять на новые разработки.

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

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

Обнаружение и селекция

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

Помимо прямых уроков это обсуждение ведет к более тонким следствиям.

Простые уроки звучали неоднократно: не слишком полагаться на документ с требованиями, не доверять грамматическим критериям.

Менее очевидный урок вытекает из обзора ложных тревог. Суть его в том, что нужен не только критерий для поиска классов, но и критерий для отбраковки (rejecting) кандидатов. Концепция может показаться вначале обнадеживающей, а в результате анализа она отбраковывается. Примеров подобных ситуаций в данной книге предостаточно (см. лекцию 5).

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

Это может быть даже более важно. Как правило, идей по поводу классов (обычно предлагаемых в виде объектов) хватало с избытком. Проблемой было поставить плотину на пути этого потока. Хотя некоторые важные классы пропускались, значительное большее количество отвергалось по результатам анализа.

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

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

Выявление класса - это двойственный процесс: генерирование кандидатов, их отбраковка.

Остаток этой лекции посвящен изучению составляющих этого процесса.

Сигналы опасности

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

Большое Заблуждение

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

При ОО-конструировании модули строятся вокруг типов объектов, а не функций. В этом ключ к преимуществам, открываемым при расширяемости системы и ее повторном использовании. Но новички склонны попадать в наиболее очевидную ловушку, называя классом то, что в действительности является функцией (подпрограммой). Записав модуль в виде class... feature ... end, еще не означает появления настоящего класса, это просто программа, скрывающаяся под маской класса.

Этого Большого Заблуждения (Grand Mistake) достаточно просто избежать, как только оно осознано. Проверка ситуации обычная: следует убедиться, что каждый класс соответствует осмысленной абстракции данных. Из этого следует, что всегда есть риск, что модуль, представленный как кандидат в классы, и носящий одежды класса, на самом деле является нелегальным иммигрантом, не заслуживающим гражданства в обществе ОО-модулей.

Мой класс выполняет...

В формальных и неформальных обсуждениях архитектуры проекта часто задается вопрос о роли некоторого класса. И часто можно слышать в ответ: "Этот класс печатает результаты" или "Класс разбирает вход" - варианты общего ответа "Этот класс делает...".

Такой ответ обычно указывает на изъяны в проекте. Класс не должен делать одну вещь, он должен предлагать несколько служб в виде компонентов над объектами некоторого типа. Если же он выполняет одну работу, то, скорее всего, имеет место "Большое Заблуждение".

Вполне вероятно, что ошибка не в самом классе, а способе его описания - использовании операционной фразеологии. Но все-таки в этой ситуации лучше провести проверку класса.

Императивные имена

Предположим, что в процессе проектирования появились классы с такими именами, как PARSE (РАЗОБРАТЬ) или PRINT (ПЕЧАТАТЬ) - глагол в императивной форме. Это должно насторожить, не делает ли класс одну вещь и, следовательно, не должен быть классом.

Возможно, вы найдете, что с классом все в порядке, но тогда имя его выбрано неудачно. Вот "абсолютно положительное" правило:

Правило Имен класса

Имя класса всегда должно быть либо:

  • cуществительным, возможно квалифицированным;
  • прилагательным (только для отложенных классов, описывающих структурное свойство).

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

Первая форма - существительные - покрывает большинство важнейших случаев. Существительное может появляться само по себе, например TREE, или с квалифицирующими словами - LINKED_LIST, квалифицируемое прилагательным, LINE_DELETION, квалифицируемое другим существительным.

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

Единственный случай, который может показаться исключением из правила, задает командные классы, так как они введены в шаблоне проектирования undo-redo, покрывающем абстракции действий. Но даже и здесь можно следовать правилу, задавая имена командных классов текстового редактора в виде: LINE_DELETION и WORD_CHANGE, а не DELETE_LINE и REPLACE_WORD (Удаление_Строки, а не Удалить_Строку).

Английский язык предоставляет бо_льшую гибкость, чем многие другие языки, где грамматическая категория - это скорее дело веры, чем факта, и почти каждый глагол может быть и существительным. При использовании английского языка в программных именах легче придерживаться этого правила и строить короткие имена. Вы можете назвать класс IMPORT , рассматривая это имя как существительное, а не как глагол. В других языках вам пришлось бы строить более тяжеловесное имя, нечто вроде IMPORTATION. Но здесь не должно быть надувательства: класс IMPORT должен покрывать абстракцию данных - "объекты, подлежащие импорту", а не быть командным классом, задающим единственную операцию импорта.

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

Однопрограммные классы

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

Исключением являются объекты, вполне законно представляющие абстрактные действия, например команды интерактивной системы (см. лекцию 3), или то, что в не объектном подходе представляло бы функцию, передаваемую в качестве аргумента другой функции. Но примеры, приведенные в предыдущих обсуждениях, достаточно ясно показывают, что даже в этих случаях у класса может быть несколько полезных компонентов. Так для класса, задающего подынтегральную функцию, может существовать не только компонент item, возвращающий значение функции. Другие компоненты этого класса могут задавать максимум и минимум функции на некотором интервале, ее производную. Даже если класс не содержит этих компонентов, знание того, что они могут появиться позднее, заставляет нас считать, что мы имеем дело с настоящей абстракцией.

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

Преждевременная классификация

Упомянув таксоманию, следует отметить еще одну общую ошибку новичков - преждевременное построение иерархии классов.

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

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

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

Мне приходилось видеть людей, начинавших с создания классов SAN_FRANCISCO и HOUSTON наследников класса CITY, когда нужно было промоделировать ситуацию с одним классом CITY и несколькими его экземплярами - объектами периода выполнения.

Классы без команд

Иногда можно обнаружить классы, вообще не имеющие команд или допускающие только запросы (доступ к объектам только в режиме чтения), но не команды (процедуры, модифицирующие объекты). Такие классы являются эквивалентами записей языка Pascal или структур Cobol и C. Такие классы могут появиться из-за ошибок проектирования, которые могут быть двух видов, а, следовательно, нуждаются в некотором исследовании.

Прежде всего, рассмотрим три случая, когда такой класс не является результатом неподходящего проектирования:

  • Он может представлять объекты, полученные из внешнего мира, не подлежащие изменениям в ПО. Это могут быть данные датчиков от органов системы управления прибора, пакеты, передаваемые в сети, структуры С, которых ОО-система не должна касаться.
  • Некоторые классы не предназначены для прямого использования - они могут инкапсулировать константы или выступают в качестве родителей других классов. Такое льготное наследование (facility inheritance) будет изучаться при обсуждении методологии наследования (см. лекцию 6).
  • Наконец, класс может быть аппликативным - описывающим объекты, не подлежащие модификации. Это означает, что у класса есть только функции, создающие новые объекты. Например, операция сложения в классах INTEGER, REAL и DOUBLE следует математическим традициям - она не модифицирует значение, но, получив x и y, вырабатывает значение: x + y. В спецификации АТД такие функции характеризуются как командные функции.

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

Теперь вернемся к подозрительным случаям. В первом из них появление класса оправдано, команды классу нужны - проектировщик просто забыл обеспечить механизм модификации объектов. Простая техника контрольной ведомости (checklist) позволяет избежать подобных ошибок (см. лекцию 5).

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

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

Подобная история имела место при разработке ISE компилятора. Для внутренних потребностей идентификации классов решено было использовать целые числа, а не специальные объекты. Все это хорошо работало несколько лет. Но потом схема идентификации усложнилась, в частности пришлось перенумеровывать классы при слиянии нескольких систем в одну. Так что пришлось вводить класс CLASS_IDENTIFIER и заменять экземплярами этого класса прежние целые. Это потребовало усилий больше, чем хотелось бы.

Смешение абстракций

Еще один признак несовершенного проектирования состоит в том, что в одном классе смешиваются две или более абстракций.

В ранней версии библиотеки NeXT текстовый класс обеспечивал полное визуальное редактирование текста. Пользователи жаловались, что, хотя класс полезен, но очень велик. Большой размер класса - это симптом, истинная причина состояла в том, что были слиты две абстракции - собственно редактирование строк и визуализация текста. Класс был разделен на два класса: NSAttributedString, определяющий механизм обработки строк, и NSTextView, занимающийся интерфейсом.

Мэйлир Пейдж Джонс ([Page-Jones 1995]) использует термин соразвитие (connascence), означающий совместное рождение и совместный рост. Для классов соразвитие означает отношение, существующее между двумя тесно связанными компонентами, когда изменение одного влечет одновременное изменение другого. Как он указывает, следует минимизировать соразвитие между классами библиотеки, но компоненты, появляющиеся внутри данного класса, все должны быть связаны одной и той же четко определенной абстракцией.

Этот факт заслуживает отдельного методологического правила, сформулированного в "положительной" форме:

Принцип Согласованности класса

Все компоненты класса должны принадлежать одной, хорошо определенной абстракции.

Идеальный класс

Этот обзор возможных ошибок по контрасту проявляет характерные черты идеального класса. Вот его некоторые типичные свойства:

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

Этот список описывает множество неформальных целей, не являясь строгим правилом. Легитимный класс может обладать лишь одним из перечисленных свойств. Большинство из примеров, играющих важную роль в этой книге, - начиная от классов LIST и QUEUE до BUFFER, ACCOUNT, COMMAND, STATE, INTEGER, FIGURE, POLYGON и многих других, - обладают всеми этими свойствами.

Общие эвристики для поиска классов

Давайте теперь обратимся к положительной части нашего обсуждения - практическим эвристикам поиска классов.

Категории классов

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

Классы анализа описывают абстракцию данных, непосредственно выводимую из модели внешней системы. Типичными примерами являются классы PLANE в системе управления полетами, PARAGRAPH в системе обработки документов, PART в системе управления запасами.

Классы реализации описывают абстракцию данных, введенную, исходя из внутренних потребностей алгоритмов системы, например LINKED_LIST или ARRAY.

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

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

Внешние объекты: нахождение классов анализа

Давайте начнем с классов анализа, моделирующих внешние объекты.

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

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

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

Эта точка зрения наиболее явно проявляется в такой области как моделирование (simulation). Неслучайно, что первый ОО-язык программирования Simula 67 вырос из Simula 1 - языка моделирования дискретных событий. Хотя Simula 67 является универсальным языком общего назначения, он сохранил имя своего предшественника и включил множество мощных примитивов моделирования. В семидесятые годы моделирование являлось принципиальной областью приложения объектной технологии. Привлекательность ОО-идей для моделирования легко понять - создавать структуру программной системы, моделирующей поведение множества внешних объектов, проще всего при прямом отображении этих объектов в программные компоненты.

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

Эти примеры рисуют лишь часть общей картины. Как заметили Валден и Нерсон ([Walden 1995]) в представлении метода B.O.N: "Класс, описывающий автомобиль, не более осязаем, чем тот, который моделирует удовлетворенность служащих своей работой"

Следует всегда иметь в виду этот комментарий при поиске внешних классов - они могут быть довольно абстрактными: SENIORITY_RULE в парламентской системе голосования, MARKET_TENDENCY в рыночной системе могут быть также реальны, как SENATOR и STOCK_EXCHANGE. Улыбка Чеширского Кота - такой же объект, как и сам Чеширский Кот.

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

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

Нахождение классов реализации

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

Плохая новость - классы реализации трудно строить. Хорошая новость - их легко выявить. Для них может существовать хорошая библиотека, допускающая повторное использование. По крайней мере, есть хорошая литература по этой тематике. Курс "Алгоритмы и структуры данных", иногда известный как CS 2, является необходимым компонентом образования в области информатики. Хотя большинство из существующих учебников явно не используют ОО-подход, многие следуют стилю АТД. Преобразование в классы в этом случае довольно прямолинейно и естественно.

В настоящее время некоторые учебники начали применять ОО-подход при изложении традиционных тем CS 2.

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

Помимо учебников и опыта лучшим вариантом для классов реализации являются библиотеки повторного использования.

Отложенные классы реализации

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

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

В одной из своих книг ([M 1993]) я описал "Линнеевскую" таксономию фундаментальных структур информатики, в основе которой лежат отложенные классы, характеризующие принципиальные виды структур данных, используемых при разработке ПО.

Нахождение классов проектирования

Классы проектирования представляют архитектурные абстракции, помогающие создавать элегантные расширяемые программные структуры. Хорошими примерами являются классы: STATE, APPLICATION, COMMAND, HISTORY_LIST, итератор и контроллер. Мы увидим и другие полезные идеи в последующих лекциях, такие как активные структуры данных и описатели ("handles").

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

  • Многие классы проектирования были изобретены уже до нас. Так что чтение книг и статей, описывающих решение проблем проектирования, может дать много плодотворных идей. Например, книга "Объектно-ориентированные приложения" ([M 1993]) содержит лекции, написанные ведущими разработчиками различных промышленных проектов. Приводятся точные и детальные архитектурные решения, полезные в таких областях, как телекоммуникации, автоматизированное проектирование, искусственный интеллект, и других проблемных областях.
  • Книга Гаммы ([Gamma 1995]) посвящена образцам проектирования, за ней последовали другие подобные книги.
  • Многие полезные классы проектирования лучше понимаются как "машины", чем как "объекты" в общем смысле этого слова.
  • Как и в случае классов реализации, повторное использование лучше, чем изобретение. Можно надеяться, что текущие образцы перестанут быть просто идеями и превратятся в непосредственно используемые библиотечные классы.

Другие источники классов

Несколько эвристик доказали свою полезность в битвах за правильные абстракции.

Предыдущие разработки

Совет искать то, что доступно, применим не только к библиотечным классам. Когда вы пишете приложения, вы аккумулируете классы, облегчающие при правильном проектировании дальнейшие разработки.

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

Адаптация через наследование

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

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

Эта техника (см. лекцию 10), которая еще будет изучаться в деталях под именем вариационное наследование (variation inheritance), предполагает, что новый класс задает вариант той же абстракции, что и оригинал. При подходящем использовании она представляет наиболее значительный вклад в Метод, позволяя разрешить проблему reuse-redo - сочетание повторного использования с расширяемостью.

Оценивание кандидатов декомпозиции

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

Важный критерий исследовался (см. лекцию 2) в примере системы, управляемой панелями, - потоки данных. Мы видели тогда, как важно при рассмотрении структуры класса кандидата анализировать потоки объектов, передаваемых как аргументы в последовательных вызовах. Если, как это было с понятием состояния, обнаруживается, что некоторый элемент информации передается многим модулям, то это определенный признак того, что пропущена важная абстракция данных. Такой анализ привел нас к необходимости введения класса STATE, важного источника абстракции.

Конечно, предпочтительнее найти правильные классы с самого начала. При позднем, апостериорном обнаружении следует найти время на анализ причин, почему важная абстракция была пропущена, чтобы извлечь уроки на будущее.

Находки других подходов

Пример анализа потока данных в нисходящей структуре иллюстрирует идею выявления класса при рассмотрении необъектной декомпозиции. Это полезно в двух непересекающихся случаях:

  • Может существовать не ОО-система, выполняющая свою часть работы. Тогда разумно провести ее анализ с позиций классов. Иногда, вместо работающей системы, используются результаты анализа или проектирования, выполненного другими, старыми методами.
  • Некоторые из разработчиков могут иметь большой опыт работы в создании не объектных систем и, как следствие, вначале проектируют систему в терминах других концепций, затем реализуют ее в виде классов.

Вот примеры этого процесса, начиная с языков программирования и заканчивая методами анализа и проектирования.

Программы Fortran включают обычно один или несколько общих блоков (common blocks) - данных, разделяемых многими подпрограммами. Зачастую за общими блоками стоят очень важные абстракции данных. Более точно, хорошие Fortran программисты знают, что в общий блок следует включать те переменные и массивы, которые соответствуют тесно связанным понятиям, и в этом случае за этим стоит шанс создать класс на основе общего блока. К сожалению, это не универсальная практика, - в начале этой книги упоминалось о "мусорном" общем блоке, куда сваливают все подряд. В этом случае анализ должен проводиться куда более тщательно для обнаружения подходящих абстракций.

Программы Pascal и C используют записи, известные в C как структуры. Они могут также соответствовать классам при условии выявления операций над данными записей. Если это не так, то запись будет представлена атрибутами некоторого класса.

Структуры Cobol и его секции данных (Data Division) помогают идентифицировать важные типы данных.

При рассмотрении моделей, основанных на понятиях "сущность-отношение", сущности ("entities") часто служат основой для построения классов.

При проектировании потоков данных (dataflow) немногое может быть непосредственно использовано для ОО-целей, но иногда "хранилища" (stores) могут приводить к нужным абстракциям.

Файлы

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

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

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

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

Ивар Якобсон ([Jacobson 1992]) пропагандирует использование ситуаций для выявления классов. Ситуации, называемые также сценариями (scenario) или трассами, описываются как

полный набор событий, инициированных пользователем или системой, и 
взаимодействий между пользователем и системой.

В телефонной системе ситуация, например "вызов, инициированный заказчиком", приводит к последовательности событий: заказчик поднимает трубку телефона, системе посылается сигнал идентификации, система вырабатывает гудок и так далее.

Использование ситуаций не самый лучший способ нахождения классов. Здесь возникает несколько рискованных моментов:

  • В сценариях предполагается упорядоченность. Это несовместимо с объектной технологией (см. лекцию 7 курса "Основы объектно-ориентированного программирования"). Не следует основываться на порядке действий, поскольку порядок в первую очередь подвержен изменениям. Не следует фокусироваться на свойствах в форме: "система выполняет a, затем b"; вместо этого следует задавать вопрос: "Какие операции доступны для экземпляров абстракции A, и каковы ограничения на эти операции?" По-настоящему фундаментальные свойства, отражающие последовательность выполнения операций, задаются ограничениями высокого уровня. Утверждение, что для стека число операций pop не должно превосходить число операций push, можно выразить в более абстрактной форме, используя пред и постусловия этих операций. Менее абстрактные свойства порядка вообще не должны учитываться на этапе анализа. При работе со сценариями возможность таких ошибок велика.
  • Сценарии также предполагают фокусирование на пользовательском видении операций. Но система пока еще не существует. Может существовать ее предыдущая версия, но, если бы она была полностью удовлетворительной, то не возникала бы потребность в ее переписывании. Ваша задача состоит в том, чтобы предложить лучший сценарий. Есть много примеров неудач, связанных с рабским повторением существующих процедур.
  • Использование сценариев предпочитают при функциональном подходе, основанном на процессах (действиях). Сохраняется опасность, что под маской классов скрывается традиционная форма функционального проектирования. Этот подход противоположен ОО-декомпозиции, сфокусированной на абстракции данных. Использование нескольких сценариев исключает одну главную программу, но и здесь начальной точкой остается вопрос, что делает система, в отличие от объектного подхода, где важнее, кто это делает. Дисгармония неизбежна.

Практические следствия очевидны:

Принцип использования сценариев

За исключением очень опытных команд разработчиков (имеющих опыт создания нескольких систем, содержащих несколько тысяч классов в чистом ОО-языке) не следует основываться на сценариях как средстве ОО-анализа и проектирования.

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

Еще одно применение сценариев связано с заключительными аспектами реализации - система может включать специальные программы для запуска типичных сценариев. Такие программы зачастую задают некоторый вид абстрактного поведения, описывая общую схему, которая может быть переопределена различными способами. В книге [Jacobson 1992] вводится понятие абстрактного сценария, что в объектной терминологии называется классом поведения.

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

КОС (CRC) карты

Для полноты картины упомянем идею, рассматриваемую иногда как метод нахождения классов. Карты КОС (Класс, Ответственность, Сотрудничество) или CRC (Class, Responsibility, Collaboration) являются бумажными карточками, используемыми разработчиками при обсуждении потенциальных классов, в терминах их ответственностей и взаимодействия. Идея проста, отличается дешевизной - набор карточек дешевле рабочей станции с CASE инструментарием. Но его технический вклад в процесс проектирования неясен.

Повторное использование

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

Подход снизу вверх

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

Довольно часто, когда мы говорим о нахождении классов, подразумевается их изобретение (devising). В объектной технологии с ростом качества библиотек и осознания идей повторного использования приобретает смысл именно поиск (finding) классов.

Сказка о поиске классов

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

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

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

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

Мудрец склонил свою голову и ответил медленно и спокойно: "Возвращайся назад, откуда пришел. Классы уже найдены".

Оглушенный, он и не заметил, как слуги Мастера выводили его прочь. "Мастер", - теперь он почти кричал, - "пожалуйста, еще только один вопрос. Как называется эта история?" Старый Учитель покачал головой: "Разве ты еще не понял? Это сказка о повторном использовании".

Метод получения классов

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

Идентификация класса требует двух неразрывно связанных видов деятельности - выявления множества кандидатов в классы и отбора среди них действительно нужных. В двух следующих таблицах подводятся итоги.

Прежде всего начнем с источников классов.

Таблица 4.1. Источники возможных классов
Источник идейЧто ищется
Существующие библиотеки
  • Классы, отвечающие потребностям приложения.
  • Классы, описывающие концепции, релевантные приложению.
Документ требований
  • Часто встречающиеся термины.
  • Термины, заданные явными определениями.
  • Термины, не определенные точно, но считающиеся само собой разумеющимися.
  • (Грамматические категории следует игнорировать.)
Обсуждения с заказчиками и будущими пользователями
  • Важные абстракции проблемной области.
  • Специфический жаргон проблемной области.
  • Помнить, что классы, приходящие из "внешнего мира", могут описывать как материальные, так и концептуальные объекты.
Документация (руководства пользователей) для других систем в той же проблемной области, например от конкурентов
  • Важные абстракции проблемной области.
  • Специфический жаргон проблемной области.
  • Полезные абстракции проектирования.
Не ОО-системы и их описания
  • Элементы данных, передаваемые в виде аргументов компонентам ПО.
  • Разделяемые данные (Common блоки FORTRAN).
  • Важные файлы.
  • Секции данных (COBOL).
  • Типы записей (Pascal, C, C++).
  • Сущности при ER-моделировании.
Обсуждения с опытными проектировщиками
  • Классы проектирования, успешно используемые в предыдущих разработках.
Литература по алгоритмам и структурам данных
  • Известные структуры данных, поддержанные эффективными алгоритмами.
Литература по ОО-проектированию
  • Применимые образцы проектирования.

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

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




Добавил: MadvEXДата публикации: 2006-02-28 01:39:56
Рейтинг статьи:3.00 [Голосов 5]Кол-во просмотров: 4581

Комментарии читателей

Всего комментариев: 1

2009-09-13 19:54:33
allnicktoday
Вот такое я ждал давно... теперь нашел сенкс
[url=http://www.magaz-on-line.ru/] [/url]
[url=http://www.w-mega.ru] [/url]
[url=http://www.buy-on-line.ru] [/url]
[url=http://www.w-mega.ru] [/url]
[url=http://www.buy-on-line.ru] [/url]
Ваше имя: *
Текст записи: *
Имя:

Пароль:



Регистрация

Какую БД предпочитаете?
MSSQL
20% (38)
BDE
1% (1)
MySQL
35% (68)
Access
6% (11)
InterBase
11% (21)
Paradox
3% (5)
Oracle
10% (19)
PostgreSQL
0% (0)
Другой
3% (6)
Не использую БД!
12% (23)

Проголосовало: 192
В мире 10 категорий людей - те, которые понимают двоичную систему счисления, и те, которые ее не понимают.
Рейтинг: 9.3/10 (74)
Посмотреть все анекдоты