Опубликован: 23.10.2005 | Доступ: свободный | Студентов: 4089 / 201 | Оценка: 4.44 / 4.19 | Длительность: 33:04:00
Специальности: Программист
Лекция 4:

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

< Лекция 3 || Лекция 4: 123456 || Лекция 5 >

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

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

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

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

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

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

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

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

Эта техника (см. "лекцию 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 инструментарием. Но его технический вклад в процесс проектирования неясен.

< Лекция 3 || Лекция 4: 123456 || Лекция 5 >