Предупреждаю, не следует ожидать слишком многого. Поиск классов - это одна из центральных проблем при ОО-конструировании ПО. Как в любой творческой деятельности ее решение определяется талантом и опытом, не исключая и доли везения. Нереально ожидать безошибочных рецептов по поиску классов, как нельзя ожидать рецептов, позволяющих математику создавать новые теории или находить
При проектировании ПО никакие книги не могут заменить вашей интуиции и владения тем, что называется "ноу-хау" (know-how). Роль методологического обсуждения состоит в том, чтобы указать на хорошие идеи, привлечь внимание к удачным
Это относится к любому методу проектирования. В объектном случае есть некоторые хорошие новости, приходящие в форме повторного использования. Поскольку многое из необходимого уже изобретено, можно начинать строить, используя достижения других разработчиков.
Есть и другие хорошие новости. Начиная со скромных притязаний, но внимательно изучив, что работает, а что нет, двигаясь мало-помалу в исключительно неблагоприятных условиях, мы получим возможность изобрести нечто, заслуживающее название метода поиска классов. Как всегда при поиске кандидатов, метод отбора включает два компонента - набор абитуриентов и их последующий отсев.
Для понимания проблемы поиска классов, возможно, лучше всего начать с известного и широко опубликованного подхода.
В нескольких публикациях предлагается простое правило для получения классов, - начинайте с документа "технические требования" (считается, что кто-то его создал, но это уже другая история). В функционально-ориентированном проекте следует концентрироваться на глаголах, соответствующих действиям. При ОО-проектировании отмечайте существительные, описывающие объекты. Так из предложения:
Лифт закрывает дверь, прежде чем двигаться к следующему этажу (The elevator will close its door before it moves to another floor)
функционально-ориентированный разработчик извлечет необходимость , and , приводящих к классам. Вот?!
Как было бы прекрасно, если бы жизнь была такой простой! Вы бы захватили документ с требованиями домой на вечерок, сыграли бы за обеденным столом в игру "Погоня За Объектами". Это был бы хороший способ отвлечь детей от
К сожалению, такой примитивный метод не слишком помогает. Естественный язык, используемый экспертами при составлении технических требований, обладает столь многими нюансами, субъективными вариациями, двусмысленностями, что крайне опасно принимать важные решения на основе
Метод " подчеркивание существительных " дает лишь очевидные понятия. Любой разумный ОО-метод проектирования системы управления лифтом будет включать класс . Получение подобных классов не самая трудная часть задачи. Повторяя сказанное в предыдущих обсуждениях, генерируемые понятия нуждаются в прополке - необходимо отделить зерна от плевел.
Хотя идея подчеркивания существительных не заслуживает особого рассмотрения, мы будем использовать ее для контраста, для лучшего понимания тех ограничений, подстерегающих нас на пути поиска классов.
Существительные в документе с требованиями покрывают некоторое множество классов будущего проекта, но они также включают слишком много "
В примере с лифтовой системой ? Может быть, да, может быть - нет. Возможно, что единственным свойством дверей лифта является их способность открываться и закрываться. Тогда проще включить это свойство в виде соответствующего запроса и команды в класс :
door_open: BOOLEAN;
close_door is
...
ensure
not door_open
end;
open_door is
...
ensure
door_open
end
В другом варианте понятие двери может заслуживать отдельного класса. Единственной реальной основой является здесь теория АТД. Вот вопрос, на который действительно следует ответить:
Является ли " |
Только наша интуиция и опыт проектировщика даст нам правильный ответ. Грамматические правила анализа документа требований малосодержательны. Вместо них следует обращаться к теории АТД, помогающей задавать правильные вопросы заказчикам и будущим пользователям системы.
Мы уже встречались (см. лекцию 3) с подобной ситуацией при проектировании механизма откатов и возвратов. Речь шла о понятии commands и более общем понятии operation, включающем запросы, подобные Undo. Оба слова фигурировали в документе требований, однако, только COMMAND приводил к абстракции данных - важнейшему классу проекта.
Еще одним примером существительного в примере с лифтом является слово . В отличие от дверей с их единственной операцией, понятие этажа является разумным АТД, однако этого мало, чтобы появился класс .
Причина проста: в данном случае для целей лифтовой системы вполне достаточно представлять этажи целыми числами - их номерами, так расстояние между этажами может быть выражено простой разностью целых чисел.
Если, однако, этажи имеют свойства, не отображаемые номерами, тогда может потребоваться класс . Например, некоторые этажи могут иметь специальные права доступа, разрешающие их посещение только избранным особам, и тогда класс может включать свойство:
rights: SET [AUTHORIZATION]
и связанные с ним процедуры. Но и в этом случае нет полной определенности. Возможно, стоит вместо создания класса включить в некоторый другой класс массив:
floor_rights: ARRAY [SET [AUTHORIZATION]]
связывающий множество значений AUTHORIZATION с каждым этажом, идентифицируемым его номером (см. У4.1).
Еще одним аргументом в пользу создания независимого класса могла бы послужить возможность ограничения доступных операций над классом. Так операции вычитания и сравнения этажей должны быть доступными, а сложение и умножение лишены смысла и должны быть недоступны. Такой класс мог быть представлен как наследник класса INTEGER.
Это обсуждение снова приводит нас к теории АТД. Класс не должен представлять физические "объекты" в наивном смысле. Он должен описывать абстрактный тип данных - множество программных объектов, характеризуемых хорошо определенными операциями и их формальными свойствами. Типы реальных объектов могут и не иметь двойников в программном мире - классов. Когда решается вопрос - стать ли некоторому понятию классом, только АТД является правильным критерием, позволяя сказать, соответствует ли данное понятие классу программной системы или оно покрывается уже существующими классами.
"PERSON может включать такие компоненты, как mother и father, но для системы уплаты налогов они не нужны, здесь личность выступает сама по себе, подобно сироте.
Если все операции и свойства некоторого типа не связаны с целями системы или покрываются другими классами, то сам тип не должен рассматриваться как самостоятельный класс.
Подчеркивание существительных может не только приводить к понятиям, не создающим классов, но и к пропуску понятий, которые должны быть классами. Есть, по меньшей мере, три причины возникновения таких ситуаций.
Напомню, мы анализируем ограничения подхода "подчеркивания существительных" лишь для лучшего понимания процесса поиска классов. |
Первой причиной пропуска классов являются гибкость и неоднозначность естественного языка - те самые качества, благодаря которым он применим в самых широких областях - от ораторских речей и романов до любовных писем. Но эти же качества становятся недостатком при написании сухой и строгой технической документации. Предположим, что наш документ с требованиями к лифтовой системе содержит предложение:
Запись базы данных должна создаваться всякий раз, когда лифт перемещается от одного этажа к другому (A database record must be created every time the |
Существительное "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 |
Здесь "move" из глагола переходит в разряд существительных, претендуя на класс в соответствии с грамматическим критерием. Угрозы и абсурдность подхода, основанного на анализе документа, написанного на естественном языке, очевидны. Такое серьезное дело, как проектирование системы, в частности ее модульная структура, не может зависеть от причуд стиля и настроения автора документа.
Другая важная причина в пропуске критически важных абстракций состоит в том, что они могут не выводиться непосредственно из документа с требованиями. Примерами подобных ситуаций изобилует данная книга. Вполне возможно, что в документе, определяющем требования к системе, управляемой панелями (см. лекцию 2) ни слова нет о понятиях состояние или приложение (State, Application), задающих ключевые абстракции нашего заключительного проекта. Ранее уже отмечалось, что некоторые понятия внешнего мира могут не иметь двойников среди классов системы ПО. Имеет место и обратная ситуация: классы ПО могут не соответствовать никаким объектам внешнего мира. Аналогично, если автор требований к текстовому редактору, включающему откаты, написал: "система должна поддерживать вставку и удаление строк" ( the system must support line insertion and deletion ), то нам повезло, и мы обратим внимание на существительные insertion и deletion. Но необходимость этих свойств точно также должна следовать из предложения в форме:
Редактор должен позволять пользователям вставлять и удалять строки в текущей |
Наивный разработчик в этом тексте может обратить внимание на тривиальные понятия курсора и позиции, пропустив абстракции команд: вставка и удаление строк.
Третья главная причина пропуска классов характерна для любого метода, использующего документ с требованиями как основу анализа, поскольку такая стратегия не учитывает повторного использования. Удивительно, но литература по ОО-анализу (см. лекцию 9) исходит из традиционного взгляда на разработку - все начинается с документа с техническими требованиями на систему и движется к поиску решения проблемы, описанной в документе. Один из главных уроков объектной технологии как раз состоит в том, что не существует четко выраженного различия между постановкой проблемы и ее решением. Существующее ПО может и должно влиять на новые разработки.
Когда ОО-разработчик сталкивается с новым программным проектом, он не должен смотреть на документ с требованиями как на альфу и омегу мудрости по данной проблеме - он должен сочетать его со знанием предыдущей разработки и доступных программных библиотек. При необходимости он может предложить обновления документа, облегчающие конструирование системы, например, удалив свойство, представляющее ограниченный интерес для пользователей, но кардинально упрощающее систему, возможно, в интересах повторного использования. Подходящие абстракции, скорее всего, находятся в существующем ПО, а не в документе с требованиями для нового проекта.
Классы COMMAND и HISTORY_LOG из примера, посвященного откатам, являются в этом отношении типичными. Способ нахождения подходящих абстракций для этой проблемы состоит не в том, чтобы сушить мозги над документом с требованиями к текстовому редактору. Это может быть процесс интеллектуального озарения ("Эврика", для которого не существует рецептов), или кто-то до нас уже нашел решение, и нам остается повторно использовать его абстракции. Конечно, можно повторно использовать и существующую реализацию, если она доступна как часть библиотеки, в этом случае вся работа по анализу, проектированию и реализации уже была бы сделана для нас.
Чтобы что-то изобрести, нужны двое. Один находит варианты, другой отбирает, обнаруживает, что является важным в той массе, которую представил первый. В том, что мы называем гением, значительно меньшая доля от первого, чем от второго, отбирающего нужное из того, что разложено перед ним. Поль Валери (процитировано в [Hadamard 1945])
Помимо прямых уроков это обсуждение ведет к более тонким следствиям.
Простые уроки звучали неоднократно: не слишком полагаться на документ с требованиями, не доверять грамматическим критериям.
Менее очевидный урок вытекает из обзора
В книгах по ОО-анализу и проектированию, которые мне довелось читать, довольно мало рассуждений по этому вопросу. Это удивительно, поскольку в практике консультирования ОО-проектов, особенно в командах новичков, я обнаруживал, что исключение плохих идей не менее важно, чем нахождение хороших.
Это может быть даже более важно. Как правило, идей по поводу классов (обычно предлагаемых в виде объектов) хватало с избытком. Проблемой было поставить плотину на пути этого потока. Хотя некоторые важные классы пропускались, значительное большее количество отвергалось по результатам анализа.
Так что следует расширить рамки названия, вынесенного в заголовок этой лекции. Термин "Как найти классы?" означает две вещи: поиск абстракций, подходящих на роль кандидатов в классы, и исключение из них неадекватных задаче нашей системы. Эти две задачи не следует рассматривать как последовательные, - они постоянно перемешиваются. Подобно садовнику, ОО-разработчик должен постоянно высаживать новые растения и выпалывать плохие.
Принцип Выявления класса Выявление класса - это двойственный процесс: генерирование кандидатов, их отбраковка. |
Остаток этой лекции посвящен изучению составляющих этого процесса.
Наш поиск предпочтительнее начать с процесса отбраковки кандидатов. Давайте рассмотрим типичные признаки, указывающие на плохой выбор. Поскольку проектирование не является строго формализованной дисциплиной, не следует рассматривать эти признаки как доказательство - в конкретной ситуации, несмотря на признаки, предложенное решение может быть вполне законным. В терминах предыдущей лекции эти признаки не являются "абсолютно отрицательными", но "рекомендательно отрицательными" - сигналами опасности, предупреждающими о присутствии подозрительных образцов, требующих тщательного рассмотрения.
Большинство из сигналов опасности, обсуждаемых ниже, указывают на общую и наиболее опасную ошибку, одновременно и наиболее очевидную - проектирование класса, которого нет.
При ОО-конструировании модули строятся вокруг типов объектов, а не функций. В этом ключ к преимуществам, открываемым при расширяемости системы и ее повторном использовании. Но новички склонны попадать в наиболее очевидную ловушку, называя классом то, что в действительности является функцией (подпрограммой). Записав модуль в виде class... feature ... end, еще не означает появления настоящего класса, это просто программа, скрывающаяся под маской класса.
Этого Большого Заблуждения (Grand
В формальных и неформальных обсуждениях архитектуры проекта часто задается вопрос о роли некоторого класса. И часто можно слышать в ответ: " Этот класс печатает результаты " или " Класс разбирает вход " - варианты общего ответа " Этот класс делает...".
Такой ответ обычно указывает на изъяны в проекте. Класс не должен делать одну вещь, он должен предлагать несколько служб в виде компонентов над объектами некоторого типа. Если же он выполняет одну работу, то, скорее всего, имеет место "Большое Заблуждение".
Вполне вероятно, что ошибка не в самом классе, а способе его описания - использовании операционной фразеологии. Но все-таки в этой ситуации лучше провести проверку класса.
Предположим, что в процессе проектирования появились классы с такими именами, как ( РАЗОБРАТЬ ) или PRINT ( ПЕЧАТАТЬ ) - глагол в императивной форме. Это должно насторожить, не делает ли класс одну вещь и, следовательно, не должен быть классом.
Возможно, вы найдете, что с классом все в порядке, но тогда имя его выбрано неудачно. Вот "абсолютно положительное" правило:
Правило Имен класса Имя класса всегда должно быть либо: |
Хотя подобно любым другим правилам, относящимся к стилю, это дело соглашения, оно помогает поддерживать принцип: каждый класс представляет абстракцию данных.
Первая форма - существительные - покрывает большинство важнейших случаев. Существительное может появляться само по себе, например TREE, или с квалифицирующими словами - LINKED_LIST, квалифицируемое прилагательным, LINE_DELETION, квалифицируемое другим существительным.
Вторая форма возникает в специфических случаях - классах, описывающих структурное свойство, как, например, библиотечный класс , описывающий объекты с заданным ABLE. Так, в системе, учитывающей PLAYER может быть наследником класса . В
Единственный случай, который может показаться исключением из правила, задает командные классы, так как они введены в LINE_DELETION и WORD_CHANGE, а не DELETE_LINE и REPLACE_WORD ( Удаление_Строки, а не Удалить_Строку ).
Английский язык предоставляет большую гибкость, чем многие другие языки, где грамматическая категория - это скорее дело веры, чем факта, и почти каждый глагол может быть и существительным. При использовании английского языка в программных именах легче придерживаться этого правила и строить короткие имена. Вы можете назвать класс IMPORT, рассматривая это имя как существительное, а не как глагол. В других языках вам пришлось бы строить более тяжеловесное имя, нечто вроде IMPORTATION. Но здесь не должно быть надувательства: класс IMPORT должен покрывать абстракцию данных - "объекты, подлежащие импорту", а не быть командным классом, задающим единственную операцию импорта.
Заметьте разницу между Правилом Имен Класса и подходом "подчеркивания существительных", обсуждаемым в начале лекции. При подчеркивании формальный грамматический критерий применяется к неформальному тексту - документу с требованиями, потому ценность его сомнительна. Наше же Правило Имен применяет тот же критерий к формальному тексту. |
Типичным симптомом Большого Заблуждения является эффективный класс, содержащий одну, часто весьма важную, экспортируемую подпрограмму, возможно вызывающую несколько внутренних подпрограмм. Такой класс - вероятно, результат функциональной, а не ОО-декомпозиции.
Исключением являются объекты, вполне законно представляющие абстрактные действия, например команды интерактивной системы (см. лекцию 3), или то, что в не объектном подходе представляло бы функцию, передаваемую в качестве аргумента другой функции. Но примеры, приведенные в предыдущих обсуждениях, достаточно ясно показывают, что даже в этих случаях у класса может быть несколько полезных компонентов. Так для класса, задающего подынтегральную функцию, может существовать не только компонент item, возвращающий значение функции. Другие компоненты этого класса могут задавать максимум и минимум функции на некотором интервале, ее производную. Даже если класс не содержит этих компонентов, знание того, что они могут появиться позднее, заставляет нас считать, что мы имеем дело с настоящей абстракцией.
При применении однопрограммного правила следует рассматривать все компоненты класса: те, которые введены в самом классе, и те, что введены в
Упомянув таксоманию, следует отметить еще одну общую ошибку новичков - преждевременное построение
Наследование занимает центральное место в ОО-методе, так что хорошая структура наследования, или более аккуратно, хорошая модульная структура, включающая
Единственным четким исключением является ситуация, в которой для области приложения разработана и широко применяется таксономия, как это имеет место в некоторых областях науки. Тогда соответствующие абстракции будут появляться вместе со структурой наследования.
В других случаях к созданию
Мне приходилось видеть людей, начинавших с создания классов SAN_FRANCISCO и HOUSTON наследников класса CITY, когда нужно было промоделировать ситуацию с одним классом CITY и несколькими его экземплярами - объектами периода выполнения.
Иногда можно обнаружить классы, вообще не имеющие команд или допускающие только запросы (доступ к объектам только в режиме чтения), но не команды (процедуры, модифицирующие объекты). Такие классы являются эквивалентами записей языка Pascal или структур
Прежде всего, рассмотрим три случая, когда такой класс не является результатом неподходящего проектирования:
INTEGER, REAL и DOUBLE следует математическим традициям - она не модифицирует значение, но, получив x и y, вырабатывает значение: x + y. В спецификации АТД такие функции характеризуются как командные функции.Во всех этих случаях абстракции обнаруживаются довольно просто, так что не трудно идентифицировать оставшиеся два случая, которые реально могут указывать на дефекты проектирования.
Теперь вернемся к подозрительным случаям. В первом из них появление класса оправдано, команды классу нужны - проектировщик просто забыл обеспечить механизм модификации объектов. Простая техника контрольной ведомости (checklist) позволяет избежать подобных ошибок (см. лекцию 5).
Во втором случае существование класса не оправдано. Он не является настоящей абстракцией данных, представляет пассивную информацию, которая может быть задана структурой, подобной списку или массиву, добавленной в виде атрибута к какому-либо классу. Этот случай встречается, когда разработчик пишет класс, исходя из системы, ранее написанной на Pascal или
Следует внимательно исследовать подобную ситуацию, чтобы понять, имеем ли мы дело с настоящим классом, теперь или в будущем. Если ответ неясен, то может быть лучше оставить класс, даже с риском неоправданных расходов. Класс влечет некоторую потерю производительности, поскольку приводит к динамическому созданию объектов, для них может потребоваться больше памяти, чем в случае простого массива. Но, если класс действительно понадобится, и он не был введен своевременно, то позднейшие затраты на адаптацию могут быть велики.
Подобная история имела место при разработке ISE компилятора. Для внутренних потребностей идентификации классов решено было использовать целые числа, а не специальные объекты. Все это хорошо работало несколько лет. Но потом схема идентификации усложнилась, в частности пришлось перенумеровывать классы при слиянии нескольких систем в одну. Так что пришлось вводить класс |
Еще один признак несовершенного проектирования состоит в том, что в одном классе смешиваются две или более абстракций.
В ранней версии библиотеки |
Мэйлир Пейдж Джонс ([Page-Jones 1995]) использует термин соразвитие (connascence), означающий совместное рождение и совместный рост. Для классов соразвитие означает отношение, существующее между двумя тесно связанными компонентами, когда изменение одного влечет одновременное изменение другого. Как он указывает, следует минимизировать соразвитие между классами библиотеки, но компоненты, появляющиеся внутри данного класса, все должны быть связаны одной и той же четко определенной абстракцией.
Этот факт заслуживает отдельного методологического правила, сформулированного в "положительной" форме:
Принцип Согласованности класса Все компоненты класса должны принадлежать одной, хорошо определенной абстракции. |
Этот обзор возможных ошибок по контрасту проявляет характерные черты идеального класса. Вот его некоторые типичные свойства:
Этот список описывает множество неформальных целей, не являясь строгим правилом. Легитимный класс может обладать лишь одним из перечисленных свойств. Большинство из примеров, играющих важную роль в этой книге, - начиная от классов LIST и QUEUE до BUFFER, ACCOUNT, COMMAND, STATE, INTEGER, , и многих других, - обладают всеми этими свойствами.
Давайте теперь обратимся к положительной части нашего обсуждения - практическим
Можно выделить три категории классов: классы анализа, классы проектирования, классы реализации. Это деление не является ни абсолютным, ни строгим (например, кто-то может привести аргументы в поддержку того, что отложенный класс LIST принадлежит к любой из трех категорий), но такое деление удобно как общее руководство.
Классы анализа описывают абстракцию данных, непосредственно выводимую из модели внешней системы. Типичными примерами являются классы в системе управления полетами, в системе обработки документов, PART в системе управления запасами.
Классы реализации описывают абстракцию данных, введенную, исходя из внутренних потребностей алгоритмов системы, например LINKED_LIST или ARRAY.
Классы проектирования описывают архитектурный выбор. Примерами могут служить класс COMMAND в системе, реализующей откаты, класс STATE в системе, управляемой панелями. Подобно классам реализации, классы проектирования принадлежат пространству решений, в то время как классы анализа принадлежат пространству проблемной области. Но подобно классам анализа и в отличие от классов реализации они описывают концепции высокого уровня.
Когда мы научимся получать классы всех трех категорий, мы обнаружим, что наиболее трудно идентифицировать классы проектирования, требующие архитектурной интуиции. Заметьте, сложность в выявлении этих классов не означает, что их трудно построить. Сложностью построения скорее отличаются классы реализации, если только мы не используем готовую библиотеку.
Давайте начнем с классов анализа, моделирующих внешние объекты.
Мы используем ПО для получения ответов на некоторые вопросы о внешнем мире, для взаимодействия с этим миром, для создания новых сущностей этого мира. В каждом случае ПО должно основываться на некоторой модели мира, на законах физики или биологии в научных программах, на синтаксисе и семантике языка программирования при построении компилятора, налоговых постановлений в системе расчета налогов.
В нашем разговоре мы избегаем термина "реальный мир", вводящего в заблуждение, поскольку ПО не менее реально, чем что-либо другое. Миры, которыми интересуются при создании ПО, зачастую искусственны, как, например, миры математика. Мы должны говорить о внешнем мире в противовес внутреннему миру ПО. |
Любая программная система основывается на операционной модели некоторых аспектов внешнего мира. Операционной, поскольку используется для генерирования практических результатов, а иногда и для создания обратной связи, возвращая результаты во внешний мир. Модель, потому что любая полезная система должна следовать определенной интерпретации некоторых феноменов этого мира.
Эта точка зрения наиболее явно проявляется в такой области как моделирование (simulation). Неслучайно, что первый ОО-язык программирования
В широком смысле построение любой программной системы является моделированием. Исходя из операционной модели, ОО-конструирование ПО использует в качестве первых абстракций некоторые типы, непосредственно выводимые из анализа объектов в непрограммном смысле этого термина, объектов внешнего мира: датчиков, устройств, самолетов, служащих, банковских счетов, интегрируемых функций.
Эти примеры рисуют лишь часть общей картины. Как заметили Валден и Нерсон ([Walden 1995]) в представлении метода B.O.N: "Класс, описывающий автомобиль, не более осязаем, чем тот, который моделирует удовлетворенность служащих своей работой" Следует всегда иметь в виду этот комментарий при поиске внешних классов - они могут быть довольно абстрактными: |
Будучи материальными или абстрактными, внешние классы, используемые специалистами, всегда дают хороший шанс породить полезные внутренние классы. Но ключом остается абстракция. Хотя и желательно добиваться соответствия классов анализа концепциям проблемной области, но не эта близость делает класс удачным. Первая версия нашей системы, управляемой панелями, драматично это показала, - она была прекрасной моделью, построенной по образцу внешней системы, но оказалась ужасной с позиций инженерии программ. Хороший внешний класс должен базироваться на абстрактных концепциях проблемной области, характеризуемых внешними свойствами долговременной значимости.
Для ОО-разработчиков предварительно существующие абстракции являются драгоценными - они дают некоторые из фундаментальных классов системы, но, отметим еще раз, являются объектами для прополки.
Классы реализации описывают структуры, создаваемые разработчиками по внутренним соображениям, диктуемым реализацией системы. Хотя в литературе по
Плохая новость - классы реализации трудно строить. Хорошая новость - их легко выявить. Для них может существовать хорошая библиотека, допускающая повторное использование. По крайней мере, есть хорошая литература по этой тематике. Курс "Алгоритмы и структуры данных", иногда известный как CS 2, является необходимым компонентом образования в области информатики. Хотя большинство из существующих учебников явно не используют ОО-подход, многие следуют стилю АТД. Преобразование в классы в этом случае довольно прямолинейно и естественно.
В настоящее время некоторые учебники начали применять ОО-подход при изложении традиционных тем CS 2.
Каждый разработчик, независимо от того, проходил ли он этот курс, должен держать на своей книжной полке учебники по структурам данных и алгоритмам и обращаться к ним довольно часто. Легко ошибиться и выбрать неверное представление или неэффективный алгоритм, например, применить
Помимо учебников и опыта лучшим вариантом для классов реализации являются библиотеки повторного использования.
В традиционных учебниках естественно описываются эффективные (полностью реализованные) классы. На практике ценность большинства классов реализации, особенно, когда предполагается их повторное использование, связана с QUEUE, описывающего абстрактные концепции.
"Отложенный класс реализации" - это не нелепица (QUEUE, хотя и абстрактны, но помогают построить таксономию с согласованными вариациями структур реализации, отводя каждому классу точное место в общей схеме.
В одной из своих книг ([M 1993]) я описал "Линнеевскую" таксономию фундаментальных структур информатики, в основе которой лежат отложенные классы, характеризующие принципиальные виды структур данных, используемых при разработке ПО.
Классы проектирования представляют архитектурные абстракции, помогающие создавать элегантные расширяемые программные структуры. Хорошими примерами являются классы: STATE, APPLICATION, COMMAND, HISTORY_LIST,
Хотя, как отмечалось, нет уверенного способа найти классы проектирования, дадим несколько полезных советов - это лучше, чем ничего.
Несколько
Совет искать то, что доступно, применим не только к библиотечным классам. Когда вы пишете приложения, вы аккумулируете классы, облегчающие при правильном проектировании дальнейшие разработки.
Не все повторно используемое ПО таким и рождалось. Зачастую первая версия класса рождается для удовлетворения текущих потребностей. Однако затем, через некоторое время после разработки выясняется, что стоит затратить усилия на то, чтобы сделать класс общим и более устойчивым, улучшить документацию, добавить утверждения. Это отличается от ситуации, когда класс создается с самого начала как повторно используемый, но не менее плодотворно. Включенные в реальную систему классы проходят первую проверку на повторное использование, называемую используемость, - они служат, по крайней мере, одной полезной цели.
При обнаружении потенциально полезного класса иногда обнаруживается, что он не в полной мере отвечает потребностям и требует адаптации.
Если у класса нет дефектов, требующих их устранения в оригинале, то обычно предпочтительнее оставить класс в целости и сохранности, заботясь о его клиентах в полном соответствии с принципом Открыт-Закрыт. Вместо этого можно использовать наследование и переопределение, настраивающее класс (потомка) на новые потребности.
Эта техника (см. лекцию 10), которая еще будет изучаться в деталях под именем вариационное наследование (variation inheritance), предполагает, что новый класс задает вариант той же абстракции, что и оригинал. При подходящем использовании она представляет наиболее значительный вклад в Метод, позволяя разрешить проблему reuse-redo - сочетание повторного использования с расширяемостью.
Критиковать проще, чем создавать. Один из способов обучения проектированию состоит в анализе существующих проектов. В частности, когда некоторое множество классов предлагается для решения определенной проблемы, следует проанализировать их в соответствии с критериями и принципами модульности, представленными в лекции 3 курса "Основы объектно-ориентированного программирования", - составляют ли они автономные, согласованные модули со строго ограниченными
Важный критерий исследовался (см. лекцию 2) в примере системы, управляемой панелями, - потоки данных. Мы видели тогда, как важно при рассмотрении структуры класса кандидата анализировать STATE, важного источника абстракции.
Конечно, предпочтительнее найти правильные классы с самого начала. При позднем, апостериорном обнаружении следует найти время на анализ причин, почему важная абстракция была пропущена, чтобы извлечь уроки на будущее.
Пример
Вот примеры этого процесса, начиная с языков программирования и заканчивая методами анализа и проектирования.
Программы
Программы Pascal и C используют записи, известные в C как структуры. Они могут также соответствовать классам при условии выявления операций над данными записей. Если это не так, то запись будет представлена атрибутами некоторого класса.
Структуры
При рассмотрении моделей, основанных на понятиях "сущность-отношение", сущности ("entities") часто служат основой для построения классов.
При проектировании потоков данных (dataflow) немногое может быть непосредственно использовано для ОО-целей, но иногда "хранилища" (stores) могут приводить к нужным абстракциям.
"Хранилища" несут общую полезную идею. Иногда большая часть информации традиционных систем находятся не в их программном тексте, а в структуре используемых файлов.
Для всякого с опытом Unix эта идея достаточно ясна: основная документация содержит описание не столько специфических команд, а описание ключевых файлов и их форматов: passwd для паролей, printcap для свойств принтера, termcap или terminfo для свойств терминала. Эти файлы можно характеризовать как абстракции данных без абстракции - документированные на совершенно конкретном уровне (" Каждый вход в printcap файле описывает принтер и представляет строку, состоящую из полей, разделенных символом двоеточия " и т. д.). Файлы задают важные типы данных, доступные через хорошо определенные примитивы с ассоциированными свойствами и условиями использования. При переходе к ОО-подходу такие файлы должны играть центральную роль.
Подобное наблюдение применимо ко многим программам, использующим файлы. Однажды я консультировал менеджера программной системы, убежденного, что его система - коллекция
Ивар Якобсон ([Jacobson 1992]) пропагандирует использование ситуаций для выявления классов. Ситуации, называемые также сценариями (scenario) или трассами, описываются как
полный набор событий, инициированных пользователем или системой, и взаимодействий между пользователем и системой.
В телефонной системе ситуация, например "вызов, инициированный заказчиком", приводит к последовательности событий: заказчик поднимает трубку телефона, системе посылается сигнал идентификации, система вырабатывает гудок и так далее.
Использование ситуаций не самый лучший способ нахождения классов. Здесь возникает несколько рискованных моментов:
a, затем b "; вместо этого следует задавать вопрос: "Какие операции доступны для экземпляров абстракции A, и каковы ограничения на эти операции?" По-настоящему фундаментальные свойства, отражающие последовательность выполнения операций, задаются ограничениями высокого уровня. Утверждение, что для стека число операций pop не должно превосходить число операций push, можно выразить в более абстрактной форме, используя пред и постусловия этих операций. Менее Практические следствия очевидны:
Принцип использования сценариев За исключением очень опытных команд разработчиков (имеющих опыт создания нескольких систем, содержащих несколько тысяч классов в чистом ОО-языке) не следует основываться на сценариях как средстве ОО-анализа и проектирования. |
Из этого принципа не следует, что сценарии являются бесполезной концепцией. Они являются потенциально значимыми, но их роль в ОО-конструировании ПО неверно понимается. Они являются не средством анализа, а инструментом проверки правильности (validation). Если, как и должно быть, в вашей команде разработчиков имеется независимая группа тестирования, то сценарии будут являться их инструментом, позволяющим проверить, способна ли система корректно выполнять различные сценарии, как они виделись пользователям. В некоторых случаях может оказаться, что система поддерживает другой сценарий, но дающий тот же или лучший результат. Это, конечно же, вполне приемлемо.
Еще одно применение сценариев связано с заключительными аспектами реализации - система может включать специальные программы для запуска типичных сценариев. Такие программы зачастую задают некоторый вид абстрактного поведения, описывая общую схему, которая может быть переопределена различными способами. В книге [Jacobson 1992] вводится понятие абстрактного сценария, что в объектной терминологии называется классом поведения.
В этих двух ролях - механизме проверки и управления реализацией - использование сценариев дает несомненную пользу. Но в объектной технологии они не являются полезным средством анализа или проектирования. Разработчикам следует концентрироваться на абстракциях, а не на том, в каком порядке выполняются операции.
Для полноты картины упомянем идею, рассматриваемую иногда как метод нахождения классов. Карты КОС ( Класс, Ответственность, Сотрудничество ) или
Не изобретать классы, а получить их из библиотеки - вот простейший и наиболее продуктивный способ нахождения классов. Помимо прочего гарантируется проверка работоспособности, основанная на опыте предыдущих пользователей библиотеки.
Начиная с этапа анализа, следует применять разработку снизу вверх. Подход, сосредоточенный исключительно на документе с требованиями и запросами пользователей (как это делается в
Довольно часто, когда мы говорим о нахождении классов, подразумевается их изобретение (devising). В объектной технологии с ростом качества библиотек и осознания идей повторного использования приобретает смысл именно поиск (finding) классов.
Жил да был в стране
Будучи подвергнутым публичной епитимии Схемом-Блоком - аббатом Священного Порядка Стрелок и Пузырей, - он решил, что тому известна истина и настал конец его поискам. С трудом он отыскал пещеру Схема, вошел в нее и увидел Схема, погруженного в поиски вечного различия между Классами и Объектами. Осознав, что не здесь обретается истина, без единого вопроса он покинул пещеру и отправился в дальнейшие странствия.
Однажды он нечаянно подслушал разговор двух людей, один из которых занимался вталкиванием какого-то груза в тележку, а другой немедленно его выталкивал. Они шептались о неком старце, знающем секрет классов. Молодой человек решил найти этого великого Мастера. Много дорог он прошел, на многие горы он поднимался, многие реки пересекал, пока не нашел убежище Мастера. Поиски продолжались так долго, что он давно уже перестал быть молодым человеком. Но, как всем пилигримам, ему еще предстояло пройти очищение в течение тридцати трех месяцев, прежде чем он был допущен к объекту своих поисков.
Наконец, в один из темных зимних дней, когда снег покрыл все окружающие горные вершины, он вошел в комнату Мастера. С бьющимся сердцем, пересохшим от волнения голосом он задал свой сакраментальный вопрос: "Мастер, как мне найти классы?"
Мудрец склонил свою голову и ответил медленно и спокойно: "Возвращайся назад, откуда пришел. Классы уже найдены".
Оглушенный, он и не заметил, как слуги Мастера выводили его прочь. "Мастер", - теперь он почти кричал, - "пожалуйста, еще только один вопрос. Как называется эта история?" Старый Учитель покачал головой: "Разве ты еще не понял? Это сказка о повторном использовании".
Мало-помалу идеи, обсуждаемые в этой лекции, в совокупности дают то, что не слишком претенциозно можно называть методом получения классов при конструировании ПО (напомним, метод - это способ породить, воспитать, проложить дорогу, создать нечто стоящее).
Идентификация класса требует двух неразрывно связанных видов деятельности - выявления множества кандидатов в классы и отбора среди них действительно нужных. В двух следующих таблицах подводятся итоги.
Прежде всего начнем с источников классов.
| Источник идей | Что ищется |
|---|---|
| Существующие библиотеки | |
| Документ требований | |
| Обсуждения с заказчиками и будущими пользователями | |
| Документация (руководства пользователей) для других систем в той же проблемной области, например от конкурентов | |
| Не ОО-системы и их описания | |
| Обсуждения с опытными проектировщиками | |
| Литература по алгоритмам и структурам данных | |
| Литература по ОО-проектированию |
Рассмотрим теперь критерии, позволяющие более внимательно исследовать классы и, возможно, отвергнуть часть из них.
| Сигналы опасности | Причина подозрительности |
|---|---|
| Класс с отглагольным именем (произведенным от глагола, инфинитив или императив) | Может быть простой подпрограммой, а не классом |
| Полностью эффективный класс с одной экспортируемой подпрограммой | Может быть простой подпрограммой, а не классом. |
| Класс, описанный как "выполняющий нечто" | Может не быть подходящей абстракцией. |
| Класс без подпрограмм | Может быть важной частью информации, но не АТД. Может быть АТД, для которого просто забыли указать операции. |
| Класс, введенный без компонентов или с небольшим их числом (но наследующий компоненты своих родителей) | Может быть результатом "таксомании". |
| Класс, покрывающий несколько абстракций | Должен быть разделен на несколько классов, по одному на каждую абстракцию. |
Советы по использованию существительных документа требований в качестве начальной точки нахождения классов стали популярными благодаря Гради Бучу [Booch 1986], который заимствовал эту идею из ранней статьи Эббота [Abbot ACM, 26, 1983]. Дальнейшие рекомендации появились в [Wirfs-Brock 1990].
Статья по формальным спецификациям [M 1985a] анализировала проблемы, связанные с естественным языком документа требований. В ней изучались описания, широко используемые в литературе по верификации программ, и была введена таксономия возникающих дефектов: шум, двусмысленность, противоречие, излишняя спецификация, ссылки вперед. В ней обсуждалось, как формальные спецификации могут справиться с некоторыми из проблем.
[Walden 1995] рассмотрел полезные рекомендации для идентификации классов.
В приложении B [Page-Jones 1995] перечислены "симптомы" отбраковки кандидатов, уведомляющие проектировщиков об опасных сигналах, подобно тем, что были рассмотрены в данной лекции.
[Ong 1993] описывает инструментарий преобразования не ОО-программ, главным образом COMMON -блоки.
Книги по типовым структурам данных являются надежным источником классов реализации, включая известный трехтомник Кнута [Knuth 1981, Knuth 1973], [Aho 1974, Aho 1983].
[Gore 1996] представляет фундаментальные структуры данных и алгоритмы полностью в ОО-манере.
Источники классов проектирования приведены в [
Определите класс как наследника INTEGER, ограничив применимые операции.
Даниел Холберт и Патрик О-Брайен обсуждали проблему, возникающую при проектировании окружения разработки ПО:
Рассмотрим свойство inspector, используемое для отображения информации об объекте в окне отладки. Для разных объектов нужны разные инспекторы. Например, информация о точке может быть выведена в простом формате, а о большом идвумерном массиве может потребовать вертикального и горизонтального скроллинга. Прежде всего следует решить, где описать поведение инспектора - в классе, связанном с инспектируемым объектом, или в отдельном классе?
Отвечая на это вопрос, рассмотрите все за и против каждого варианта. Заметьте, могут оказаться полезными результаты обсуждения, приведенные в последующих лекциях, посвященных наследованию.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.