Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 176
Текст из файла (страница 176)
Активно выискивайте общности. Обобщение н классификация — это абстрактная деятельность, требующая проникновения в самую суть вещей для получения полезных и надежных результатов. Общий базовый класс должен отражать самую общую концепцию, а не просто похожую концепцию с минимальными данными для представления. 826 Глава 23. Общий взгляд на разработку программ. Проектирование Отметим, что классификации должны быть подвергнуты 'те аспекты концепций, которые мы моделируем в нашей системе, а не аспекты, имеющие значение в других случаях.
Например, в математике окружность — это частный случай эллипса, но в большинстве программ окружность не моделируют в свете ее связи с эллипсом, и эллипс не представляют в виде типа, производного от типа окружности. Тут такие аргументы, что мол так обстоит дело в математике, не работают. Для большинства программ ключевое свойство окружности — это равноудаленность ее точек от центра окружности. Это свойство должно поддерживаться всеми операциями типа окружности (то есть это свойство является инвариантом; Э24.3.7.1). С другой стороны, эллипс характеризуется наличием двух фокальных точек, которые во многих программах можно изменять независимым образом. Если эти точки совмещаются, эллипс становится похож на окружность, но это не окружность, поскольку его операции не сохраняют инварианта окружности.
В большинстве систем это отличие будет отражено в разных наборах операций для окружности и эллипса, так что один набор не является подмножеством другого. Мы не просто определяем набор классов и отношений между ними и используем их в окончательной системе — мы на самом деле создаем первоначальный набор классов и их взаимоотношений с тем, чтобы многократно совершенствовать этот набор 523.4.3.5) и в итоге получить универсальный, гибкий и стабильный набор, помогающий на всех будущих стадиях эволюции системы.
Наилучшим инструментом для поиска и выявления концепций/классов является обычная классная доска. Уточнение начального набора лучше всего выполнять в процессе дискуссий со специалистами в предметной области задачи и некоторыми друзьями. Дискуссии нужны для выявления первоначального словаря задачи и базовой системы концепций. Редкие люди могут делать это в одиночку. Дальше с целью уточнения и улучшения классов из первоначального набора стоит попытаться смоделировать систему, используя проектировщиков в роли классов. Это может выявить абсурдность некоторых первоначальных идей, стимулировать предложение альтернатив и создать согласованное понимание системы у всех участников. Для документирования дискуссии можно применить, например, СВС-карточки (С1азз, ВеаропгйЬ!!йу, Со!!аЬогатога) [%1г(з-Вгос)г, 1990], которые так называются в соответствии с записываемой на них информацией (Класс, Ответственность, Сотрудники). Примеры использования (иве солев) — это описание частных случаев использования системы.
Вот простой пример использования телефонной системы; поднимаем трубку, набираем номер, телефон на другом конце звонит, там снимают трубку. Разработка набора примеров (частных случаев) использования чрезвычайно полезна на всех этапах разработки. На начальном этапе выявление полезных частных случаев применения нашей системы помогает нам глубже понять, что именно мы хотим разработать. На этапе проектирования они помогают нам интроспектировать систему (например, через СВС-карточки) с целью убедиться, что относительно статическое описание системы в терминах классов и объектов имеет смысл с точки зрения пользователя системы. На этапах программирования и тестирования примеры использования становятся источниками тестов.
Таким образом, примеры частных случаев использования вносят ортогональный взгляд на систему с точки зрения ее соответствия реальности. Примеры использования описывают систему как (динамическую) работающую сущность. Поэтому они могут сконцентрировать внимание проектировщика на 23.4. Процесс разработки 827 функциональных аспектах системы и отвлечь его внимание от поиска важных концепций, отображаемых в классы. Это особенно опасно в том случае, когда проектировщик имеет большой опыт в структурном анализе и недостаточный опыт в объектно-ориентированном проектировании/программировании, так что упор на частных примерах использования может привести в итоге к функциональной декомпозиции. Вообще, набор примеров использования — это не проект. Примеры использования системы должны быть уравновешены анализом структуры системы.
Команда разработчиков также может попасться в ловушку погони за полным набором примеров использования. Такие ошибки дорого обходятся. Как и в случае поиска важных сущностей и соответствующих им классов приходит время остановиться и сказать: «Хватит! Наступил момент испробовать то, что мы уже получили и посмотреть, как это работает». Применяя подходящий набор классов и подходящий набор примеров использования можно двигаться вперед, приобретая опыт и обратную связь для корректировки проекта. Всегда трудно решить, когда же нужно остановиться.
Особенно трудно остановиться тогда, когда известно, что позже все равно придется вернуться для завершения проекта. Сколько нужно примеров? В общем случае на этот вопрос ответить невозможно. Тем не менее, в конкретном проекте всегда приходит время, когда становится ясно, что большая часть стандартной функциональности системы уже отражена в текущем варианте проекта, а некоторая часть специфического поведения и обработки ошибок до некоторой степени изучена. Здесь и наступает момент, когда нужно переходить к следующим этапам проектирования и программирования. Когда вы пытаетесь оценить процент покрытия системы набором примеров использования, полезно разделить все имеющиеся примеры на первичные и вторичные.
Первичные описывают наиболее общие и нормальные действия, а вторичные — менее обычные действия и сценарии обработки ошибок. Часто говорят, что когда 80% первичных примеров и некоторое количество вторичных описаны, можно заканчивать сбор примеров, но поскольку мы не знаем, что такое 100%, то эту рекомендацию следует воспринимать как ориентировочную. Опыт и вкус сослужат здесь неоценимую пользу. Концепции, операции и взаимосвязи естественным образом вытекают из нашего понимания прикладной области задачи или появляются в процессе практической работы с существующей классовой структурой. Они составляют основу фундаментального понимания приложения. Часто они порождаются классификацией, такой как, например, «машина с раздвижной лестницей» есть частный случай пожарной машины, которая есть частный случай грузовика, который есть частный случай колесного транспортного средства.
Разделы Э23.4.3.2 и Э23.4.5 знакомят с несколькими способами взгляда на классы и классовые иерархии с точки зрения их улучшения. Остерегайтесь излишней графической инженерии. В некоторый момент вас попросят представить проект кому-нибудь в графической форме, раскрывающей структуру разрабатываемой системы. Это может оказаться для вас положительным опытом, так как заставит сосредоточить внимание на том, что действительно важно для системы и заставит представить все это в форме, понятной окружающим. Презентации можно рассматривать как ценное проектное средство.
Подготовка презентации для людей, имеющих к проекту интерес и возможность порождать конструктивные замечания, является испытанием для качества разработанных вами концепций и способности ясного изложения идей. 828 Глава 23. Общий взгляд на разработку программ. Проектирование В то же время, формальная презентация проекта также имеет и отрицательные стороны, ибо подталкивает к тому, чтобы представить проект идеальной системы, а не той, которая у вас уже есть или которую можно произвести в реальные сроки.
Когда конкурируют разные подходы, а начальство на самом деле не понимает деталей или не заботится о них, презентация превращается в ложное соревнование, в котором побеждает та команда, которая представит наиболее грандиозный прожект ради сохранения заказа для своего коллектива разработчиков. В таких случаях ясное изложение идей часто подменяется тяжеловесным жонглированием малопонятными аббревиатурами. Если вы на такой презентации представляете руководство, то вам чрезвычайно важно отличать благие пожелания от реалистичного планирования.
Визуальное качество презентации не гарантирует такого же качества у проекта. Более того, зачастую организации, сосредоточенные на качественном выполнении проектов, проигрывают тем, кто меньше беспокоится о реальном производстве эффективных программ.
Наконец, у всех систем существуют такие свойства, которые не представимы классами. Например, надежность, производительность и удобство тестирования не отвечают никаким классам программы, хотя их и можно измерить. Такие свойства систем можно закладывать в проект и измерять в процессе работы программных продуктов. Забота об этих свойствах должна красной нитью проходить через все классы, и ее можно отразить в проектировании и реализации классов и компонентов (э23.4.3). 23.4.3.2. Этап 2: определение операций Уточните классы, определив набор их операций. Конечно, невозможно полностью отделить выявление концепций и классов от определения операций над ними. Но в процессе поиска классов мы сосредотачиваемся в первую очередь на сущности концепций без упора на операции, в то время как на втором этапе уже можно сосредоточиться именно на операциях, определяя их полный и удобный набор. Действительно, трудно было бы делать это одновременно, особенно в случае параллельной разработки многих, связанных друг с другом классов.
Здесь полезными могут оказаться СКС-карточки (323.4.3.1). Для выяснения набора необходимых операций могут оказаться полезными следующие соображения: 1. Рассмотрите, как объекты классов должны создаваться (конструироваться), копироваться и уничтожаться. 2. Определите минимальный набор операций, который требует отражаемая классом концепция.
Как правило, эти операции реализуются функциями-членами (310.3). 3. Подумайте, какие операции можно добавить для удобства. Включите только несколько наиболее важных операций. Часто такие операции реализуются в виде вспомогательных функций (ае!рег~илсггат) (э! 0.3.2). 4. Подумайте, какие операции должны быть виртуальными, то есть для которых класс обеспечивает интерфейс, а реализацию предоставляет производный класс. 5.