Текст
                    С.В. Назаров
АРХИТЕКТУРА
И ПРОЕКТИРОВАНИЕ
ПРОГРАММНЫХ СИСТЕМ

НАУЧНАЯ МЫСЛЬ СЕРИЯ ОСНОВАНА В 2008 ГОДУ С.В. НАЗАРОВ АРХИТЕКТУРА И ПРОЕКТИРОВАНИЕ ПРОГРАММНЫХ СИСТЕМ МОНОГРАФИЯ 2-е издание, переработанное и дополненное Электронно- Б иблиотсчная Система znanium.com Москва ИНФРА-М 2023
УДК 004.2(075.4) ББК 32.973.26-02 Н19 Рецензенты: Л. Г. Гагарина — доктор технических наук, профессор Национального исследовательского университета «Московский институт электронной техники» (МИЭТ); А.Б. Барский — доктор технических наук, профессор Московского го- сударственного университета путей сообщения (МИИТ) Назаров С.Б. Н19 Архитектура и проектирование программных систем : моногра- фия/С. В. Назаров. — 2-е изд., перераб. и доп. — Москва: И НФРА-М, 2023. — 374 с. — (Научная мысль). — DO1 10.12737/18292. ISBN 978-5-16-011753-9 (print) ISBN 978-5-16-104150-5 (online) В монографии рассматриваются технологии и проблемы создания больших программных систем, их архитектуры и жизненного цикла. Основное внимание обращено на разработку и анализ требований, опре- деление спецификаций, методы и средства проектирования архитектуры программных систем. Уделено значительное внимание рефакторингу про- граммных систем, в том числе архитектурному рефакторингу. Для аспирантов, преподавателей технических вузов и специалистов, занимающихся разработкой программных систем. УДК 004.2(075.4) ББК 32.973.26-02 ISBN 978-5-16-011753-9 (print) ISBN 978-5-16-104150-5 (online) © Назаров С.В., 2016
Предисловие Термин «архитектура программного обеспечения» является относи- тельно новым. Являясь в настоящий момент дисциплиной без четких правил, проектирование архитектуры программных систем (ПС) — все еще смесь науки и искусства. Важность начального этапа создания ПС — определения ее архитектуры — трудно переоценить. Известно, что ошиб- ки, допущенные на начальных этапах проектирования ПС, могут свести на нет успех всего проекта. В монографии рассмотрен широкий круг вопросов, связанных с про- блемами создания больших ПС: инженерный подход к разработке ПС, становление и развитие программной инженерии, развитие технологий программирования, точки зрения широкого круга лиц, заинтересован- ных в ПС, которые могут быть объединены в архитектуре ПС. Это аргу- мент в целесообразности создания архитектуры ПС еще до этапа разра- ботки ПС. Существует большое количество технологий создания ПС, рассмат- ривающих полный жизненный цикл (ЖЦ) разработки ПС и сочетающих в себе научный подход, серьезную базу исследований и имеющих исто- рию реального использования. В предлагаемой монографии детально рассматриваются элементы ЖЦ ПС, обобщенные и сформулированные на основе анализа массы публикаций. Значительная часть монографии посвящена вопросам проектирова- ния ПС в части определения требований и целей программного продук- та. Этот материал представляет собой обобщение опубликованных мо- нографий и статей, а также собственных исследований автора. Процесс проектирования рассматривается как последовательная трансляция тре- бований, предъявляемых к ПС. Предложены формализованная схема процесса проектирования и авторский подход, который может быть ис- пользован для выбора требований, предъявляемых к системе. Даны по- становка задачи и принципы разработки требований, включая бизнес- моделирование, определение функциональных и нефункциональных требований, предъявляемых к ПС. Дан материал по вопросам разработки предварительного внешнего проекта ПС: представление и анализ требований, роль моделирования в определении требований и спецификаций, разработка ПС, управляе- мая моделями. Продолжена формальная схема проектирования приме- нительно к описанию спецификаций. Рассмотрен структурный и объект- ный подход в анализе требований и определении спецификаций, в том числе метод функционального моделирования, функциональные диа- граммы, диаграммы потоков данных и др., достаточные сведения о язы- ке UML как языке моделирования сложных систем. Дан материал по проектированию архитектуры ПС. Изложены теоре- тические основы методологии проектирования, модульно-интерфейс- ный подход и модульное программирование, вопросы оценки сложно- сти ПС, представление архитектуры ПС на основе модульно-интер фсйс- 3
ного, объектно-ориентированного и компонентного подходов. Предло- жено формальное определение слоев ПС. Показано, что структурный подход правомочен при разработке ПС на основе объектно ориентиро- ванной и компонентной методологии. Рассмотрен широкий круг вопросов, связанных с рефакторингом объектно ориентированных программ. Объясняется суть рефакторинга, его связь с производительностью и качеством ПС. Излагаются ситуа- ции, в которых следует применять рефакторинг, его методы. Уделено внимание формальным методам рефакторинга, позволяющим автомати- зировать процессы рефакторинга. Рассматриваются вопросы построения архитектуры ПС по ее программному коду, рефакторинг архитектуры многослойной иерархической ПС, паттерн выделения слоев. Предлага- ется подход к решению задачи оптимизации архитектуры программной системы в интересах повышения производительности. Данная монография является вторым изданием, переработанным и дополненным.
Введение За более чем шестидесятилетнюю эволюцию аппаратное обеспечение компьютеров достигло небывалого прогресса. Эмпирическое наблюде- ние, сделанное Г. Муром в 1965 г., в современной трактовке говорит об удвоении производительности компьютеров каждые два года. Современ- ному специалисту (пользователю) доступна такая вычислительная мощ- ность, которую 10—15 лет назад имели немногие научные учреждения. Однако эти вычислительные мощности невозможно использовать без программных систем. И в этой области, несмотря на значительный рост доступности аппаратных ресурсов, наблюдаются значительные пробле- мы. Надо сказать, что компьютерная теория и практика с момента своего образования столкнулись с проблемами, связанными со сложностью про- граммных систем. Первоначально проблемы сложности решались разра- ботчиками путем правильного выбора структур данных, разработки алго- ритмов и применения концепции разграничения полномочий. Хотя тер- мин «архитектура программного обеспечения» является относительно но- вым для индустрии разработки ПС, фундаментальные принципы в этой области неупорядоченно применялись пионерами разработки программ- ного обеспечения, с начала 1980-х гг. Считается, что начало архитектуре программного обеспечения как концепции было положено в научно-ис- следовательской работе Э. Дейкстры в 1968 г. иД. Парнаса вначале 1970-х. Эти ученые подчеркнули, что структура системы программного обеспечения имеет существенное значение, и построение правильной структуры критически важно. По данным американских исследователей, в эти годы только 14% про- ектов по созданию ПС завершались успешно (т.е. с удовлетворением тре- бований заказчика, завершением в срок и соблюдением бюджета). Сего- дня, после нескольких десятилетий эволюции языков программирования, инструментальных средств разработки, практически неограниченной дос- тупности машинного времени, ситуация практически не изменилась. Со- гласно статистике о состоянии дел в программной индустрии в 2008 г., опубликованной компанией Standish Group, из 30 тыс. программных про- ектов 32% проектов завершились успешно, 44% проектов завершились с проблемами (превысили бюджет, сроки и пр.) и 24% проектов полно- стью провалились. На сегодняшний день до сих пор нет согласия в отношении четкого определения термина «архитектура программного обеспечения». Являясь в настоящий момент своего развития дисциплиной без четких правил о «правильном» пути создания системы, проектирование архитектуры ПС все еще является смесью науки и искусства. Популярность изучения этой области возросла с начала 1990-х гг. вместе с научно-исследовательской работой по исследованию архитектурных стилей (шаблонов), языков опи- сания архитектуры, документирования архитектуры и формальных мето- дов проектирования. Известна книга М. Шоу и Д. Гэрлана из университе- та Carnegie Mellon под названием «Архитектура программного обсспече- 5
ния: перспективы новой дисциплины в 1996 г,». В книге выдвинуты неко- торые концепции архитектуры программного обеспечения, такие как ком- поненты, соединители (connectors), стили и др. В Калифорнийском уни- верситете (Ирвайн), занимающемся изучением ПС, в первую очередь ис- следуются архитектурные стили, языки описания архитектуры и динами- ческие архитектуры. Результатами подобных исследований являются по- пулярные монографии, например книга Л. Басса и др. «Архитектура про- граммного обеспечения на практике». Однако таких трудов не так-то много, тем более в отечественной литературе. В этом свете данная моно- графия, по мнению автора, в некоторой степени снижает дефицит таких публикаций. Первая глава монографии вводит в проблему архитектурного проекти- рования. В ней рассматривается широкий круг вопросов, связанных с проблемами создания больших программных систем: особенности раз- работки сложных (больших) программных систем, инженерный подход к разработке ПС, становление и развитие программной инженерии, разви- тие технологий программирования, индустрия программного обеспече- ния. Вторая глава — одна из центральных в монографии. В ней рассматри- ваются все аспекты архитектур современных программных систем. С точ- ки зрения пользователя программной архитектуры (заинтересованного лица - заказчика, разработчика ПС, специалистов по тестированию, спе- циалистов по развертыванию и сопровождению ПС, а также конечных пользователей), эта архитектура дает направление для рассмотрения и ре- шения задач, связанных со специальностью каждого такого пользователя. Тот факт, что эти несколько различных точек зрения могут быть объеди- нены в архитектуре программного обеспечения, является аргументом в защиту необходимости и целесообразности создания архитектуры ПС еще до этапа разработки ПС. Именно в этом направлении в монографии излагаются архитектуры ПС. В ней дается понятие архитектуры про- граммной системы, отмечаются причины ее важности, откуда и как появ- ляется архитектура, кто и что на нее влияет, что определяет и на что влияет архитектура. Заключительный материал главы посвящен рассмот- рению некоторых архитектурных представлений программных систем. Сегодня в мире существует большое количество различных процессов и технологий для создания ПС, рассматривающих полный жизненный цикл проекта разработки ПС и сочетающих в себе научный подход, серь- езную базу исследований и имеющих историю реального использования. В третьей главе монографии детально рассматриваются элементы ЖЦ ПС, обобщенные и сформулированные на основе анализа массы публика- ций. Освещены само понятие жизненного цикла программных систем, ос- новные, вспомогательные и организационные процессы ЖЦ ПС и их взаимосвязь. Большое внимание уделено современным прогрессивным видам моделей ЖЦ ПС, технологиям и инструментам создания программ- ных систем, в том числе рациональному унифицированному процессу (RUP), Scrum-методологии и Agilc-методологии. Особое место в этом списке занимают технология и инструменты компании IBM Rational 6
Software, В этом плане интересна заключительная часть главы, в которой рассматривается управление жизненным циклом приложений и интегри- рованная среда поддержки создания программных систем (Application Lifecycle Management, ALM) на основе комплекса решений IBM Rational Software and Systems Delivery, являющихся наиболее полным по спектру реализованных компонентов ALM, и проекта IBM Jazz. В четвертой главе рассмотрены вопросы проектирования программ- ных систем в части определения требований и целей программного про- дукта. Материал этой главы представляет собой обобщение опубликован- ных монографий и статей, а также собственных исследований автора. Процесс проектирования рассматривается как последовательная трансля- ция требований, предъявляемых к ПС. Оригинальными являются фор- мализованная схема процесса проектирования, а также предложенный автором подход, который может быть использован для выбора тех тре- бований, предъявляемых к системе, которые наиболее совместимы с другими требованиями. Даны постановка задачи и принципы разра- ботки требований, включая бизнес-моделирование, определение функ- циональных и нефункциональных требований. Рассмотрены вопросы анализа и управления требованиями, соотношения требований и рисков, проверка правильности требований, формирование целей программного продукта и проекта. Пятая глава содержит материал, позволяющий решать вопросы разра- ботки предварительного внешнего проекта программной системы. Здесь рассмотрены представление и анализ требований, роль моделирования в определении требований и спецификаций, разработка программных систем, управляемая моделями. Продолжена формальная схема предыду- щей главы применительно к описанию спецификаций. Уделено внимание инструментам IBM Rational RcquisitcPro, Rational Software Architect и IBM Rational Software Modeler, а также другим средствам IBM. Дан структур- ный и объектный подход в анализе требований и определении специфика- ций, в том числе метод функционального моделирования, функциональ- ные диаграммы, диаграммы потоков данных и др., достаточные сведения о языке UML как языке моделирования сложных систем. В заключение главы дана последовательность разработки предварительного внешнего проекта, включающая описание процесса внешнего проектирования, про- ектирование взаимодействия с пользователем, подготовку и проверку правильности внешних спецификаций. В шестой главе содержится обширный материал по проектированию архитектуры программных систем. Здесь изложены теоретические осно- вы методологии проектирования ПС, модульно-интерфейсный подход и модульное программирование, вопросы оценки сложности программ- ных систем, представление архитектуры программных систем на основе модульно-интерфейсного, объектно ориентированного и компонентного подходов. Интересным и во многом оригинальным, с элементами форма- лизации является материал по оценке сложности ПС и представлению многослойного программного продукта. Показан подход к формальному определению слоев программной системы. Большое внимание уделено 7
рассмотрению методов структурного проектирования ПС. Показано, что структурный подход в ряде случаев правомочен при разработке про- граммных систем на основе объектно ориентированной и компонентной методологии. В заключение главы дано описание методики разработки модульной (компонентной) архитектуры программной системы на основе формализованной схемы процесса проектирования, изложенной в четвер- той главе. Этот материал отличается новизной и оригинальностью. Важной, на взгляд автора, является седьмая, заключительная глава, посвященная актуальной в последнее время тематике рефакторинга про- граммных систем. В ней рассматривается большой круг вопросов, связан- ных с рефакторингом объектно ориентированных программных систем. Объясняется, что такое рефакторинг и как он связан с производительно- стью и проектированием ПС, подробно излагаются ситуации, в которых следует применять рефакторинг, а также методы рефакторинга. Уделено внимание формальным методам рефакторинга, позволяющим автомати- зировать процессы рефакторинга. Вводится понятие уровня рефакторинга и высшего его уровня — архитектурного рефакторинга. Определяются си- туации, в которых необходим архитектурный рефакторинг. В заключение главы рассматриваются вопросы построения архитектуры ПС по ее про- граммному коду, рефакторинг архитектуры многослойной иерархической ПС, слои модулей (компонентов) в архитектуре ПС и паттерн выделения слоев. Предлагается подход к решению задачи оптимизации архитектуры программной системы в интересах повышения производительности. Это наиболее интересная часть главы, в направлении которой предполагается дальнейшая работа автора.
Глава 1 ПРОБЛЕМЫ СОЗДАНИЯ БОЛЬШИХ ПРОГРАММНЫХ СИСТЕМ 1.1. Особенности разработки сложных (больших) программных систем Из года в год увеличиваются разнообразие и сложность систем, полу- чивших в международной научно-технической практике название систем, интенсивно использующих программное обеспечение,— (Software Intensive Systems (SIS). В системах такого рода функциональный потен- циал определяется программным обеспечением (ПО) или зависит от ПО в существенной мере [23]. Общепризнанный законодатель в области ис- следований и разработок SIS, Институт программной инженерии Универ- ситета Карнеги-Меллон (Software Engineering Institute, SE1), относит к классу SIS системы, в которых программные системы представляют су- щественный сегмент по следующим позициям: функциональность систе- мы, ее стоимость, риски в процессе разработки, время разработки. В таких системах программные компоненты взаимодействуют друг с другом и компонентами и подсистемами другой природы, датчиками, приборами и людьми, вовлеченными в процессы использования SIS. К числу SIS, например, относятся разнородные автоматизированные сис- темы управления, встроенные бортовые транспортные системы, телеком- муникационные и корпоративные системы, в том числе и на базе web-сер- висов. Для разработок S1S типичны крупномасштабные проекты - десят- ки или сотни разработчиков, месяцы или годы разработки, сотни тысяч или десятки миллионов долларов, комплектование из многочисленных разнородных подсистем, большая часть из которых включает программ- ные системы. Такие программные системы называют сложными или большими про- граммными комплексами, программными продуктами. Они отличаются от небольших не только по размерам (достаточно вспомнить современ- ные операционные системы). Важным для таких систем является наличие дополнительных факторов. Эти факторы связаны с востребованностью программных систем и готовностью пользователей платить деньги как за приобретение самой программы, так и за ее сопровождение и даже за спе- циальное обучение работе с ней. Не все программные системы сложны. Существует множество про- грамм, которые задумываются, разрабатываются, сопровождаются и ис- пользуются одним и тем же человеком. Обычно это начинающий про- граммист или профессионал, работающий изолированно. Нельзя сказать, что все такие системы плохо сделаны или тем более усомниться в квали- фикации их создателей. Но такие системы, как правило, имеют очень ог- раниченную область применения и короткое время жизни. Обычно их лучше заменить новыми, чем пытаться повторно использовать, переделы- вать или расширять. Разработка подобных программ скорее утомительна, чем сложна, так что изучение этого процесса нас не интересует [6]. 9
Какого-либо одного формального признака, отличающего обычную программу от сложной, не существует. В целом сложные программы вы- годно отличаются разнообразием предоставляемого сервиса и количест- вом обрабатываемой информации. Возможно обозначить лишь некоторые качественные характеристики, свойственные сложной программе [15]. Сложная программа характеризуется также более сложным алгоритмом обработки событий. В частности, такая программа предполагает некото- рую реакцию на вмешательство пользователя в управляемый процесс или объект. Существенно, что сложные программы предназначены для многократ- ного использования и применения разными пользователями. В связи с этим следует обратить внимание на ряд необходимых свойств про- граммного обеспечения. Обычно сложная программа обладает следующими свойствами [4, 18]: 1) программа решает одну или несколько связанных прикладных за- дач, зачастую сначала не имеющих четкой постановки и настолько важ- ных для каких-либо лиц или организаций, что те приобретают значимые выгоды от ее использования; 2) программа не предназначена для решения каких-либо прикладных задач, но от нее зависит эффективное решение этих прикладных задач. Это системные программы, например операционные системы, системы управления базами данных, различные инструментальные системы и т.п.; 3) существенно, чтобы программа была удобной в использовании. В частности, она должна включать достаточно полную и понятную поль- зователям документацию, возможно, специальную документацию для ад- министраторов, а также набор документов для обучения работе с про- граммой; 4) программа должна обладать высокой производительностью, высо- кой реактивностью или удовлетворять другим требованиям, в противном случае ее использование по назначению (на реальных данных) может привести к значимым для пользователей потерям; 5) программа должна обладать высокой надежностью. Неправильная работа программы может нанести ощутимый ущерб пользователям и дру- гим организациям и лицам, даже если сбои происходят не слишком часто; 6) для выполнения своих задач программа должна удовлетворять тре- бованиям совместимости, переносимости и интеграции с другими про- граммами и программно-аппаратными системами и обеспечивать работу на разных платформах; 7) пользователи, работающие с программой, могут приобретать допол- нительные выгоды от того, что программа развивается, в нее вносятся но- вые функции и устраняются ошибки. Поэтому необходимо наличие про- ектной документации, позволяющей развивать ее, возможно, вовсе не тем разработчикам, которые ее создавали, без больших затрат на обратную разработку (реинжиниринг); 8) в разработку программы вовлечено значительное количество людей (десятки и сотни человек). Большую программу практически невозможно написать с первой попытки, с небольшими усилиями и в одиночку; 10
9) большая программа имеет намного большее количество ее возмож- ных пользователей по сравнению с небольшими программами и еще больше тех лиц, деятельность которых будет так или иначе затронута ее работой и результатами. Примерами больших программ могут служить операционные систе- мы, системы программирования, системы сетевых протоколов, библиоте- ки классов Java или C# и т.п. Строго говоря, ни одно из указанных свойств не является обязательным для того, чтобы программу можно бы- ло считать большой, но при наличии двух-трех из них достаточно уверен- но можно утверждать, что она большая. На основании некоторых из пере- численных свойств можно сделать вывод, что большая программа или программная система чаще всего представляет собой не просто код или исполняемый файл, а включает еще и набор проектной и пользователь- ской документации. Рост спроса на программные системы является следствием того, что по мере удешевления, повышения надежности и увеличения объема про- изводства компьютеров автоматизация труда человека с помощью компь- ютера становится все более выгодной. Эту тенденцию, отмеченную Б. Бо- эмом еще в 80-е гг. прошлого века и подкрепленную нашим временем, иллюстрируют расширение масштабов использования компьютеров и увеличение социального влияния этого использования. Надо сказать, что часто жизнь вносит свои поправки, и ожидаемые события опережают время. В США к 1985 г. примерно 40% работающих использовали в своей профессиональной деятельности компьютер и программные системы, не обязательно зная, как эти средства функционируют [4]. По данным [16] в США в 2010 г. уровень использования компьютеров составил 90%, в Европе — 70%. Однако эта тенденция усиливается, и к 2015—2018 гг. около 95% работающих будут использовать компьютеры в своей повсе- дневной деятельности. При этом более половины пользователей будут иметь определенные знания о работе компьютера. Еще более глубокое воздействие компьютеры и ПС оказывают на ча- стную жизнь. С каждым днем все большая часть данных, относящихся к личной жизни, банковским счетам, коммунальным услугам, медицин- скому обслуживанию, управлению движением, воздушному сообщению, общественному питанию, производству материальных ценностей и др., а также национальной безопасности, доверяется компьютерам и про- граммным системам. Поэтому все труднее становится ограничивать по- тенциальную угрозу личному благосостоянию, которая возможна при ис- пользовании компьютеров в преступных целях, а также из-за наличия большого числа банков и баз данных, содержащих сведения обо всех и обо всем, и вычислительных систем, заставляющих людей думать и действовать как машины. В России с 1 января 2010 г. все организации, обрабатывающие в своих информационных системах персональные данные физических лиц (со- трудников, клиентов, партнеров и т.п.), независимо от размера и формы собственности должны выполнять требования, установленные Законом И
№ 152-ФЗ «О персональных данных» [22]. Последние изменения по за- щите персональных данных были внесены Федеральным законом №261- ФЗ от 25.07.2011. Этим законом была уточнена сфера действия Федераль- ного закона «О персональных данных», используемые в нем основные по- нятия, принципы и условия обработки персональных данных. Все возрастающее воздействие компьютеров на благосостояние чело- века предъявляет несколько важных требований к созданию программ- ных систем. Эти требования состоят в необходимости такой разработки и сопровождения ПС, которые гарантируют, что вычислительные систе- мы должны быть: • исключительно надежными; • удобными в использовании; • безопасными и труднодоступными для злоупотреблений; • проверяемыми; • оставляющими главную (решающую) роль за человеком, а не ком- пьютером. Основная доля затрат при создании таких вычислительных систем приходится на прикладное программное обеспечение (ПО) и базы данных (БД). Производство ПО является в настоящее время крупнейшей отрас- лью мировой экономики, в которой заняты миллионы специалистов, не- посредственно производящих программный продукт или опосредованно участвующих в этом процессе. Стоимость и качество производимого про- граммного продукта определяется уровнем развития инженерного про- граммирования. Важность инженерного программирования обусловлива- ется двумя основными тенденциями: • программные продукты являются сложными изделиями, и их стои- мость все более возрастает; • программное обеспечение оказывает значительное и все возрастаю- щее воздействие на общественное благосостояние и развитие общества. Темпы роста производства ПО в настоящее время в ряде стран выше темпов роста экономики в целом. При этом по сравнению со стоимостью аппаратуры вычислительных систем стоимость ПО продолжает расти в соответствии с предсказанными в ряде публикаций закономерностями. В настоящее время эта тенденция стала настолько ярко выраженной, что аппаратуру можно рассматривать как своего рода упаковку ПО [4], кото- рое в решающей степени определяет ценность вычислительной системы. Рост спроса на программные системы предъявляет существенные тре- бования к инженерному проектированию. Требования эти двоякого рода: во-первых, существенно повысить производительность труда при разра- ботке программного обеспечения, и, во-вторых, повысить эффективность сопровождения программного продукта (ПП). Последнее особенно важ- но, поскольку сопровождение требует больших затрат, чем разработка. В частности, еще по данным 80-х гг. прошлого века, приведенным Б. Бо- эмом [4], среднее распределение затрат по 477 системам обработки дан- ных оказалось следующим: разработка — 43,7%, сопровождение — 48,8%, другие работы — 7,9%. Более свежие данные по ряду источников [13] сви- детельствуют о подтверждении такого распределения и в настоящее вре- 12
мя. И это несмотря на совершенствование инструментальных систем, ме- тодологии программирования и развитие CASE-средств, поддерживаю- щих все этапы жизненного цикла программных систем. 1.2. Проблемы создания ПС Предмет исследования данной монографии — разработка того, что мы будем называть промышленными программными продуктами. Они при- меняются для решения самых разных задач, таких, например, как систе- мы с обратной связью, которые управляют или сами управляются собы- тиями физического мира и для которых ресурсы времени и памяти огра- ничены; задачи поддержания целостности информации объемом в сотни тысяч записей при параллельном доступе к ней с обновлениями и запро- сами; системы управления и контроля за реальными процессами (напри- мер, диспетчеризация воздушного или железнодорожного транспорта). Системы подобного типа обычно имеют большое время жизни, и боль- шое количество пользователей оказывается в зависимости от их нормаль- ного функционирования. В мире промышленных программ мы также встречаем среды разработки, которые упрощают создание приложений в конкретных областях, и программы, которые имитируют определенные стороны человеческого интеллекта. Существенная черта промышленной программы — уровень сложности: один разработчик практически не в состоянии охватить все аспекты такой системы. Грубо говоря, сложность промышленных программ превышает возможности человеческого интеллекта. Увы, но сложность, о которой мы говорим, по-видимому, присуща всем большим программным систе- мам. Говоря «присуща», мы имеем в виду, что эта сложность здесь неиз- бежна: с ней можно справиться, но избавиться от нее нельзя. Проблемы создания программных систем следуют из их свойств, а именно из их сложности. Современные крупномасштабные проекты ПС характеризуются следующими особенностями [4, 8, 17, 19, 20]: • структурной сложностью (многоуровневой иерархической струк- турной организацией) и территориальной распределенностью; • функциональной сложностью (многоуровневой иерархией и боль- шим количеством функций, сложными взаимосвязями между ними); • информационной сложностью, большим количеством источников и потребителей информации, разнообразными формами и форматами представления информации, сложной технологией прохождения докумен- тов др.; • большим количеством внешних систем различных организаций с разными форматами обмена информацией; • высокой технической сложностью, определяемой наличием сово- купности тесно взаимодействующих подсистем (компонентов), имеющих свои локальные задачи и цели функционирования; • сложной динамикой поведения, обусловленной высокой изменчиво- стью внешней (изменения в законодательных и нормативных актах, не- 13
стабильность экономики и политики) и внутренней среды (структурная реорганизация, текучесть кадров и др.); • отсутствием полных аналогов, ограничивающих возможность ис- пользования каких-либо типовых проектных решений и прикладных сис- тем, высокой долей вновь разрабатываемых программ. Перечислим дополнительные факторы, увеличивающие сложность разработки программных систем [5,20, 26]. 1. Сложность реальной предметной области, из которой исходит за- каз на разработку. Сложность задачи и порождает сложность программ- ного продукта. Проблемы, которые пытаются решить с помощью про- граммного обеспечения, часто неизбежно содержат сложные элементы, а к соответствующим программам предъявляется множество различных, порой взаимоисключающих требований. Эта внешняя сложность обычно возникает из-за «нестыковки» между пользователями системы и ее разра- ботчиками: пользователи с трудом могут объяснить в форме, понятной разработчикам, что на самом деле нужно сделать. Бывают случаи, когда пользователь лишь смутно представляет, что ему нужно от будущей про- граммной системы. Это в основном происходит не из-за ошибок с той или иной стороны; просто каждая из групп специализируется в своей области, и ей недостает знаний партнера. У пользователей и разработчиков разные взгляды на сущность проблемы, и они делают различные выводы о воз- можных путях ее решения. 2. Сложность определения требований к программным системам. Во- первых, по причине необходимости учета большого количества различ- ных факторов. Во-вторых, по причине слабого (чаще всего) знания разра- ботчиками предметной области применения ПС. В то же время специали- сты в этой предметной области, как правило, не могут сформулировать проблему в нужном для разработчика ракурсе. 3. Отсутствие удовлетворительных средств формального описания поведения дискретных систем. Популярные последнее время средства графического языка UML не решают полностью этой задачи. В процессе создания ПС часто используются языки сравнительно низкого уровня (на- пример, С при разработке операционных систем), что приводит к ранней детализации операций в процессе создания ПС и увеличивает объем опи- сания разрабатываемых продуктов (десятки миллионов строк языка про- граммирования в операционных системах). 4. Коллективная разработка. Вследствие больших объемов ПС разра- ботка ведется достаточно большим коллективом специалистов, иногда сотнями и тысячами разработчиков (достаточно напомнить проект OS/360, не говоря уже о линейки современных Windows). Обеспечивать целостность и качество проекта в этом случае трудно по причине сложно- сти организации эффективного взаимодействия специалистов в таких коллективах. 5. Необходимость увеличения степени повторяемости кодов. С це- лью увеличения производительности труда компании стремятся к созда- нию библиотек компонентов, которые можно было бы использовать в дальнейших разработках. Однако в этом случае компоненты приходится 14
делать более универсальными, что в итоге увеличивает сложность разра- ботки. Однако не только повторяемость кодов сказывается негативно. Стремление использовать имеющийся задел приводит к «перетягиванию» нелучшего кода в последующие разработки. Здесь можно вспомнить, как Microsoft долго уверяла общественность о ликвидации 16-разрядного DOS-кода (и не только его) в разработке последующих операционных систем. 6. Большая программная система — это крупное капиталовложение, и нельзя позволить выкидывать сделанное при каждом изменении внеш- них требований. Тем не менее даже большие системы имеют тенденцию к эволюции в процессе их использования. Следовательно, встает задача, которую часто неправильно называют сопровождением программного обеспечения. Чтобы быть более точными, введем несколько терминов [6]: • под сопровождением понимается устранение ошибок; • под эволюцией - внесение изменений в систему в ответ на изменив- шиеся требования к ней; • под сохранением - использование всех возможных и невозможных способов для поддержания жизни в дряхлой и распадающейся на части системе. К сожалению, опыт показывает, что существенный процент затрат на разработку программных систем тратится именно на сохранение. Все пе- речисленные факторы существенно увеличивают сложность разработки программных систем. 1.3. Кризис программирования. Инженерный подход к разработке ПС В начале 70-х гг. прошлого века в США заговорили о кризисе в про- граммировании. Он выражался в том, что большие программные проекты стали выполняться с отставанием от графика или с превышением смет- ных расходов, разработанный программный продукт не обладал требуе- мыми функциональными возможностями, производительность его была низка, качество получаемых ПС не устраивало потребителей [4, 8]. Аналитические исследования и обзоры, выполненные в последующие годы ведущими зарубежными аналитиками, показывали не слишком об- надеживающие результаты. Так, например, результаты исследований, проведенных в 1995 г. компанией Standish Group, проанализировавшей работу 364 американских корпораций и итоги выполнения более 23 тыс. проектов, связанных с разработкой ПО, показали, что только 16,2% про- ектов завершились в срок, не превысили запланированный бюджет и реа- лизовали все требуемые функции [8]. С опозданием были завершены 52,7% проектов, расходы на их разработку превысили запланированный бюджет, а требуемые функции не были реализованы в полном объеме. Были аннулированы до завершения 31,1% проектов. Для двух последних категорий проектов бюджет среднего проекта оказался превышен на 89%, а срок выполнения - на 122%. В 1998 г. процентное соотношение трех пе- речисленных категорий лишь немного изменилось в лучшую сторону (26, 15
46 и 28% соответственно) и в последующие годы улучшилось незначи- тельно. Причинами столь низких показателей, по мнению разработчиков, яв- ляются следующие: • нечеткая и неполная формулировка требований к программным сис- темам; • недостаточное вовлечение пользователей в работу над проектом; • отсутствие необходимых ресурсов; • неудовлетворительное планирование и плохое управление проек- том; • частые изменения требований и спецификаций; • новизна и несовершенство используемой технологии; • недостаточная поддержка со стороны высшего руководства; • недостаточно высокая квалификация разработчиков, отсутствие не- обходимого опыта. Потребность контролировать процесс разработки ПО, прогнозировать и гарантировать стоимость разработки, сроки и качество результатов при- вели в конце 70-х гг. прошлого века к необходимости перехода от кустар- ных к индустриальным способам создания ПО. Начала развиваться сово- купность инженерных методов и средств создания программного обеспе- чения, объединенных общим названием «программная инженерия». Впер- вые этот термин (software engineering) был использован как тема конфе- ренции, проведенной под эгидой НАТО в 1968 г. В 1975 г. в Вашингтоне была проведена первая международная конференция, посвященная про- граммной инженерии. В процессе становления и развития программной инженерии можно выделить два этапа: 70-е и 80-е гг. - систематизация и стандартизация процессов создания ПО (на основе структурного подхода), 90-е гг. — нача- ло перехода к сборочному, индустриальному способу создания ПО на ос- нове объектно ориентированного подхода. 1.4. Становление и развитие программной инженерии Программная инженерия - это область компьютерной науки и тех- нологии, которая занимается построением программных систем, настоль- ко больших и сложных, что для этого требуется участие слаженных ко- манд разработчиков различных специальностей и квалификаций. Обычно такие системы существуют и применяются долгие годы, развиваясь от версии к версии, претерпевая на своем жизненном пути множество изме- нений, улучшение существующих функций, добавление новых или удале- ние устаревших возможностей, адаптацию для работы в новой среде, уст- ранение дефектов и ошибок. Суть методологии программной инженерии состоит в применении систематизированного, научного и предсказуемого процесса проектирования, разработки и сопровождения программных средств [19]. В основе программной инженерии лежит фундаментальная идея: про- ектирование ПО является формальным процессом, который можно изу- 16
чать и совершенствовать. Однако, как показывает опыт более тридцати прошедших лет, использовать математические методы для формализации процесса проектирования программных систем не столь просто, и успехи в этом отношении достаточно скромные. Ф. Брукс, руководитель проекта разработки операционной системы OS/360, отмечал, что самым существенным свойством программных сис- тем является их сложность [5]. По причине уникальности и несхожести своих составных частей программные системы отличаются от техниче- ских систем, например компьютеров, в которых преобладают повторяю- щиеся элементы. Сами компьютеры сложнее большинства продуктов че- ловеческой деятельности. Количество их возможных состояний очень ве- лико, поэтому их так трудно понимать, описывать и тестировать. У про- граммных систем количество возможных состояний на порядок превыша- ет количество состояний компьютеров. Дополнительно к отмеченным выше особенностям ПО нужно доба- вить следующие [4, 8, 9, 20]: • сложность описания программной системы, обусловленная боль- шим количеством функций, процессов, элементов данных и сложными взаимосвязями между ними; • наличие (как правило) в программной системе совокупности тесно взаимодействующих подсистем, имеющих локальные задачи и цели функционирования, которые могут быть противоречивыми; • отсутствие полных аналогов, ограничивающее возможность ис- пользования типовых проектных решений и прикладных систем при соз- дании новой программной системы; • необходимость интеграции существующих и вновь разрабатывае- мых программных систем и приложений; • функционирование сложной программной системы в неоднородной среде на нескольких аппаратных платформах и в различных операцион- ных средах, часто трудно совместимых; • разобщенность и разнородность отдельных групп разработчиков по уровню квалификации и сложившимся традициям использования тех или иных инструментальных средств; • временная протяженность проекта, обусловленная сложностью проекта, ограниченными возможностями коллектива разработчиков, мас- штабами организации-заказчика и различной степенью готовности под- разделений заказчика к внедрению программной системы. Как уже отмечалось, сложность - существенное свойство программ- ных систем. Многие проблемы разработки следуют из этой сложности и ее нелинейного роста при увеличении размера системы. Сложность яв- ляется причиной затруднений, возникающих в процессе общения между разработчиками, что ведет к ошибкам в продукте, превышению стоимо- сти разработки, затягиванию графика работ. Сложность вызывает трудно- сти понимания всех возможных состояний программ, что приводит к сни- жению их надежности. Сложность структуры сдерживает развитие про- граммной системы и возможность добавления новых функций. 17
Для успешной реализации проекта объект проектирования (ПС) дол- жен быть адекватно описан, т.е. должны быть построены полные и непро- тиворечивые модели архитектуры ПС, определяющей совокупность структурных элементов системы и связей между ними, поведение элемен- тов системы в процессе их взаимодействия, а также иерархию подсистем, объединяющих структурные элементы. По мнению Г. Буча [6], моделирование — центральное звено всей дея- тельности по созданию качественного программного обеспечения. Моде- ли строятся, чтобы понять и осмыслить структуру и поведение будущей системы, облегчить управление процессом ее создания, уменьшить воз- можный риск, а также документировать принимаемые проектные реше- ния. Модель — это полное описание программной системы с определен- ной точки зрения. Ни одна модель не является абсолютно достаточной. Напротив, чтобы понять большинство систем, кроме самых тривиальных, требуется множество взаимосвязанных моделей. Хорошие модели явля- ются основой взаимодействия участников проекта и гарантируют кор- ректность архитектуры. Модели разрабатываются на специальных языках. Язык моделирова- ния должен включать: • элементы модели— функциональные концепции моделирования и их семантику; • нотацию (систему обозначений)- визуальное представление эле- ментов моделирования; • руководство по использованию— правила применения элементов в рамках построения тех или иных типов моделей программных систем. Модели представляют собой абстракции, которые отображают основу сложной проблемы или структурируют ее, скрывая второстепенные дета- ли, и тем самым делают проблему более доступной для понимания. Для создания сложной системы разработчик должен рассмотреть ее с разных точек зрения, построить модель с использованием подходящей для дан- ной предметной области терминологии (нотации), проверить, что модель удовлетворяет требованиям, предъявляемым к системе, и, наконец, по- строить на основе созданной модели требуемую сложную систему. Уже почти 15 лет унифицированный язык моделирования UML (Unified Modeling Language) является промышленным стандартом для ви- зуализации, специфицирования, конструирования и документирования артефактов систем, в которых главная роль принадлежит программному обеспечению. Артефакты являются строительными блоками при модели- ровании физических аспектов системы. Артефакт - это физическая и за- меняемая часть системы. Артефакты используются для моделирования таких физических сущностей, которые могут располагаться на узлах- физических элементах, представляющих собой вычислительный ресурс, например на исполняемых программах, библиотеках, таблицах, в файлах и документах. Будучи де-факто стандартным языком моделирования, UML дает разработчикам возможность достичь взаимопонимания и избе- жать разночтений [6, 14]. 18
Очевидно, что конечная цель разработки ПС — это не модель, а рабо- тающее приложение в форме программного кода. Диаграммы UML, в ко- нечном счете, — лишь наглядные изображения, поэтому, используя языки графического моделирования, важно понимать, чем они могут помочь при написании кода программы. Использование графических языков мо- делирования целесообразно в следующих случаях. 1. При изучении методов проектирования. Программисты, многие го- ды работавшие до появления объектно ориентированной технологии, от- мечают серьезные трудности, связанные с освоением объектно ориенти- рованных методов, и в первую очередь со сменой парадигмы. Графиче- ские средства облегчают решение этой проблемы. 2. При общении с заказчиками программной системы- будущими пользователями и экспертами организации. Графические методы нагляд- но и понятно представляют архитектуру системы и объясняют, что эта система будет делать. 3. При получении общего представления о системе. Графические ме- тоды показывают, какого рода абстракции существуют в системе и какие ее части нуждаются в дальнейшем уточнении. Эта информация полезна при появлении в коллективе разработчиков новых сотрудников. Следует отметить, что в версии UML 2.0 возможности языка были расширены (формальная спецификация версии UML 2.0 опубликована в августе 2005 г., последняя версия UML 2,3 опубликована в мае 2010 г.). Благодаря свой выразительности он позволяет моделировать буквально все - от офисных информационных систем и распределенных web-прило- жений до встроенных систем реального времени. Язык UML — нечто большее, чем просто набор графических символов. Каждый из этих сим- волов имеет четкую семантику. Это означает, что один разработчик мо- жет описать модель на языке UML, а другой разработчик и даже инстру- ментальное средство — однозначно интерпретировать ее. Язык UML — это язык специфицирования, он позволяет специфициро- вать все важные решения, касающиеся анализа, дизайна и реализации, принимаемые в процессе разработки и внедрения программных систем. UML не является языком визуального программирования, но его модели могут быть непосредственно ассоциированы с такими языками програм- мирования, как Java, C++, C# или Visual Basic. Отображение модели на языке программирования позволяет осуществить прямое проектирова- ние — генерацию кода из модели, обратное проектирование (восстановле- ние модели по коду) также возможно. Многие компании, специализирую- щиеся на разработке программных продуктов, помимо исполняемого кода производят и другие продукты. Сюда относится все, что связано с разра- боткой требований, архитектурой, проектными решениями (дизайном), исходным кодом, проектными планами, тестами, прототипами, релизами (версиями) и др. Корпорация IBM, крупнейшая в мире компания, работающая в облас- ти информационных технологий, лидер в разработке и внедрении иннова- ционных решений, предлагает полный комплекс решений, сетевых техно- логий и услуг, которые помогают преобразовать традиционные процессы 19
в компаниях — разработчиках программного обеспечения и максимально эффективно использовать их интеллектуальные ресурсы и новые рыноч- ные возможности. В 2003 г, в состав компании IBM вошла корпорация Rational Software. Платформа Rational вместе с Lotus, Tivoli, WebSphere и DB2 вошла в число ключевых компонентов стратегии IBM по созданию программного обеспечения. IBM Rational выпускает CASE-средства, сис- темы автоматизированного проектирования ПО, а также средства управ- ления проектами, связанными с разработкой, документированием и со- провождением крупных информационных систем. Например, продукт IBM Rational Software Architect (RSА) предназна- чен для построения моделей разрабатываемых программных систем с ис- пользованием унифицированного языка моделирования UML 2.0, прежде всего моделей архитектуры разрабатываемого приложения. Тем не менее RSA объединяет в себе многие функции таких программных продуктов, как Rational Application Developer, Rational Web Developer и Rational Software Modeler, тем самым предоставляя возможность архитекторам и аналитикам создавать различные представления разрабатываемой ин- формационной системы с использованием языка UML 2.0, а разработчи- кам - выполнять разработку J2EE, XML, веб-сервисов и т.д. В процессе развития практики и науки создания программных систем сложились определенные технологи и программирования [7, 9, 10, 17, 18]. Эти технологии целесообразно рассмотреть в историческом контексте, выделяя основные этапы развития программирования как науки. 1.5. Развитие технологий программирования В соответствии с обычным значением слова «технология» под тех- нологией программирования (programming technology) понимается со- вокупность производственных процессов, приводящая к созданию тре- буемого программного продукта, а также описание этой совокупности процессов. Другими словами, технология программирования понимает- ся здесь в широком смысле как технология разработки программных средств, включая все процессы с момента зарождения идеи (концепции) этого средства до создания необходимой программной документации. Каждый процесс этой совокупности базируется на использовании ка- ких-либо методов и средств, например компьютера (в этом случае речь идет о компьютерной технологии программирования). В литературе имеются и другие, несколько отличающиеся определе- ния технологии программирования. Используется в литературе и близ- кое к технологии программирования понятие программной инжене- рии, определяемой как систематический подход к разработке, эксплуа- тации, сопровождению и изъятию из обращения программных средств. Главное различие между технологией программирования и программ- ной инженерией как дисциплинами для изучения заключается в способе рассмотрения и систематизации материала [17]. В технологии программирования акцент делается на изучении про- цессов разработки программных систем (технологических процессов), 20
порядке их прохождения и на том, как методы и инструментальные средства разработки программных систем используются в этих процес- сах, как их применение образует технологические процессы. Тогда как в программной инженерии изучаются различные методы и инструмен- тальные средства разработки ПС с точки зрения достижения определен- ных целей. Эти методы и средства могут использоваться в разных тех- нологических процессах (и в разных технологиях программирования). Не следует также путать технологию программирования с методоло- гией программирования. В технологии программирования методы рас- сматриваются «сверху» — с точки зрения организации технологических процессов, а в методологии программирования методы рассматривают- ся «снизу» — с точки зрения основ их построения. Например, в работе [23] методология программирования определяется как совокупность ме- ханизмов, применяемых в процессе разработки программного обеспече- ния и объединенных одним общим философским подходом. В историческом аспекте в развитии технологии программирования можно выделить несколько этапов [9, 10, 17, 18, 26, 27]. 1. «Стихийное» программирование — отсутствие сформулированной технологии, когда программирование было, по сути, искусством. Этап охватывает период от появления первых ЭВМ до середины 60-х гг. XX в. Основные вехи этапа: двоичный код, восьмеричный код, шестна- дцатиричный код, ассемблеры, макроассемблеры, алгоритмические язы- ки, подпрограммы, пользовательские функции, процедуры, библиотеки расчетных и служебных подпрограмм. Развитие программирования шло по пути замены машинных языков ассемблерами, а затем алгоритмическими языками (Fortran, Algol и др.) и повторного использования подпрограммами, что повысило произво- дительность труда программиста. Стихийно использовалась разработка «снизу вверх» — подход, при котором вначале проектировали и реализо- вывали сравнительно простые подпрограммы, а из них потом пытались построить сложную программу. 2. Структурный подход к программированию. Этот подход сложил- ся в 60-70 гг. XX в. и представлял собой совокупность рекомендуемых технологических приемов, охватывающих все этапы разработки про- граммного обеспечения. В основе структурного подхода лежит деком- позиция сложных систем с целью последующей реализации в виде от- дельных небольших подпрограмм. В отличие от используемого ранее процедурного подхода к декомпозиции, структурный подход требовал представления задачи в виде иерархии подзадач простейшей структуры. Проектирование осуществлялось «сверху вниз» и подразумевало реа- лизацию общей идеи, обеспечивая проработку интерфейсов подпро- грамм [9, 11]. Вводились ограничения на конструкции алгоритмов, ре- комендовались формальные модели их описания, а также специальный метод проектирования алгоритмов — метод пошаговой детализации. Поддержка принципов структурного программирования была заложена в основу процедурных языков программирования (PL/1, Algol-68, Pascal, С). Появилась и начала развиваться технология модульного программи- рования, которая предполагает выделение групп подпрограмм, исполь- 21
зующих одни и те же глобальные данные, в отдельно компилируемые модули. Практика показала, что структурный подход в сочетании с мо- дульным программированием позволяет получить достаточно надежные программы, размер которых не превышает 100 000 операторов [9]. Уз- ким местом модульного программирования стали межмодульные интер- фейсы, ошибки в которых трудно обнаружить по причине раздельной компиляции модулей (ошибки выявляются только при выполнении про- граммы). 3. Объектный подход к программированию. Складывался с середины 80-х до конца 90-х гг. XX в. Объектно ориентированное программирова- ние (ООП) определяется как технология создания сложного программ- ного обеспечения, основанная на представлении программы в виде со- вокупности объектов, каждый из которых является экземпляром опреде- ленного типа (класса), а классы образуют иерархию с наследованием свойств. Взаимодействие программных объектов осуществляется путем передачи сообщений. Основное достоинство объектно ориентированно- го программирования по сравнению с модульным программированием — более естественная декомпозиция программного обеспечения, которая существенно облегчает его разработку. Кроме того, объектный подход предлагает новые способы организации программ, основанные на меха- низмах наследования, полиморфизма, композиции. Это позволяет суще- ственно увеличить показатель повторного использования кодов и созда- вать библиотеки классов для различных применений. Развитие объектного подхода в технологии программирования при- вело к созданию сред визуального программирования. Появились языки визуального объектно ориентированного программирования, такие как Delphi, C++ Builder, Visual C++, C# и т.д. Однако технология ООП име- ет и недостатки. Главный из них — зависимость модулей программного обеспечения от адресов экспортируемых полей и методов, структур и форматов данных. Эта зависимость объективна, так как модули долж- ны взаимодействовать между собой, обращаясь к ресурсам друг друга. 4. Компонентный подход и CASE-технологии (с середины 90-х гг. XX в. до нашего времени) [8, 9, 18]. Этот подход предполагает построе- ние программного обеспечения из отдельных компонентов — физически отдельно существующих частей программного обеспечения, которые взаимодействуют между собой через стандартизованные двоичные ин- терфейсы. В отличие от обычных объектов, объекты-компоненты мож- но собирать в динамически вызываемые библиотеки или исполняемые файлы, распространять в двоичном виде (без исходных текстов) и ис- пользовать в любом языке программирования, поддерживающем соот- ветствующую технологию. В настоящее время рынок компонентов — ре- альность, поддерживаемая Интернетом, массовой рекламой и публика- циями. Основы компонентного подхода были разработаны компанией Microsoft начиная с технологии OLE (Object Linking and Embedding — связывание и внедрение объектов), которая использовалась в ранних версиях Windows для создания составных документов. Ее развитием 22
стало появление COM-технологии (Component Object Model — компо- нентная модель объектов), а затем ее распределенной версии — DCOM, на основе которых были разработаны компонентные технологии и ре- шаются различные задачи разработки программного обеспечения. Среди них следует отметить OLE-automation — технологию создания программируемых приложений, обеспечивающую доступ к внутренним службам этих приложений. На основе OLE-automation разработана тех- нология ActiveX, предназначенная для создания программного обеспе- чения - как сосредоточенного на одном компьютере, так и распределен- ного. Безопасность и стабильная работа распределенных приложений обеспечивается еще двумя технологиями, заложенными в СОМ. Это технология MID AS (Multi-tier Distributed Application Services Suite — сер- вис для создания многоуровневых распределенных приложений), кото- рая была предложена фирмой Borland уже довольно давно, и MTS (Microsoft Transaction Server) - сервер управления транзакциями. Компонентный подход лежит также в основе технологии CORBA (Common Object Request Broker Architecture - общая архитектура с по- средником обработки запросов объектов). Эта технология, реализующая подход, аналогичный СОМ, разработана группой компаний ОМС (Object Management Group - группа внедрения объектной технологии программирования). Программное ядро CORBA реализовано для всех основных аппаратных и программных платформ и обеспечивает созда- ние программного обеспечения в гетерогенной вычислительной среде. Важнейшая особенность современного этапа технологии программи- рования — широкое использование компьютерных технологий для созда- ния и сопровождения программных систем на всех этапах их жизненно- го цикла. Эти технологии получили название CASE-технологий (Computer-Aided Software I System engineering - разработка программно- го обеспечения I программных систем с использованием компьютерной поддержки). Сегодня существуют CASE-технологии, поддерживающие как структурный, так и объектный, в том числе компонентный, подходы к программированию [8]. Зная, какие программные технологии оказали самое сильное влияние на разработку программных систем за последние 25 лет, можно оценить их по количеству опубликованных научных статей или выяснить, на- пример, сколько времени продолжался поднятый вокруг них ажиотаж. С другой стороны, с точки зрения пользователя, можно судить о них, оценивая, чего удалось добиться по сравнению с тем, что предваритель- но было обещано. Так в чем же уникальность последних 25 лет [31]? Во-первых, программное обеспечение перестало быть прерогативой нескольких компаний и стало частью повседневной жизни практически каждого жителя нашей планеты: ПК, Internet и мобильные телефоны — свидетельства этой грандиозной эволюции. Во-вторых, теперь принято опираться на эмпирические оценки, а не на мнения. М. Шоу, описывая ситуацию в 1980-х гг. прошлого века, заметила: «Программная инжене- рия пока еще не стала настоящей дисциплиной, но имеет необходимый потенциал для того, чтобы ею стать» [31]. В то время многие техноло- 23
гии только-только создавались и предлагались вниманию публики, но еще с 1980-х гг. инженеры анализировали и эмпирически оценивали но- винки для того, чтобы судить об их влиянии. На рис. 1.1 показаны программные технологии и периоды, когда они достигли важнейших этапов своего развития. Эта таблица построена, исходя из структуры, предложенной С. Редвайном и В. Риддлом [31]. Для простоты показаны только три этапа развития: основы — когда про- ведены базовые исследования и созданы краеугольные концепции; огра- ниченное использование — когда эти концепции были взяты на вооруже- ние некоторыми компаниями и пользователями; широкое использова- ние — когда технология стала применяться примерно третьей частью це- левого рынка. Журналы и интернет-ресурсы за последние 25 лет кардинально изме- нились. До 1980 г. для практиков основным источником сведений о про- граммных технологиях был Datamation, сейчас существует несколько таких источников, например IEEE Software, а также онлайновые ресур- сы вроде Slashdot (англоязычный новостной сайт, специализирующийся на технических и интересных технической аудитории темах), которые тоже дают представление о последних шагах в эволюции технологий. В статье [31] собран материал из множества отдельных источников дан- ных, которые найдены в различных обзорах, и wiki в программном ми- ре. Для объективности автор статьи К. Эберт обратился к своим колле- гам из советов IEEE Software и поинтересовался их представлением о технологиях, появившихся за последние 25 лет. На самом деле точно определить время перехода на новый этап просто невозможно. Это от- носится, например, к объектно ориентированной разработке, которая ак- тивно используется с 1990-х гг., но до сих пор не нашла своего примене- ния в некоторых отраслях. На упомянутом рисунке показаны три основные группы программ- ных технологий. Базовые технологии по мере своего развития влияют на массовые тенденции и дисциплины, и они применяются во всех об- ластях и направлениях программной разработки. Большинство из из- вестных сейчас таких технологий существует последние 25 лет. Техно- логические концепции и методологии объединяют базовые методики, которые используются во многих различных отраслях и продуктах. Кон- солидированные технологии опираются на концепции и предоставляют готовые технические решения. В тех случаях, когда технологии принад- лежат к двум таким группам, они отнесены к более общей группе. Что имеют в виду, когда говорят, что программная технология ока- зывает влияние? Задавая этот вопрос, можно получить множество отве- тов, отражающих точку зрения конкретного собеседника. Профессор будет оценивать репутацию, исследовательские гранты и то, как техно- логия поможет добиться этих целей. 24
Вазовые технологии Программная инжсмермя Программный процесс Оценка, планирование, измерение Инженерий систем безопасности Искусственный" интеллект Эмпирический программная инженерия Инженерия эргономики Биологические вычислительные системы Технологические концепции и методологии Объектно-ориентированная разработка Модели развития/- совершенствования Общее моделирование Свободно- распространяемое ПО Параллельная обработка (распределенная много ядерная) Быстрая разработка Инженерия серии продуктов Программные шаблоны Компонентная разработка Разработка на базе модели Программы как сервис SOA Автономное ПО (агенты, обучение) Формальная разработка (определение, верификация) Консолидированные технологии Персональный компьютер и офисные системы Экосистема Unix, ОС Unix, инструментарий) Графические многозадачные ОС (Windows и т.д.) Стандартное корпоративное ПО (Ш, CRM) Internet (протоколы, инфраструктура) Internet-браузер, языки разметим Мобильность (протоколы инфраструктура) Экосистема Java (Java, КМ, библиотеки) Анимированная трехмерная графика, виртуальная и смешанная реальность Wih LAMP: срежтва работы со скриптами Linux A pacta MySQL РНР/ Foil IPSE интегрированная среда поддержки проектов CASE: средства автомагиwродамнего проектирования IDE ххтетрироваю441 среда разработки PLM упражлнмд жхыитмым цииом продукта Промежуточное программное обеспечение LAMP, механизмы поиска Инструментарий IPSE CASE П)ЕИМ_ Экосистема Eclipse 1АМР: средства работы со скриптами Linux, Arachc, MySQL. PHP/Pcrl IPSE: интегрированная среда поддержки проектов CASE: средства автоматизированного проектирования IDE: интегрированная среда разработки PLM: управление жизненным циклом продукта Осмеш J Ограниченное использование В Широкое использование J Основы J Ограниченное использование I Широкое использование Рис. 1.1. Основные группы программных технологий 25
Ученый сформулирует ответ, учитывая инновационный потенциал. Менеджеров производства в первую очередь интересует рентабель- ность. Программный инженер будет иметь в виду полезность и эффек- тивность при решении той проблемы, которой он занимается. Типичный потребитель, скорее всего, будет судить о технологии по тому, насколько ее можно использовать в повседневной жизни, а дети - как не отстать от своих ровесников. Эти две последние потребительские группы — повседневные пользователи программного обеспечения — не только численно превосходят другие, но и совсем иначе судят о про- граммных технологиях и продуктах. Они обращают внимание на то, на- сколько незаметным, удобным в использовании и встроенным является программное обеспечение. Другими словами, насколько незаметно, но эффективно оно помогает решить значимую для них задачу. На рис. 1.1 можно заметить ряд тенденций, характерных для эволю- ции программного обеспечения за последние годы: • развитие программных технологий теперь стимулируют не отдель- ные компании, а экосистемы исследователей, поставщиков, потребителей и пользователей; • технологии должны пройти ряд апробаций с различной направлен- ностью, прежде чем они будут признаны успешными; • каждая конкретная технология распространяется в разных отраслях с разной задержкой; • ориентированность на конкретную предметную область дает поль- зователям возможность адаптировать технологии к своим специфическим потребностям; • работа с процессами заменила создание методом проб и ошибок ре- шений под конкретную ситуацию; • технологии, которые раньше были фрагментированными и изолиро- ванными, сейчас интегрируются. Каждая из этих тенденций оказывает серьезное влияние на инженер- ные продукты и на формирование программной отрасли. Microsoft с Windows или Sun с Java - пример того, как отдельная компания опре- деляет развитие технологии. Но технологии от этих производителей до- бились успеха благодаря тому, что они создавались и широко распро- странялись в отраслях программирования. Невозможно даже предста- вить себе Windows без Intel и всей экосистемы поставщиков и провайде- ров сервисов. Точно так же банки создали банкоматы и разработали множество связанных с ними программных технологий, таких как рас- пределенная и защищенная обработка транзакций. Компании розничной торговли стимулировали разработку кассовых аппаратов и необходимо- го программного обеспечения для поддержки цепочки поставки, в том числе штрихкоды и средства радиочастотной идентификации (RFID). Некоторые технологии прошли очень долгий период развития либо ни- когда не были полностью разработаны. График их перехода к широкому использованию напоминает сину- соиду, что свойственно инновациям, которые переходят от этапа на- чальных исследований и опытных эксплуатаций к широкому отраслево- 26
му применению, а затем все повторяется снова [31]. Это объясняет, по- чему успешные компании практически в одночасье могут потерпеть крах просто потому, что они своевременно не предложили определен- ную технологию. Программные менеджеры также часто склонны к ста- билизации, а не к росту: их интересует эффективность, и они недооце- нивают экспериментирование и инновации. Программные технологии полезны, если они широко используются. Однако любая конкретная технология в одних отраслях начинает завое- вывать популярность быстрее, чем в других. Хороший тому пример — долгая и трудная дорога к пользователям, которую прошли полезные пакеты инструментов для генерации кода и инженерии программного обеспечения. Когда эти пакеты появились вместе с технологией, они еще не были готовы для повсеместного применения, а позже не был го- тов рынок. Такой же оказалась и судьба экспертных систем и систем ис- кусственного интеллекта. Сейчас они применяются почти везде, по- скольку в отрасли осознали, что экспертная система не является авто- номной технологией, а должна быть интегрирована в другие продукты. На рис. 1.2 показан этот эффект на примере обеспечения безопасности информации. Рост сложности, открытые интерфейсы, сети поставщиков Безопасность для конкретных ситуаций, атаки, нскоррктнос использование, ошибочная обработка, инциденты Безопасность как основной принцип проектирования, многоуровневые подходы ИТ-инфраструктура 1985 1995 Телекомуникация 1995 Промышленная автоматизация Сейчас Рис. 1.2. Эффект внедрения программных технологий Безопасность впервые была признана ключевой технологией в ИТ- инфраструктурах в конце 1980-х гг., когда вирус Jerusalem и червь Morris, по существу, парализовали интернет-трафик [25]. Инциденты продолжались в 1990-х гг., поскольку технология применялась только как особая мера и без тщательного архитектурного анализа. Сейчас, по прошествии более двадцати лет, вместе с новыми ИТ-продуктами нако- нец стали реализовывать базовые принципы обеспечения безопасности. То же самое повторяется в отрасли телекоммуникаций: как показывают атаки в сфере IP-телсфонии, здесь пока лишь создаются заплатки на особый случай, но без реального контроля. Промышленная автоматиза- ция и другие предметные области еще больше отстают с внедрением ин- женерии обеспечения безопасности, как это продемонстрировал червь Slammer. Ориентированность на конкретную предметную область заменила универсальность 1990-х гг. Первые CASE и распределенные компонент- 27
ные модели увязли в попытках решить сразу много проблем. Когда в от- расли осознали, что различные предметные области имеют свои специ- фические потребности и скорости внедрения, то оказалось, что доста- точно лишь оптимизировать технологию, предложив ее конкретному рынку. Инструменты моделирования сразу же стали пользоваться попу- лярностью после того, как были адаптированы к потребностям конкрет- ных предметных областей, таких как встроенные контроллеры или теле- коммуникационные протоколы. Программные процессы (как для инженерии, так и для управления) стимулировали эволюцию технологий с 1980-х гг. Сложность про- граммных систем растет быстрее, чем люди в состоянии ею управлять. Эти трудности были уже в 1960-х гг., но тогда ситуация начала терять свою остроту после того, как ведущие отрасли перенесли свое внимание на процесс инженерии программного обеспечения. Как следствие, раз- работка программного обеспечения за последние 35 лет кардинально из- менилась, превратившись из индивидуального творчества в дисциплину программной инженерии. Сейчас трудно поверить, что 30 лет назад большая часть программ- ного обеспечения и его разработчики и пользователи действовали изо- лированно. Программная интеграция лучше всего стала видна с появле- нием Интернета и его огромными темпами роста благодаря развитию средств взаимодействия. Компонентные платформы и открытые стан- дарты еще больше усиливают эту тенденцию. Успешное внедрение и интеграция отнюдь не тривиальны: чтобы предложить что-то полезное инженерам, новые технологии, процессы и средства инженерии нужда- ются в аппарате глубокого управления изменениями. Литература к главе 1 1. ГГ-рынок в 2010 г. прибавил 8% (до 1,5 трлн долларов) [Элек- тронный ресурс]// URL: http://www.dp.ru/aZ2011/02/10/-IT- rinok_v_2010_godu_prib/ 2. Software 500 / Промышленность. Избранные статьи [Электронный ресурс] // URL: http://www.softwarcmag.com/focus-areas/the-software-500- industry/featured-articlcs/innovation-alive-and-wcll/ 3. Ассоциация АП КИТ [Электронный ресурс] // URL: http://www.apkit.ru/ http://www.apkit.ru/ 4. Боэм Б. У. Инженерное проектирование программного обеспече- ния: пер. с англ. — М.: Радио и связь, 1985. - 512 с. 5. Брукс Ф.П. Как проектируются и создаются программные ком- плексы: очерки по системному программированию. — М.: Наука, 1979. — 151с. 6. Буч Г., Рамбо Д., Якобсон И. Язык UML: руководство пользовате- ля: пер. с англ. — 2-е изд. — М.: ДМК Пресс, 2007. — 496 с. 7. Ван Тассел Д. Стиль, разработка, эффективность, отладка и испы- тание программ: пер. с англ. - М.: Мир, 1981. — 320 с. 28
8. Вендров А.М. Проектирование программного обеспечения эконо- мических информационных систем: учебник. — М.: Финансы и стати- стика, 2000. — 352 с. 9. Гагарина Л.Г, Кокорева Е.В., Виснадул Б.Д. Технология разработ- ки программного обеспечения: учебное пособие / под ред. Л.Г. Гагари- ной. - М.: ФОРУМ, ИНФРА-М, 2008.-400 с. 10. Жоголев Е.А. Введение в технологию программирования (кон- спект лекций). — М.: ДИАЛОГ-МГУ, 1994. 11. Зиглер К. Методы проектирования программных систем: пер. с англ. - М.: Мир, 1985. - 328 с. 12. Индустрия программного обеспечения [Электронный ресурс] // URL: http://ru. wikipedia.org/wiki/-%C8%ED%E4%F3%F 1 %F2%F0%E8% FF_%EF%F0 13. Исаев Г Информационные технологии. — М.: Омега-Л, 2011.— 464 с. 14. Кватрани Т., Палистрает Д. Визуальное моделирование с помо- щью IBM Rational Software Architect и UML: пер. с англ. — М.: КУДИЦ- ПРЕСС, 2007. - 192 с. 15. Ковалев С. Программирование на алгоритмическом языке MQL4 [Электронный ресурс] //URL: http://book.mql4.com/ru/special/index 16. Корпорация Microsoft держит «свой курс» в СФО [Электронный ресурс] И URL: http://ria-sibir.ru/viewnews/43726.html 17. Крылов Е.В., Острейковский В.А., Типикин Н.Г Техника разра- ботки программ. В 2 кн. Кн. 2: Технология, надежность и качество про- граммного обеспечения. - М.: Высшая школа, 2008.-469с. Г&.Кулямин В.В. Технологии программирования. Компонентный подход [Электронный ресурс] // URL: http://lib.mdpu.org.ua/e- book/vstup/L/J ogolev.pdf 19. Липаев В.В. Программная инженерия. Методологические основы. Предисловие к курсу [Электронный ресурс] И URL: http://citforum.ru/ SE/lipaev/prcface.shtml 20. Майерс Г, Надежность программного обеспечения: пер. с англ. — М.: Мир, 1980.-360 с. 21. Новости [Электронный ресурс] // URL: www.apkit.ru 22. Защита персональных данных [Электронный ресурс] // URL: http://www.runa.ru/Zaschita_pcrsonalnih_dannih2gclidM2K-q69rpqsCFUO_ zAod7mgbl А 23. Соснин П.И. Архитектурное моделирование систем, интенсивно использующих программное обеспечение [Электронный ресурс] // URL: http://www.ict.edu.ru/ft/00565 U62328el-stl5.pdf 24. Состояние и потенциал развития ИТ-индустрии России в 2010- 2013 годах [Электронный ресурс] // URL: http://www.apkit.ru/coinmit- tees/investment /projects/strategy/02.php]. 25. Таненбаум Э. Современные операционные системы. - 4-е изд. — СПб.: Питер, 2006. - 1040 с. 29
26.....Томаев М.Х. Технологии программирования [Электронный ре- сурс] // URL: http://www.skgmi-gtu.ru/aoi/Method/%D0%A2%D0% В5% htm 27. Турский В. Методология программирования: пер. с англ. — М.: Мир, 1981.-264 с. 28. Ало Э., Джексон К., Дик Д. Разработка и управление требова- ниями: практическое руководство пользователя. — Telclogic, 2005 29. Хьюз Дж., Мичтом Дж. Структурный подход к программирова- нию: пер. с англ. - М.: Мир, 1980. - 128 с. 30. Чернышов Л.Н. Среды разработки программного обеспечения: история и перспективы [Электронный ресурс] И URL: http://nit.micm. edu.ru/sbornik/2009-/plen/008.html 31. Эберт К. Экскурс в историю программных технологий // Откры- тые системы. - 2008. — № 10.
Глава 2 АРХИТЕКТУРЫ ПРОГРАММНЫХ СИСТЕМ 2.1. Понятие архитектуры программной системы С понятием «архитектура» каждый знаком еще со времени обучения в школе. Слово «архитектура» латинское (architectura), хотя и имеет гре- ческие корни ctpyt и T8KTOVIKT], что значит «высшее плотничное или строительное искусство». В этом смысле слову «архитектура» придава- ли очень обширное значение. Так, например, были в ходу выражения «военная архитектура», «корабельная архитектура», «гидротехническая архитектура», «ландшафтная архитектура» и т.д. В настоящее время под словом «архитектура» чаще всего понимается искусство градостроения, проектирования зданий и сооружений, предназначенных для помещения людей, животных или каких-либо предметов [8]. В начале развития информационных технологий термин «архитекту- ра» применялся только к аппаратному обеспечению (архитектура ЭВМ, вычислительных комплексов, сетей и др.) и лишь позже стал применять- ся к программному обеспечению. В настоящее время дисциплина про- граммных архитектур и их проектирования интенсивно развивается. В любой методологии проектирования ПС (пожалуй, кроме экстремаль- ного программирования) понятие архитектура занимает ключевое место [26]. У понятия «архитектура» в его применении к программному обеспе- чению и системам, базирующимся на ПО, достаточно долгая жизнь. На- чало этой «жизни» принято связывать с именами Э. Дейкстры [13], Ф. Брукса [10] и Д. Парнаса [23], заложивших основы структурного и объектно ориентированного программирования. Но «отсчет» совре- менного значения этого понятия принято начинать с 1992 г. с работы Д. Перри и А. Вульфа [22]. Когда речь заходит об архитектуре, обычно не возникает недостатка в определениях. Есть даже веб-сайты, которые собирают такие опреде- ления [6]. Стандарты IEEE Std 1472000 и IEEE 1471 «Рекомендуемые методы описания архитектуры преимущественно-программных систем» дают такое определение архитектуры [2]: «Архитектура - это базовая организации системы, воплощенная в ее компонентах, их отношениях между собой и с окружением, а также принципы, определяющие проек- тирование и развитие системы». Стандарт IEEE 1471 [2] определяет также представление архитекту- ры (architectural description) как согласованный набор документов, опи- сывающий архитектуру с точки зрения определенной группы заинтере- сованных лиц с помощью набора моделей. Архитектура может иметь несколько представлений, отражающих интересы различных групп за- интересованных лиц. Стандарт рекомендует для каждого представления фиксировать от- раженные в нем взгляды и интересы, роли лиц, которые заинтересованы в таком взгляде на систему, причины, обусловливающие необходимость 31
такого рассмотрения системы, несоответствия между элементами одно- го представления или между различными представлениями, а также раз- личную служебную информацию об источниках информации, датах соз- дания документов и пр. Стандарт ШЕЕ 1471 отмечает необходимость использования архи- тектуры системы для решения следующих задач: • анализ альтернативных проектов системы; • планирование перепроектирования системы, внесения измене- ний в ее организацию; • общение по поводу системы между различными организациями, вовлеченными в ее разработку, эксплуатацию, сопровождение, приобре- тающими систему или продающими ее; • выработка критериев приемки системы при ее сдаче в эксплуата- цию; • разработка документации по ее использованию и сопровожде- нию, включая обучающие и маркетинговые материалы; • проектирование и разработка отдельных элементов системы; • сопровождение, эксплуатация, управление конфигурациями и внесение изменений и поправок; • планирование бюджета и использования других ресурсов в про- ектах, связанных с разработкой, сопровождением или эксплуатацией системы. • проведение обзоров, анализ и оценка качества системы. В стандартах IEEE Std 1472000 и ШЕЕ 1471 определяются также сле- дующие термины, связанные с определением архитектуры. Система — это набор компонентов, объединенных для выполнения определенной функции или набора функций. Термин «система» охваты- вает отдельные приложения, системы в традиционном смысле, подсис- темы, системы систем, линейки продуктов, семейства продуктов, целые корпорации и другие агрегации, имеющие отношение к данной теме. Система существует для выполнения одной или более миссий в своем окружении (ШЕЕ 1471). Здесь несколько терминов, которые следует пояснить. Окружение, или контекст, определяет ход и обстоятельства эконо- мических, эксплуатационных, политических и других влияний на систе- му. Миссия — это применение или действие, для которого одно или не- сколько заинтересованных лиц планируют использовать систему в соот- ветствии с некоторым набором условий. Заинтересованное лицо - это физическое лицо, группа или органи- зация (или ее категории), которые заинтересованы в системе или имеют связанные с ней задачи. Говоря об архитектуре программных систем, прежде всего надо оп- ределить понятие «программная система». Распространено такое опре- деление: «Программная система — это совокупность системных, при- кладных и сервисных программ, обеспечивающих решение некоторой совокупности задач». 32
Программные системы могут входить в состав программных ком- плексов. Обычно это более масштабные системы в рамках больших тех- нических и организационных систем. Системы, в свою очередь, могут состоять из подсистем. Архитектурные решения необходимы на каждом из этих уровней. Любая система может рассматриваться с разных точек зрения: пове- денческой (динамической), структурной (статической), логической (удовлетворение функциональным требованиям), физической (распре- деленность), реализации (как детали архитектуры представляются в ко- де) и т.п. В результате можно рассматривать различные архитектурные представления (view). Архитектурное представление может быть опре- делено как частные аспекты программной архитектуры, рассматриваю- щие специфические свойства программной системы. Классическим источником комплекса архитектурных точек зрения и представлений, построенных в системе координат «вопрос — уровень детализации» является модель Захмана [12]. Каждое архитектурное представление является результатом ответа на вопрос («как?», «что?», «где?» и т.п.) в контексте необходимого уровня абстракции. Например, физическая модель данных (Physical Data Model) является ответом на вопрос «что?» в контексте технологической модели, а логическая мо- дель данных, отвечая на тот же вопрос, находится на один уровень абст- ракции выше — в контексте системной или логической модели. В информационных технологиях понятие архитектур используется в следующих областях: • функциональная архитектура (бизнес-архитектура); • системная архитектура (архитектура программных систем); • технологическая, или инфраструктурная, архитектура; • информационная архитектура (организация данных). Следует отметить, что в определениях архитектуры часто употребля- ется слово «компонент». Большая часть определений архитектуры не определяет термин «компонент». Стандарт IEEE 1471 намеренно ос- тавляет это понятие неопределенным, чтобы оно соответствовало мно- жеству толкований, возможных в отрасли. Компонент может быть логи- ческим или физическим, технологически независимым или технологи- чески связанным, крупно- или мелкогранулированным. Определение термина «компонент» по спецификации UML 2.0 дает- ся в достаточно широком смысле, что позволяет охватить различные элементы архитектуры [11]. Компонентом называется модульная часть системы, которая инкап- сулирует ее содержимое; воплощение компонента является замещае- мым в его окружении. Поведение компонента определяется в терминах предоставляемого и требуемого интерфейсов. Таким образом, компо- нент используется в качестве типа, соответствие которого описывается двумя интерфейсами: предоставляемым и требуемым (объединяя как статическую, так и динамическую их семантику). Хотя в отрасли программного обеспечения не существует общепри- знанного определения понятия «архитектура», имеет смысл рассмотреть 33
и другие определения, чтобы можно было установить сходство между ними. Рассмотрим следующие определения, в которых курсивом выде- лены основные характеристики термина в каждом отдельном определе- нии. Архитектура - это: 1) набор значимых решений по поводу организации системы про- граммного обеспечения; 2) набор структурных элементов и их интерфейсов, при помощи которых компонуется система, вместе с их поведением, определяемым во взаимодействии между этими элементами; 3) компоновка элементов в постепенно укрупняющиеся подсистемы, а также стиль архитектуры, который направляет эту организацию — эле- менты и их интерфейсы, взаимодействия и компоновку. Такое определение дает Ф. Крачтен — создатель RUP (Rational Unified Process) [15]. Архитектура программы или компьютерной системы - это структура или структуры системы, которые включают элементы про- граммы, видимые извне свойства этих элементов и связи между ними. [9]. Архитектура — это структура организации и связанное с ней пове- дение системы. Архитектуру можно рекурсивно разобрать на части, взаимодействующие посредством интерфейсов, связи, которые соединя- ют части, и условия сборки частей. Части, которые взаимодействуют че- рез интерфейсы, включают классы, компоненты и подсистемы [16]. Архитектура программного обеспечения системы или набора систем состоит из всех важных проектных решений по поводу структур про- граммы и взаимодействий между этими структурами, которые составля- ют системы. Проектные решения обеспечивают желаемый набор свойств, которые должна поддерживать система, чтобы быть успешной. Проектные решения предоставляют концептуальную основу для разра- ботки системы, ее поддержки и обслуживания [3]. В заключение приведем определение архитектуры программного обеспечения, данного в S WEB OK (Software Engineering Body of Knowledge) — документе, подготовленном комитетом Software Engineering Coordinating Committee, в который вовлечено сообщество IEEE Computer Society [7]. Назначение SWEBOK — в объединении зна- ний по программному обеспечению. По SWEBOK, в строгом значении архитектура программного обес- печения (software architecture) - описание подсистем и компонентов программной системы, а также связей между ними. Архитектура пыта- ется определить внутреннюю структуру получаемой системы, задавая способ, которым система организована или конструируется. Хотя приведенные определения несколько отличаются, можно ви- деть немалую степень сходства. Например, большинство определений указывают на то, что архитектура связана со структурой и поведением, а также только со значимыми решениями, может соответствовать неко- торому архитектурному стилю, на нее влияют заинтересованные в ней 34
лица и ее окружение, она воплощает решения на основе логического обоснования. Таким образом, можно видеть, что слова «архитектура программ- ной системы» обычно довольно согласованно понимаются специалиста- ми на уровне подсознания и достаточно несогласованно определяются. По мнению Н. Михайловского [18], сложилось два основных класса оп- ределений архитектур: конструктивный и идеологический. Основные идеологические определения архитектуры ПС таковы: 1) архитектура ПС — это набор решений, наиболее существенным об- разом влияющих на совокупную стоимость владения системой; 2) архитектура ПС — это набор ключевых решений, неизменных при изменении бизнес-технологии в рамках бизнес-видения. Оба эти определения согласованы в том смысле, что если ключевое решение приходится изменять при изменении бизнес-технологии в рам- ках бизнес-видения, то резко возрастает стоимость владения системой. Следствием этих определений является понимание важности принятия архитектуры системы как стандарта предприятия ввиду значимости ар- хитектурных решений и их устойчивости по отношению к изменениям бизнес-технологии. Еще одно важное следствие первого определения — архитектура системы на самом деле должна строиться еще на стадии технико-экокомического обоснования системы. Конструктивно архитектура обычно определяется как набор ответов на следующие вопросы: • что делает система; • на какие части она разделяется; • как эти части взаимодействуют; • где эти части размещены. 2.2. Почему важна архитектура Наверное, следующее высказывание подтвердит значимость архи- тектуры ПС [18]. Архитектура — это то, за что увольняют системного ар- хитектора и руководителя проекта. Кто-то может заметить, что проекты на самом деле проваливаются из-за неправильной организации процесса разработки. Не отрицая этого, можно сказать, что известно гораздо больше успешных систем, построенных в «кривом процессе» разработ- ки, чем успешных систем с «кривой архитектурой» Вначале остановимся на вопросе, почему важна архитектура про- граммной системы с технической точки зрения. В этом контексте следу- ет привести три основных фактора, которые отмечает Л. Басс [9]. 1. Взаимодействие между заинтересованными лицами. Программ- ная архитектура — это универсальная абстракция системы, на основе ко- торой все или почти все заинтересованные в системе лица могут искать взаимопонимания, вести переговоры, находить компромиссы. В этом плане архитектура - это общий язык, на котором можно сформулиро- вать, обсудить и решить те или иные проблемы на уровне, доступном для комплексного изучения человеком. 35
Каждое заинтересованное в программной системе лицо — заказчик, пользователь, руководитель проекта, программист и т.д. - требует вне- дрить в систему различные характеристики, основа для которых закла- дывается в архитектуре. Пользователю необходимо, чтобы система бы- ла надежной и доступной в любой момент. Заказчика интересует, чтобы программная система была реализована без расхождений с графиком и бюджетом. Менеджер озабочен не только соблюдением временных и финансовых ограничений, но и возможностью организации при помо- щи архитектуры относительно независимой работы различных групп разработчиков, их четкого и управляемого взаимодействия. Архитектор думает о том, какие стратегии позволят ему решить все эти задачи. 2. Начальные проектные решения. Программная архитектура содер- жит сведения о том, какие решения были приняты на ранних этапах раз- работки системы. Значимость подобного рода сведений не ограничива- ется удельным весом этих этапов относительно оставшихся операций разработки и сопровождения. Именно в это время впервые появляется возможность анализа проектных решений, определяющих дальнейшую разработку системы. Начальные проектные решения труднее всего принять, а по мере раз- работки — скорректировать. При этом последствия наиболее существен- ны. Архитектура определяет ограничения реализации. Реализацию сле- дует разделить на ряд установленных архитектурой элементов. Взаимо- действие между этими элементами должно производиться в установлен- ном архитектурой порядке. Обязательства каждого элемента в отноше- нии других элементов должны исполняться согласно требованиям архи- тектуры. Решения о распределении ресурсов также накладывают опре- деленные ограничения на реализацию. Проблема в том, что конструкторы, работающие над отдельными элементами, иногда не имеют об этих решениях ни малейшего пред- ставления. Рассматриваемые решения подразумевают разделение задач, благодаря которому решения менеджера в плане трудовых и вычисли- тельных ресурсов реализуются наилучшим образом. От конструкторов отдельных элементов требуется знание их спецификаций, однако во всем, что касается компромиссных архитектурных решений, они могут пребывать в полном неведении. Архитекторы, напротив, обычно не яв- ляются экспертами в области разнообразных аспектов построения алго- ритмов и тонкостей языков программирования, но именно они отвечают за принятие архитектурных компромиссов. Архитектура определяет организационную структуру. Стандарт- ный способ разделения труда по разработке крупной системы предпола- гает распределение отдельных частей системы между несколькими группами конструкторов, т.е. декомпозицию обязанностей. Поскольку в состав системной архитектуры входит высокоуровневая декомпозиция системы, именно она служит основой для декомпозиции обязанностей. Это определяет, в свою очередь, структуру планирования, определения сроков и бюджета, каналы межгруппового взаимодействия, организа- 36
цию управления конфигурациями и файловой системой, планы и проце- дуры интеграции и др. 3. Переносимая абстракция системы. Программная архитектура — это относительно небольшая, вполне доступная для человеческого вос- приятия модель структурирования системы и взаимодействия ее компо- нентов. Помимо прочего, эта модель обладает свойством переносимости из системы в систему. Например, ее можно применять в отношении дру- гих систем, для которых характерны примерно те же требования к атри- бутам качества и функциональным возможностям, и тем самым дать на- чало полноценному многократному применению. По мере накопления полезных архитектурных образов и образцов проектирования становится очевидным, что, несмотря на безграничные возможности комбинирования компьютерных программ, сведение вари- антов к относительно небольшому количеству альтернатив в контексте взаимодействия между программами и их сотрудничества может при- нести определенные выгоды. Таким образом, проектная сложность кон- струируемой программы сводится к минимуму. Среди преимуществ этого принципа следует отметить повышенные возможности многократ- ного применения, более стандартизованные и простые решения, кото- рые легче понять и распространять, углубление анализа, сокращение длительности отбора и совершенствование способности к взаимодейст- вию. 2.3. Как появляется архитектура. Кто и что влияет на архитектуру Любая архитектура является результатом принятия ряда экономиче- ских и технических решений [9]. Эти факторы влияния играют свою роль в процессе проектирования, а их конкретная реализация обуслов- ливается средой, в которой архитектура должна работать. Архитектор, которому для проектирования системы отводятся сжатые сроки в реаль- ном времени, принимает одни проектные решения. Тот же архитектор, не стесненный временными рамками, принимает уже другие решения. Еще один ряд решений принимается в ходе разработки системы вне ре- ального времени. Даже если архитектору предъявляют те же требова- ния, предоставляют то же оборудование, программное обеспечение и тот же штат, что и пять лет назад, сегодня, скорее всего, он будет при- нимать новые решения. В форме требований формулируются некоторые, но далеко не все желаемые свойства системы. С другой стороны, не все требования на- прямую затрагивают эти свойства. Например, требования могут регла- ментировать процесс разработки и применение тех или иных инстру- ментальных средств. Спецификация требований - это только начало. Существует масса других ограничений, которые необходимо учитывать при выборе варианта архитектуры программной системы. 1. Влияние на архитектуру оказывают заинтересованные в системе лица и организации. Они включают среди прочих заказчика, конечных 37
пользователей, разработчиков, руководителей проекта, специалистов по сопровождению и маркетингу. У разных заинтересованных лиц есть свои представления о свойствах системы. В частности, они высказыва- ют пожелания относительно поведения системы при прогоне, произво- дительности на тех или иных аппаратных средствах, настраиваемости, переносимости на другие платформы, быстрого выхода на рынок и низ- ких затрат на разработку, высокой заработной платы программистов со- ответствующих специальностей и т.д. Желательно, чтобы у системы были такие свойства, как высокая про- изводительность, надежность, готовность, совместимость с различными платформами, умеренное потребление памяти, возможность использо- вания в сети, безопасность, модифицируемость, практичность, способ- ность взаимодействия с другими системами и приемлемое поведение. Именно из этих свойств складывается архитектура. От этих ее свойств зависит, понравится ли система заинтересованным лицам. Трудность в том, что каждое заинтересованное лицо имеет свои представления о свойствах желаемой системы и свои задачи, которые могут иногда быть противоречивыми. Желаемые свойства системы можно изложить в таком артефакте, как перечень требований, что и дол- жен сделать заказчик. Однако такого рода документ, в котором все тре- бования сформулированы достаточно подробно, разработать непросто, потому и встречаются они довольно редко. Как правило, архитектору приходится исправлять такой документ и улаживать противоречия. 2. Влияние на архитектуру оказывает компания-разработчик. Воз- действие компании-разработчика не исчерпывается доработкой совме- стно с заказчиком требований, предъявляемых к системе. На архитекту- ру оказывают влияние структура и характер организации компании- разработчика [9]. Например, если в компании достаточно много про- граммистов, специализирующихся на клиент-серверных технологиях, то весьма вероятно, что руководство компании будет настаивать на разра- ботке клиент-серверной архитектуры. Факторы влияния, действующие в рамках компании-разработчика, делятся на три группы: краткосрочные экономические, долгосрочные экономические и организационные. Остановимся на краткой характери- стике этих факторов. Компании делают прямые инвестиции в различные активы, в частно- сти в существующие варианты архитектуры (разработанные ранее) и ос- нованные на них продукты. В этом случае каждый последующий проект мыслится как продолжение ряда подобных систем, а в его смету закла- дывается повторное использование имеющихся средств. Следуя своим стратегическим задачам, компании делают долгосроч- ные инвестиции в инфраструктуру. В таком случае предполагаемая сис- тема мыслится как одно из средств финансирования и расширения этой инфраструктуры. Определенное влияние на программную архитектуру оказывает ор- ганизационная структура компании-разработчика. Л. Басс [9] приводит пример разработки системы моделирования условий полета для ВВС 38
США, в которой компания-разработчик привлекла к работе несколько субподрядчиков, обладающих штатом специалистов в данной области. Специализация разработки подсистем стала возможной благодаря реа- лизованному в архитектуре разделению функций. 3. Влияние на архитектуру оказывают опыт и привычки архитек- торов. Если у архитектора есть опыт применения того или иного архи- тектурного решения, то, скорее всего, он будет его использовать в по- следующей работе. С другой стороны, если предыдущие попытки при- менения того или иного решения были неудачны, архитектора будет трудно убедить использовать это решение вновь. Решения в арсенале архитектора появляются по мере повышения образовательного уровня и накопления опыта использования архитектурных образцов и обраще- ния с системами, которые работали плохо или особенно хорошо. Кроме того, многие архитекторы имеют обыкновение экспериментировать с новыми образцами архитектуры и методиками, о которых они узнают из различных источников. 4. Влияние на архитектуру оказывает техническая база. Подготов- ка и опыт архитектора, в частности, проявляются в его работе с техниче- ской базой. Определенное воздействие на архитектуру оказывают суще- ствующие в настоящее время технические средства. Здесь может идти речь как о методах работы, принятых в данной отрасли, так и о приемах программной инженерии, распространенных в профессиональном сооб- ществе, в которое входит архитектор. 5. На архитектуру оказывает влияние ее окружение. Система раз- мещается в некотором окружении, и это окружение оказывает влияние на архитектуру. Окружение определяет границы, в которых должна ра- ботать система, а это влияет на архитектуру. Факторы окружения, влияющие на архитектуру, - это миссия бизнеса, которую будет поддер- живать архитектура; заинтересованные в системе лица; внутренние (стандарты организации) и внешние (необходимость взаимодействия с внешней системой) технические ограничения. 2.4. Архитектурные образцы, эталонные модели и эталонные варианты архитектуры Как отмечает Л. Басс, между простейшими «набросками» архитектур и комплексными архитектурами, укомплектованными всей необходи- мой информацией о системах, существуют многочисленные переходные этапы. Каждый из этих этапов представляет собой результат принятия ряда архитектурных решений, совокупность архитектурных альтерна- тив. Некоторые из них сами по себе имеют определенную ценность. В [9] отмечается три промежуточных этапа. 1. Архитектурный образец (шаблон) - это описание типов элемен- тов и отношений и изложение ряда ограничений на их использование. Образец имеет смысл рассматривать как совокупность ограничений, на- кладываемых на архитектуру, или, конкретнее, на типы элементов и об- разцы их взаимодействия. На основе этих ограничений складывается 39
ряд (или семейство) соответствующих им вариантов архитектуры. На- пример, одним из общеупотребительных архитектурных образцов явля- ется «клиент — сервер». Клиент и сервер — это два типа элементов. Их взаимодействие описывается посредством протокола, при помо- щи которого сервер взаимодействует со всеми своими клиентами. Об- разцу «клиент - сервер» соответствует бесчисленное множество вариан- тов архитектуры, в чем-то отличных друг от друга. Несмотря на то что архитектурный образец не является архитектурой, он содержит весьма полезный образ системы: он накладывает на архитектуру, а следова- тельно, систему полезные ограничения. В монографии [11] отмечается следующее. Образцы помогают при визуализации, специфицировании, конструировании и документирова- нии артефактов программной системы. Можно заниматься прямым про- ектированием системы, выбрав подходящий набор образцов и применяя их к абстракциям, специфичным для данной предметной области. На практике интерес представляют два вида образцов: образцы проектиро- вания и каркасы. В UML есть средства для моделирования и тех, и дру- гих. Отличие каркаса от образца проектирования заключается в том, что каркас — это архитектурный образец, предлагающий расширяемый шаб- лон для приложений в некоторой конкретной области. У образцов есть очень полезный аспект: они демонстрируют извест- ные атрибуты качества. Именно поэтому архитекторы выбирают образ- цы, исходя из определенных соображений, и во многих случаях выбор архитектурного образца является первым существенным решением ар- хитектора. Заметим, что синонимом термина «архитектурный образец» является термин «архитектурный стиль». 2. Эталонная модель — это разделение между отдельными блоками функциональных возможностей и потоков данных. По сути, эталонной моделью является стандартная декомпозиция известной проблемы на части, которые, взаимодействуя, способны ее разрешить. Поскольку эта- лонные модели имеют происхождение в опыте, их наличие характерно только для сформировавшихся предметных областей. Например, если разработчик может назвать стандартные элементы компилятора и объ- яснить, как эти элементы решают свою общую задачу, значит, он зна- ком с эталонной моделью этого приложения. 3. Эталонная архитектура - это эталонная модель, отображенная на программные элементы, которые сообща реализуют функциональ- ность, определенную в эталонной модели, и потоки данных между ни- ми. В то время как эталонная модель обеспечивает разделение функций, эталонная архитектура отображает эти функции на декомпозицию сис- темы. Соответствие может быть как однозначным, так и неоднознач- ным. В программном элементе может быть реализована как отдельная часть функции, так и несколько функций сразу. В заключение этого раздела заметим, что эталонные модели, архи- тектурные образцы и эталонные архитектуры не являются вариантами архитектуры. Это не более чем полезные понятия, способствующие фиксации отдельных элементов архитектуры. Каждый из них появляет- 40
ся как результат проектных решений, принимаемых на самых ранних этапах разработки программных систем. 2.5. Что определяет и на что влияет выбранная архитектура Свойства программного проекта во многом определяются выбран- ным архитектурным образцом. Те образцы, которые в наибольшей сте- пени подходят для решения конкретной задачи, повышают качество реализации конечного проектного решения. Так что же определяет и на что влияет выбранная архитектура [9, 14]? 1. Архитектура определяет структуру. Термин «структура» чаще всего используется при представлении архитектуры. Действительно, структура является важнейшей характеристикой архитектуры. Струк- турные аспекты архитектуры проявляются многими способами. Струк- турный элемент может быть подсистемой, процессом, библиотекой, ба- зой данных, вычислительным узлом, системой в традиционном смысле, готовым продуктом и т.д. Многие определения архитектуры признают не только сами структурные элементы, но и композиции из структурных элементов, их связи, любые соединительные звенья, необходимые для поддержки этих отношений, интерфейсы и разбиения. Каждый из этих элементов может быть представлен различными способами. Например, соединительное звено может представлять собой сокет, быть синхронным или асинхронным, быть связанным с конкрет- ным протоколом и т.д. Пример некоторых структурных элементов пока- зан на рис. 2.1. На нем изображена диаграмма классов UML, содержа- щая некоторые структурные элементы, которые представляют систему обработки заказов. Представлено три класса: OrderEntry, CustomcrMan- agement и AccountManagement. Класс OrderEntry зависит от класса Cus- tomcrManagement и от класса AccountManagement. Рис. 2.1. Структурные элементы в диаграмме классов 2. Архитектура определяет поведение. Наряду с определением структурных элементов любая архитектура определяет взаимодействие между этими структурными элементами. Это такие взаимодействия, ко- торые обеспечивают желаемое поведение системы. На рис. 2.2 пред- ставлена диаграмма сценария UML, которая показывает несколько взаи- модействий, которые в сумме позволяют системе поддерживать созда- ние заказа в системе обработки заказов. 41
На рис. 2.2 показано пять взаимодействий. Сначала деятель Sales Clerk создает заказ при помощи экземпляра класса OrderEntry. Экземп- ляр класса OrderEntry получает сведения о клиенте при помощи экземп- ляра класса CustomerManagcmcnt. Затем экземпляр класса OrderEntry использует экземпляр класса AccountManagcment для того, чтобы соз- дать заказ, внести в него элементы заказа, а затем разместить заказ. Сле- дует заметить, что рис. 2.2 согласуется с рис. 2.1 так, что можно извлечь зависимости, показанные на рис. 2.1, из взаимодействий, определенных на рис. 2.2. Эта зависимость отражается в отношении зависимости меж- ду соответствующими классами OrderEntry и CustomcrManagement, как показано на рис. 2.1. Рис. 2.2. Диаграмма сценария UML 3. Архитектура концентрируется на значимых элементах. Хотя ар- хитектура определяет структуру и поведение, она определяет не все в структуре и поведении. Она занимается только такими элементами, которые оцениваются как значимые. Значимые элементы - это такие элементы, которые имеют продолжительное и устойчивое действие, на- пример главные структурные элементы, элементы, связанные с основ- ным поведением, и элементы, которые определяют значимые свойства, такие как надежность и масштабируемость. Как правило, архитектура не имеет отношения к гранулярным деталям этих элементов. Архитек- турную значимость можно назвать также экономической значимостью, поскольку главный признак, по которому некоторые элементы оценива- 42
ются выше остальных, — это стоимость создания и стоимость измене- ния. Поскольку архитектура фиксируется только на значимых элементах, она предполагает конкретную перспективу оцениваемой системы, — пер- спективу, которая наиболее значима для разработчиков архитектуры. В этом смысле архитектура является некоторым обобщением системы, помогающим разработчику архитектуры управлять сложностью. Следу- ет также заметить, что набор значимых элементов не является статич- ным, он может изменяться с течением времени. Он может измениться при уточнении требований, идентификации рисков, создании исполняе- мой программы. Однако относительная стабильность архитектуры, не- смотря на изменения, в некоторой степени является признаком хорошей архитектуры, хорошо отлаженного процесса разработки и хорошего раз- работчика. Если архитектура требует постоянного пересмотра при отно- сительно небольших изменениях, это плохой признак. 4. Архитектура уравновешивает потребности заинтересованных лиц. Архитектура создается для удовлетворения комплекса потребно- стей заинтересованного лица. Однако часто невозможно выполнить все выраженные пожелания. Например, заинтересованное лицо может по- просить, чтобы некоторая функциональность укладывалась в опреде- ленный временной промежуток, но эти две потребности (функциональ- ность и промежуток времени) являются взаимоисключающими. Можно или уменьшить границы функции, чтобы она соответствовала расписа- нию, или предоставить полную функциональность, но за более продол- жительный промежуток времени. Принятие компромиссных решений является необходимым аспектом процесса разработки. Например, рассмотрим потребности нескольких заинтересованных в проекте лиц: • конечный пользователь заинтересован в интуитивно понятном и корректном поведении, производительности, надежности, удобстве использования, доступности и безопасности; • системный администратор заинтересован в интуитивно понятном поведении системы, управлении и инструментах мониторинга; • специалист по маркетингу заинтересован в конкурентоспособных функциях, времени до выхода программы, позиционировании среди других продуктов и в стоимости; • клиент заинтересован в цене, стабильности и возможности плани- ровать; • разработчик заинтересован в понятных требованиях и простом и непротиворечивом принципе проектирования; • руководитель проекта заинтересован в предсказуемости хода про- ектирования, планировании, продуктивном использовании ресурсов и бюджета; • специалист по сопровождению заинтересован в понятном, непро- тиворечивом и документируемом принципе проекта, а также в легкости, с которой можно вносить изменения. 43
Многие пункты из списка интересов являются нефункциональными, т.е. не влияют на функциональность системы (например, интерес по от- ношению к цене и планированию). Тем не менее такие интересы форму- лируют свойства или ограничения системы. Нефункциональные требо- вания очень часто являются самыми значимыми требованиями, по- скольку в них заинтересован разработчик архитектуры. Как видно из этого списка, заинтересованные лица не только хотят, чтобы система обеспечивала необходимую функциональность, но и предъявляют массу других нефункциональных требований. К тому же некоторые из требований противоречивы. Задача разработчика архитек- туры — обеспечить компромиссное решение. При выборе архитектуры необходимо учитывать основные факторы, определяющие: 1) трудоемкость разработки — масштаб; сложность (техническую и логическую); ясность целей; 2) основные цели разработки (цели продукта), вытекающие из требо- ваний к системе: эффективность; расширяемость (масштабируемость, облегчение добавления новых свойств); адаптируемость (облегчение смены требований); простота (понимания, реализации); надежность (от- сутствие ошибок); 3) цели проекта — удобство сопровождения; стоимость; сроки реали- зации и т.п. 5. Архитектура воплощает решение на основе логического обосно- вания. Важнейший аспект архитектуры— ее логическое обоснование. Важно обеспечить документирование решений, которые привели к соз- данию этой архитектуры, и логическое обоснование таких решений. Эта информация является значимой для многих заинтересованных лиц, осо- бенно для тех, кто должен обслуживать систему. Она имеет определен- ную ценность для разработчика архитектуры, когда ему нужно пере- смотреть логические обоснования принятых решений, чтобы избежать ненужного повторения своих действий. Эта информация используется при пересмотре архитектуры, а новому разработчику помогает понять принятые ранее решения. 6. Архитектура может соответствовать некоторому архитек- турному стилю. Архитектурный стиль определяет семейство систем в терминах шаблона организации структуры. Архитектурный стиль оп- ределяет номенклатуру компонентов и типов соединительных звеньев, а также набор условий, в соответствии с которыми они могут соеди- няться [14]. Большинство архитектур построено на основе систем, кото- рые используют сходные наборы интересов. Сходство может быть опре- делено как архитектурный стиль, который можно рассматривать как особый вид шаблона, хотя этот шаблон часто является сложным и со- ставным (при одновременном применении нескольких шаблонов). При- меры архитектурных стилей включают распределенный стиль, стиль «каналы и фильтры», стиль с централизованной обработкой данных, стиль, построенный на правилах, и т.д. Конкретная система может де- монстрировать более одного архитектурного стиля. Как и шаблон, ар- хитектурный стиль представляет собой кодификацию опыта, и для раз- работчиков архитектур желательно использовать этот опыт. 44
В терминах UML шаблон - это общее решение общей проблемы в данном контексте. Применение архитектурного стиля (или шаблона) несколько облегчает жизнь разработчиков, поскольку стиль обычно до- кументирован в терминах рационального обоснования его использова- ния (меньше придется обдумывать) и в терминах его структуры и пове- дения (придется выработать меньше документации по архитектуре, по- скольку вместо этого можно просто обратиться к стилю). 7. Архитектура определяет ограничения реализации. Реализация от- ражает архитектуру только в том случае, если она соответствует изло- женным в ней проектным решениям. Таким образом, реализацию следу- ет разделить на ряд установленных архитектурой элементов. Взаимо- действие между этими элементами должно производиться в установлен- ном архитектурой порядке. Обязательства каждого отдельного элемента в отношении других элементов также должны исполняться согласно требованиям архитектуры. Решения о распределении ресурсов также накладывают на реализа- цию определенные ограничения. Проблема в том, что конструкторы, ра- ботающие над отдельными элементами, иногда не имеют об этих реше- ниях ни малейшего представления. Рассматриваемые ограничения под- разумевают разделение задач, благодаря которому менеджерские реше- ния в плане трудовых и вычислительных ресурсов реализуются наилуч- шим образом. Понятно, что реализация программной системы во многом зависит от распределения ресурсов по различным работам. В этой части испол- нители, работающие над отдельными элементами системы, могут и не иметь четкого и правильного представления о решениях, принимаемых менеджерами. Ограничения реализации связанные с разделением задач, которые при правильном решении менеджерами приведут к наиболее эффективному использованию людских и вычислительных ресурсов, должны приниматься не архитекторами. Исполнителям отдельных эле- ментов разрабатываемой системы необходимо хорошее знание специ- фикаций этих элементов, а не знание важных архитектурных решений, в которых, без вреда общему процессу работы, они могут быть и не осве- домлены. С другой стороны, архитекторы, не являясь глубокими спе- циалистами в части выбора лучших алгоритмов и в знании языков про- граммирования, должны быть ответственными за архитектурные реше- ния. 8. Архитектура способствует или сдерживает реализацию атрибу- тов качества системы. Способность или неспособность системы реа- лизовать предполагаемые атрибуты качества в значительной степени обусловлена архитектурой. При этом важно понимать, что сама по себе архитектура не гарантирует ни функциональности, ни качества. На ка- чество системы влияют все решения, вне зависимости от того, на каком этапе ее жизненного цикла они принимаются, будь это этап высоко- 45
уровневого проектирования или этап кодирования. Следовательно, ка- чество нельзя признать полностью находящимся в зоне ответственности архитектурного проектирования. В контексте обеспечения качества хо- рошая архитектура необходима, но не достаточна. Можно ли, не дожидаясь окончания разработки и размещения систе- мы, утверждать, что те или иные архитектурные решения приняты вер- но? Будь ответ на этот вопрос отрицательным, задача выбора архитекту- ры потеряла бы всякий смысл: ее можно было бы выбрать произвольно! На самом деле существуют методики оценки архитектур. Так, в моно- графии Л. Басса [9] дан метод анализа архитектурных решений АТАМ (Architecture Tradeoff Analysis Method). 9. Архитектура облегчает анализ изменений и их организацию. В продолжение своего жизненного цикла программные системы обычно часто претерпевают изменения, которые во многих случаях реализуются с некоторыми трудностями. Любая архитектура предполагает разделе- ние всех возможных изменений на следующие категории: локальные, нелокальные и архитектурные. Для внесения локальных изменений дос- таточно откорректировать соответствующий отдельный элемент. Нело- кальное изменение требует корректировки нескольких элементов. Одна- ко базовые архитектурные принципы при этом остаются неизменными. Архитектурные изменения затрагивают сущность взаимодействия меж- ду элементами — образец архитектуры - и в большинстве случаев пред- полагают корректировку всей системы. Для принятия решений о необходимости внесения изменений, уста- новления наименее опасных путей, оценки последствий предполагае- мых изменений, определения последствий и расстановки приоритетов относительно требуемых изменений — для всего этого требуется подроб- ный анализ взаимоотношений, производительности и поведения про- граммных элементов системы. Все эти задачи прописаны в должност- ной инструкции архитектора. Средством приобретения знаний, доста- точных для принятия решений о предполагаемых изменениях, следует считать анализ архитектуры. 10. Архитектура облегчает эволюционное макетирование. Любую ар- хитектуру можно проанализировать и смоделировать в качестве макета. Процесс разработки от этого выигрывает в двух отношениях. Система становится исполняемой на раннем этапе жизненного цикла продукта. Ее точность повышается по мере замены элементов макета полноценными версиями программного обеспечения. Из того, что система довольно рано становится исполняемой, следует, что потенциальные проблемы, связан- ные с производительностью, выявляются на ранних этапах ее жизненного цикла. Каждый из этих факторов снижает риск разработки. 11. Архитектура позволяет более точно рассчитать стоимость и сроки создания системы. Рассчитав стоимость и составив график, руко- водитель может, во-первых, получить необходимые ресурсы, а во-вторых, узнать, удовлетворительно ли состояние проекта. Расчеты стоимости на основе блоков системы оказываются точнее тех, которые базируются на общих знаниях о системе. Организационная структура проекта определя- 46
ется его архитектурой. Расчеты по блокам лучше поручить не руководи- телю проекта, а участникам соответствующей группы разработчиков. Они, во-первых, справятся с этой задачей точнее, а во-вторых, будут ощу- щать ответственность за соблюдение установленных сроков. 12. Архитектура оказывает влияние на окружение. Создание архи- тектуры изменяет окружение с технологической точки зрения, может также изменить среду в терминах навыков, доступных в пределах орга- низации. Когда речь идет о преимущественно-программных системах, то следует учитывать конкретные аспекты окружения. Для того чтобы ПС работала, ее запускают на некотором аппаратном обеспечении. По- этому результирующая система представляет собой сочетание про- граммного и аппаратного обеспечения, и именно это сочетание позволя- ет добиться таких характеристик, как надежность и производитель- ность. Стандарты IEEE Std 12207- 1995, IEEE Standard for Information Technology - Software Life Cycle Process (стандарт IEEE по информаци- онным технологиям - процессы жизненного цикла приложения) опреде- ляют систему иначе, чем стандарт IEEE 1471, который концентрируется на преимущественно-программных системах [14]. Системой называется интегрированный комплекс, состоящий из од- ного или более процессов, аппаратных устройств, программ, средств и людей, предоставляющий возможность удовлетворить данную по- требность или условие. Техническое описание «А configuration of the Rational Unified Process for System Engineering (RUP SE)» [1] содержит похожее определение: системой называется набор ресурсов, предоставляющий сервисы, кото- рые используются предприятием для выполнения цели или миссии биз- неса. Компоненты системы обычно состоят из аппаратного обеспече- ния, программного обеспечения, информационных ресурсов и сотруд- ников. В области системного проектирования компромисс достигается за счет реализации системы в большей или меньшей степени теми или иными компонентами. Так, если ключевой фактор — производитель- ность, то принимается решение реализовать определенные элементы системы аппаратными устройствами. В другом случае, чтобы предста- вить удобную для покупателей систему, принимается решение предста- вить клиентский интерфейс не программой, а человеком. Интересно отметить, что системное проектирование особенно заинте- ресовано в том, чтобы рассматривать программное, аппаратное обеспече- ние и людей как равноценные понятия, избегая, таким образом, неверных заключений, в которых аппаратные устройства рассматриваются как эле- менты второго сорта по сравнению с программными, или наоборот. 13. Архитектура оказывает влияние на структуру и задачи компа- нии-разработчика. Архитектура обусловливает структуру системы, в частности набор блоков программ, которые надлежит реализовать, а потом интегрировать в рамках системы. Группы разработчиков уком- плектовываются именно по блокам; операции в рамках процессов разра- ботки, тестирования и интеграции также выполняются в отношении блоков. Согласно графикам и бюджетам, ресурсы выделяются частями на отдельные блоки. Если компания наработала опыт конструирования 47
семейств сходных систем, она будет вкладывать средства в повышение профессионального уровня разработчиков. Следовательно, группы встраиваются в структуру организации. Каждая из этих групп может требовать различных наборов навыков. Поэтому имеет смысл выровнять структуры групп разработчиков про- граммного обеспечения в соответствии с архитектурой, после того как она будет определена. Однако чаще встречается ситуация, когда перво- начальная группа разработчиков оказывает влияние на архитектуру, а не наоборот. Это ошибка, которой следует избегать, поскольку в результа- те обычно получается архитектура, далекая от идеала. «Закон Конвея» утверждает, что «если у вас есть четыре группы, работающие над ком- пилятором, то вы получите четырехпроходный компилятор». Практиче- ски часто непреднамеренно создаются архитектуры, которые являются отражением организации, создающей архитектуру [14]. Однако так же справедливо будет сказать, что это несколько идеали- зированное представление не всегда реально, поскольку по причинам чисто прагматичным реальная структура коллектива и доступные навы- ки представляют весьма ощутимое ограничение того, что могло бы быть, и разработчику следует это учитывать. Архитектура способна оказывать воздействие на задачи компании- разработчика. Сконструированная на основе определенной архитектуры успешная система предоставляет компании возможность укрепиться в данном сегменте рынка. Такая архитектура предусматривает дальней- шее эффективное производство, вследствие чего компания может откор- ректировать свои задачи и, воспользовавшись своим новым преимуще- ством, занять соответствующую нишу рынка. Таким образом, существу- ет обратная связь от системы к компании-разработчику и конструируе- мым ею системам. 14. Архитектура помогает в процессе обучения. Архитектура, со- держащая описание взаимодействия элементов в рамках требуемого по- ведения, может послужить своеобразной инструкцией, вводящей новых участников проекта в курс дела. Этим дополнительно подтверждается утверждение о том, что одной из основных функций программной архи- тектуры является организация и содействие общению между представи- телями различных заинтересованных групп. 15. Архитектура представлена в каждой системе. Каждая система имеет архитектуру, даже если эта архитектура формально не документи- рована или система слишком проста. Обычно документирование архи- тектуры представляет собой очень ценное средство. Документирован- ные архитектуры имеют тенденцию быть более продуманными, а следо- вательно, более эффективными, чем недокументированные, поскольку процесс записи архитектуры естественным образом ведет к всесторон- нему обдумыванию. Если архитектура не документируется, трудно (ес- ли не невозможно) доказать, что она соответствует утвержденным тре- бованиям в терминах адресных характеристик, таких как удобство об- служивания, заимствование передового опыта и т.д. В заключение этого раздела следует отметить, что архитектура име- ет особую область применения [14]. В области разработки программно- 48
го обеспечения можно встретиться с различными формами архитекту- ры, Например, помимо понятия «архитектура программного обеспече- ния» можно столкнуться с такими понятиями, как «корпоративная архи- тектура», «системная архитектура», «организационная архитектура», «архитектура информации», «архитектура аппаратного обеспечения», «архитектура приложения», «архитектура инфраструктуры» и т.д. Мож- но услышать также и другие термины, каждый из которых определяет особую область разработки архитектуры. К сожалению, в отрасли IT-технологий не существует соглашения о значении каждого из этих терминов или их отношениях друг к другу, в результате чего одни и те же термины могут иметь разные значения (омонимы), а два или более терминов могут обозначать одно и то же (синонимы). Однако об области применения некоторых из этих терми- нов можно сделать вывод на основании рис. 2.3. Если внимательно изу- чить рисунок и описание, то определенно можно найти элементы, с ко- торыми вы не согласны, или элементы, которые в вашей организации используются по-другому. Но в этом и смысл — показать, что термины используются в отрасли, но по поводу их значений нет согласия. Элементы, показанные на рисунке 2.3: • архитектура программного обеспечения (software), главная тема данного раздела, как было определено выше; • архитектура аппаратного обеспечения (hardware), которое включа- ет такие элементы, как ЦПУ, память, жесткие диски, периферийные уст- ройства, например принтер, а также элементы, используемые для их со- единения; • архитектура организации (organization), которая включает элемен- ты, имеющие отношение к бизнес-процессам, структурам организации, ролям и ответственности, а также основные области специализации ор- ганизации; • структура информации (information), включающая структуры, ко- торые упорядочивают информацию. Корпоративная информационная система ’ Архитектура программного обеспечения Архитектура аппаратного обеспечения Архитектура организации Банки данных Рис. 2.3. Основные термины ИТ-отрасли 49
Архитектура программного обеспечения, архитектура аппаратного обеспечения, архитектура организации и архитектура информации явля- ются подмножеством целостной архитектуры системы (system). Архи- тектура корпорации (enterprise) похожа на архитектуру системы тем, что тоже включает элементы, а именно: аппаратное и программное обеспе- чение и людей. Однако архитектура корпорации более сильно связана с бизнесом, так как она концентрируется на достижении бизнес-целей и занимается такими объектами, как быстрое реагирование на возни- кающие ситуации и эффективность организации. Архитектура корпора- ции может выходить за границы компании. Как и следовало ожидать, существуют корреспондирующие формы, например разработчик архитектуры (разработчик архитектуры про- граммного обеспечения, разработчик архитектуры аппаратного обеспе- чения и т.д.) и разработка архитектуры (например, разработка архитек- туры программного обеспечения, разработка архитектуры аппаратного обеспечения и т.д.). После рассмотрения этих определений многие во- просы остались без ответа. В чем заключается разница между архитектурой корпорации и архи- тектурой системы? Является ли корпорация системой? Архитектура ин- формации и архитектура данных, которая встречается в некоторых пре- имущественно-информационных программах, - это одно и то же? К со- жалению, в отрасли программного обеспечения нет непротиворечивых определений этих терминов и их взаимосвязей. Следовательно, можно рекомендовать выбирать термины, подходящие для конкретной органи- зации, и соответствующим образом их определять. Тогда, по крайней мере, можно добиться некоторой непротиворечивости и уменьшить ве- роятность недопонимания между заинтересованными лицами. 2.6. Архитектурные структуры и представления Современные программные системы настолько сложны, что разби- рать их в комплексе крайне сложно. Приходится концентрировать вни- мание на одной или нескольких структурах программной системы. Для рассуждения об архитектуре программной системы нужно определиться с тем, какая структура в данный момент является предметом обсужде- ния, о каком представлении архитектуры идет речь. Рассматривая представления архитектуры, часто употребляют свя- занные между собой понятия структуры (structure) и представления (view) [9]. Представление - это отображение ряда связанных архитек- турных элементов в том виде, в котором им оперируют заинтересован- ные в системе лица. В нем фиксируется отображение совокупности эле- ментов и установленных между ними связей. Структура же — это собст- венно ряд элементов, существующих в раках программного или аппа- ратного обеспечения. В частности, модульная структура представляет собой набор модулей системы с указанием их организации. Модульное представление есть отображение этой структуры, документируемое и применяемое теми или иными заинтересованными лицами. Нужно 50
сказать, что эти термины («структура» и «представление») часто ис- пользуются как синонимы. По классификации, предложенной в [9], архитектурные структуры подразделяются на три общие группы, в каждую из которых включают- ся элементы определенного характера. 1. Модульные структуры. Элементами таких структур являются мо- дули - блоки реализации. Модули предполагают рассмотрение системы с точки зрения программного кода. Модульные структуры позволяют отвечать на такие вопросы, как: «Какие функциональные требования выполняет данный модуль?», «К каким программным элементам он мо- жет обращаться?», «Какое программное обеспечение он фактически ис- пользует?», «Между какими модулями установлены отношения обобще- ния или специализации (например, наследования)?» 2. Структуры «компонент и соединитель». В данном случае эле- ментами являются компоненты (основные единицы вычислений) и со- единители (инструменты взаимодействия между компонентами) перио- да прогона (выполнения). Среди вопросов, на которые отвечают струк- туры «компонент и соединитель», такие, например, как: «Каковы основ- ные исполнительные компоненты и как происходит их взаимодейст- вие?», «Каковы основные совместно используемые хранилища дан- ных?», «Какие части системы воспроизводятся?», «Каким образом по системе проходят данные?», «Какие элементы системы способны вы- полняться одновременно?», «Какие структурные изменения происходят в системе во время ее исполнения?» 3. Структуры распределения. Структуры распределения демонстри- руют связь между программными элементами, с одной стороны, и эле- ментами одной или нескольких внешних сред, в которых данное про- граммное обеспечение создается и исполняется, - с другой. Они отвеча- ют на вопросы: «На каком процессоре исполняется данный программ- ный элемент?», «В каких файлах каждый элемент хранится в ходе раз- работки, тестирования и конструирования системы?», «Каким образом программные элементы распределяются между группами разработчи- ков?» Перечисленные три структуры соответствуют трем универсальным типам решений, применяемым в ходе архитектурного проектирования. • Каким образом следует структурировать совокупность блоков ко- да (модулей) системы? • Каким образом следует структурировать совокупность элементов системы, обладающих поведением (компоненты) и демонстрирующих взаимодействие (соединители) в период прогона? • Каким образом установить связи между системой и непрограмм- ными структурами среды (например, с процессорами, файловыми систе- мами, сетями, группами разработчиков и т.д.)? Наиболее распространенные и полезные программные структуры можно разделить на три гуппы: модульные, компонент и соединитель и распределение [9]. Модульные структуры делятся на следующие разновидности. 51
1. Декомпозиция. В качестве блоков выступают модули, между кото- рыми установлены отношения «является подмодулем...». Таким обра- зом, крупные модули в рекурсивном порядке разлагаются на меньшие, и этот процесс завершается только тогда, когда меньшие модули стано- вятся вполне понятными. Модули в рамках этой структуры часто ис- пользуются в качестве отправной точки для последующего проектиро- вания: архитектор перечисляет блоки, с которыми ему предстоит рабо- тать, и в расчете на более подробное проектирование, а также реализа- цию распределяет их между модулями. Структура декомпозиции в зна- чительной степени обеспечивает модифицируемость системы, при этом складывается ситуация, когда наиболее вероятные изменения приходят- ся на долю нескольких небольших модулей. 2. Варианты использования. Блоками этой важной, но не слишком распространенной структуры могут быть либо модули, либо (когда тре- буется более мелкая структура) процедуры или ресурсы интерфейсов модулей. Между такими блоками устанавливаются отношения исполь- зования. Структура использования полезна при конструировании сис- тем, которые легко расширяются дополнительными функциями либо предлагают возможность быстрого извлечения полезных функциональ- ных подмножеств. Способность без труда разбить рабочую систему на ряд подмножеств подразумевает возможность инкрементной разработ- ки - многофункционального конструкторского приема. 3. Многослойные. Если отношения использования в рамках этой структуры находятся под строгим, особым образом осуществляемым контролем, возникает система слоев— внутренне связанных наборов родственных функций. В рамках строгой многослойной структуры уро- вень N может обращаться к услугам только в том случае, если они пред- ставлены слоем N-1. На практике это правило существует в виде много- численных вариантов, которые частично снимают приведенное струк- турное ограничение. Слои во многих случаях проектируются в виде аб- стракций (виртуальных машин), которые, стараясь обеспечить перено- симость, скрывают детали своей реализации нижележащих слоев от вы- шележащих. 4. Класс или обобщение. Блоки модулей в рамках этой структуры на- зываются классами. Отношения между ними строятся по образцам «на- следуют от...» и «являются экземпляром...». Данное представление способствует анализу коллекций сходного поведения или сходных воз- можностей (например, классов, которые наследуют от других классов) и параметрических различий, фиксация которых производится путем определения подклассов. Структура классов позволяет анализировать вопросы повторного использования и инкрементного введения функ- циональности. Структуры «компонент и соединитель». Среди структур данного ви- да выделяются следующие структуры. 1. Процесс или сообщающиеся процессы. Подобно другим структу- рам «компонент и соединитель», эта является ортогональной по отно- шению к модульным структурам, поскольку она связана с динамически- ми аспектами исполняемой системы. В качестве блоков в данном случае 52
выступают процессы или потоки, связь между которыми устанавливает- ся путем передачи данных, синхронизации и/или операций исключения. Здесь, как и во всех остальных структурах «компонент и соединитель», действует отношение прикрепления, демонстрирующее связь компонен- тов друг с другом. Структура процессов существенно облегчает конст- руирование рабочей производительности и готовности системы. 2. Параллелизм. Данная структура позволяет архитекторам выявлять перспективы параллелизма и локализовать возможности состязаний за ресурсы. В качестве блоков выступают компоненты, а соединители иг- рают роль логических потоков. Логическим потоком называется такая последовательность вычислений, которую впоследствии, в ходе процес- са проектирования, можно связать с отдельным физическим потоком. Структура параллелизма задействуется на ранних этапах проектирова- ния и способствует выявлению требований к организации параллельно- го исполнения. 3. Совместно используемые данные или репозиторий. В состав дан- ной структуры входят компоненты и соединители, обеспечивающие соз- дание, хранение и обращение к данным постоянного хранения. Она наи- лучшим образом приспособлена к таким ситуациям, когда система структурирована на основе одного или нескольких репозиториев совме- стно используемых данных. 4. Клиент — сервер. Эта структура предназначена для систем, скон- струированных в виде группы взаимодействующих клиентов и серве- ров. В качестве компонентов в данном случае выступают клиенты и сер- веры, а соединителями являются протоколы и сообщения, которыми они обмениваются в процессе обеспечения работоспособности системы. Такая структура требуется для разделения задач, физического распреде- ления и выравнивания нагрузок. Структуры распределения. Среди этих структур можно выделить следующие структуры. 1. Размещение. Структура размещения отражает распределение про- граммной системы между элементами аппаратной обработки (компью- терами) и передачи данных. Отношения устанавливаются по распреде- лению и демонстрируют физические устройства, на которых размеща- ются программные элементы. Возможны также отношения миграции в случае динамического распределения, например, в системах виртуаль- ных машин. Настоящее представление позволяет инженерам анализиро- вать производительность, целостность данных, готовность и безопас- ность. Все эти характеристики чрезвычайно важны в условиях распреде- ленных и параллельных систем. 2. Реализация. Данная структура демонстрирует отображение про- граммных элементов (обычно модулей) на файловую структуру (струк- туры) в условиях разработки системы, интеграции и управления конфи- гурациями. Это крайне важно в контексте разработки и процессов кон- струирования. 3. Распределение функций. Данная структура обеспечивает распреде- ление обязанностей по реализации и интеграции модулей между соот- 53
ветствующими группами разработчиков. Наличие в составе архитекту- ры структуры распределения функций делает очевидным, что при при- нятии соответствующих решений учитывались как архитектурные, так и организационные факторы. Архитектору должно быть известно, какие именно навыки требуются от разных групп разработчиков. 2.7. Отношения между структурами Из рассмотренного определения архитектуры и архитектурных структур становится совершенно понятным, что все программные сис- темы (и вообще системы) состоят из множества структур. Как правило, структура системы анализируется с точки зрения ее функциональности. При этом часто забывают о других ее свойствах: физическом распреде- лении, взаимодействии процессов и синхронизации и др. Все эти свой- ства обязательно должны учитываться на уровне архитектуры. Каждая структура содержит метод анализа тех или иных атрибутов качества. К примеру, чтобы создать легко расширяемую или сокращаемую систе- му, необходимо сконструировать структуру использования (именно сконструировать, а не просто зафиксировать). Структура процессов кон- струируется с целью исключения взаимоблокировки и расширения «уз- ких мест». Структура декомпозиции модулей конструируется в расчете на производство модифицируемых систем и т.д. Все перечисленные структуры предполагают различные представле- ния и подходы к проектированию программной системы. Элементы од- ной структуры связаны с элементами прочих, и на эти отношения следу- ет обратить внимание. В частности, модуль в структуре декомпозиции может декларироваться как отдельный компонент, как часть отдельного компонента или несколько компонентов в рамках одной из структур «компонент и соединитель», отражая, таким образом, своего представи- теля периода прогона. В то же время каждая рассмотренная структура снабжает архитекто- ра оригинальным представлением системы и дополнительной базовой точкой проектирования. Заметим, что современные CASE-средства про- ектирования архитектуры программных систем, например Rational Software Architect, реализуют именно подобный подход при разработке программных систем, позволяя представлять архитектуру системы мно- жеством взаимосвязанных моделей [4]. Иногда в рамках отдельных проектов одна структура считается ос- новной, а остальные структуры, если это возможно и нужно, определя- ются в ее категориях. Часто, хотя и не всегда, в качестве доминантной структуры выступает декомпозиция модулей. Объясняется это вполне разумно: декомпозиция модулей порождает структуру проекта. Структуры - это основные базовые точки конструирования архитек- туры. Отдельные структуры отвечают за те или иные атрибуты качест- ва. Они выражают принцип разделения задач при создании архитекту- ры, а также при ее последующем анализе и изложении для заинтересо- ванных лиц. Например, если разработчик программного обеспечения желает внести в клиент-серверную структуру системы какие-либо измс- 54
нения, он должен учитывать представления процессов и размещения. Дело в том, что клиент-серверные механизмы, как правило, задействуют процессы и потоки, а физическое распределение реализует различные механизмы управления, которые оказываются необходимыми в случае локализации процессов на одной машине. При необходимости измене- ния механизмов управления оценить степень их корректировки помога- ют представление декомпозиции модулей и многоуровневое представ- ление. 2.8. Варианты архитектур программных систем Далее рассматриваем доминантные архитектуры программных сис- тем в смысле, определенном в предыдущее разделе. Архитектуры про- граммных систем менялись по мере развития информационных техно- логий. В 60-е гг. XX в. значительный процент систем составляли тради- ционные пакетные системы, которые представляли собой ряд автоном- ных последовательных программ, общающихся через файлы во вторич- ной памяти. Выделялись лишь два похода к построению систем: метод уровней абстракции и метод портов [17]. Позже получил развитие мо- дульно-интерфейсный подход — один из методов структурного проекти- рования, затем подходы, основанные на потоках данных, независимых компонентах, объектно ориентированном проектировании и др. Цель данного раздела — рассмотрение некоторых типичных вариан- тов архитектур программных систем на основе классификации, приве- денной в [9, 17] и материалах других публикаций [24, 25]. 2.8.1. Архитектура, основанная на уровнях абстракций Ввиду важности концепции абстракций и широкого использования этой концепции в разработке архитектур различных сложных систем остановимся на этой концепции достаточно подробно. Наиболее мощ- ный инструмент, дающий разработчику возможность справиться со сложностью системы, - это понятие абстракции. Абстракция представ- ляет собой описание системы или ее части, в котором не указываются абсолютно все детали. Она обеспечивает разработчику общий, «макро- скопический» обзор системы, в силу чего дает представление о глобаль- ных связях и свойствах основных элементов системы, которое трудно достичь, когда учитываются все подробности. Не существует такого понятия, как вполне определенная, единствен- ная абстракция конкретной системы. Для каждой системы имеется мно- жество возможных абстракций. Каждая абстракция даст разработчику различную точку зрения на то, как выглядит система и что она делает. Абстракции системы могут быть тесно связаны. Так, например, одна аб- стракция а(2) может быть уточнением абстракции «(1). Если абстракция а(2) уточняет абстракцию л(1), то абстракция описывает все, что описы- вает абстракция а( 1), но уровень детализации в абстракции а(2) всегда 55
не меньше, чем в абстракции «(1)- Другими словами, абстракция а(2) яв- ляется равномерно более детализированным описанием, чем л(1). Пусть а(1), л(2), ..., а(п) — такой ряд абстракций, что для каждого i абстракция a(i + 1) - уточнение предыдущей абстракции a(i). Каждая абстракция a(i) представляет собой полное, хотя и не обязательно под- робное описание всей системы. Когда абстракции упорядочены таким образом, имеет смысл говорить об уровне абстракции, т.е. о степени де- тализации описания. Концепция уровней абстракции была предложена Э. Дейкстрой в 1968 г. [17]. Программа разбивается на различные иерархически упо- рядоченные части £(1), £(2), ..., £(«), называемые слоями (уровнями), удовлетворяющие определенным проектировочным критериям. Каждый уровень является группой тесно связанных модулей. Идея уровней при- звана минимизировать сложность системы за счет такого их определе- ния, которое обеспечивает высокую степень независимости уровней друг от друга. Это достигается благодаря тому, что свойства определен- ных объектов такой системы (ресурсы, представления данных и т.п.) убираются внутрь каждого уровня, что позволяет рассматривать каж- дый уровень как абстракцию этих объектов. На рис. 2.4 и 2.5 показаны две возможные (общие) структуры таких уровней. На рис. 2.4 показан такой подход, когда задача рассматривается как создание машины пользователя начиная с самого низшего уровня аппа- ратуры (или, возможно, операционной системы). Последовательность уровней, называемых абстрактными машинами, определяется так, что каждая следующая машина строится на предыдущих, расширяя их воз- можности. Каждый уровень может ссылаться только на один, отличный от него самого уровень (вызывать его), а именно тот, который непо- средственно ему предшествует. Рис. 2.4. Вариант классической структуры В структуре, изображенной на рис. 2.5, уровни не являются полными абстракциями более низших уровней, каждый из них может ссылаться на любой предшествующий уровень. 56
Рис. 2.5. Вариант произвольной структуры Перечислим свойства уровней абстракции [17, 25]. 1. На каждом уровне абсолютно ничего не известно о свойствах (и даже существовании) более высоких уровней. Это фундаментальное свойство уровней абстракции, существенно сокращающее число связей между частями программной системы и их предположений относитель- но свойств друг друга, благодаря чему уменьшается сложность системы. 2. На каждом уровне ничего не известно о внутреннем строении дру- гих уровней. Связь между уровнями осуществляется только через жест- кие, заранее определенные сопряжения. 3. Каждый уровень представляет собой группу модулей (раздельно компилируемых подпрограмм). Некоторые из этих модулей являются внутренними для уровня, т.е. недоступны для других уровней. Имена остальных модулей известны на следующем, более высоком уровне. Эти имена представляют собой сопряжение с этим уровнем. 4. Каждый уровень располагает определенными ресурсами и либо скрывает их от других уровней (более низких), либо предоставляет дру- гим (более высоким) некоторые их абстракции. Например, в системе управления файлами один из уровней может содержать физические файлы, скрывая их организацию от остальных частей системы. Другие уровни могут владеть такими ресурсами, как каталоги, словари данных, механизмы защиты. 5. Каждый уровень может обеспечивать некоторую абстракцию дан- ных в системе. Например, запись в базе данных - это абстракция табли- цы данных, хранящаяся в некотором файле. 6. Предположения, которые делаются на каждом уровне относитель- но других уровней, должны быть минимальны. Эти предположения мо- гут принимать вид соотношений, которые должны соблюдаться перед выполнением функции, либо относиться к представлению данных или факторов внешней среды. 7. Связи между уровнями ограничены явными аргументами, переда- ваемыми с одного уровня на другой. Недопустимо использование гло- бальных данных несколькими уровнями. Более того, желательно полно- стью исключить использование глобальных данных (даже внутри уров- ня) в системе. 57
8, Каждый уровень должен иметь высокую прочность и слабое сцеп- ление (далее остановимся на этом более подробно). Это значит, что вся- кая функция, выполняемая уровнем абстракции, должна быть представ- лена единственным входом. Аргументы, пересылаемые между уровня- ми, должны быть отдельными элементами данных, а не сложными структурами. Одной из первых программных систем, основанных на уровнях абст- ракции, была операционная система THE [17, 25]. Она является пакет- ной системой с мультипрограммированием и виртуальной памятью. Система включает в себя пять уровней абстракций, структура которых близка к изображенной на рис. 2.5. Почему выбрана эта схема, а не схе- ма по рис. 2.4? На рис. 2.4 и 2.5 изображена последовательность слоев А(0), £(1), ..., А(н), причем Л(0) — аппаратура, a L( 1) — первый слой программ. Рассмот- рим вариант, когда слой £(/), может иметь доступ только к средствам виртуальной машины, определяемой слоем L(i - 1). Если слою L(i) по- требуется какое-либо средство слоя L(i — 2), то слой L(i - 1) должен так- же предоставить это средство. Рассмотрим теперь другой вариант, когда слой £(z), может использо- вать все средства, предоставляемые слоями А(1), £(2), ..., А(г-1). Таким образом, виртуальная машина, определяемая слоем £(7), включает в себя все команды до слоя L(i) и команды, реализуемые слоем L(i). Третий ва- риант является промежуточным между двумя первыми. В этом случае слою L(i) разрешается использовать только некоторые из команд, обес- печиваемых слоями A(l), £(2), 1). В некоторых системах (например, в операционных системах) первый слой является единственным слоем, который может выдавать привиле- гированные команды аппаратному оборудованию. На рис. 2.6 представ- лена возможная многоуровневая иерархическая структура операцион- ной системы, в которой машинно-зависимые модули ядра (слой £(1)) и базовые механизмы ядра (слой L(2)) используют привилегированные команды процессора. Другие слои могут выполнять непривилегированные команды плюс команды нижележащих слоев в соответствии с одним из трех рассмот- ренных выше вариантов. Каждый вариант имеет свои достоинства и недостатки. Для их рас- смотрения обратимся крис. 2.7. Здесь показаны два варианта возмож- ной структуры операционной системы: слева (рис. 2.7 а))— вариант структуры с многоуровневым модульным ядром, справа (рис. 2.7 б)) — вариант структуры с компактным микроядром [19]. Если в варианте 2.7 а) каждый слой имеет доступ к командам только одного слоя, разра- ботчик должен иметь в виду только предыдущий слой. Хотя с точки зрения проектирования этот вариант кажется привлекательным, он мо- жет оказаться очень неэффективным. 58
Утилиты, системные программы Приложения пользователей Интерфейс системных вызовов API Менеджеры ресурсов Файловая система, вирт память Базовые механизмы ядра Машин независим ые модули ядра ОС Средства аппаратной поддержки ОС ядра Рис. 2,6. Многоуровневая иерархическая структура операционной системы Например, если некоторое средство, предоставляемое слоем А(2) (ба- зовые механизмы ядра), потребуется в слое L(i)9 то каждый из слоев А(3), Л(4), L(i— 1) должен обеспечить это средство. Это значит, что запрос данного средства слоем L(i) должен «просачиваться» вниз через слой L(i — 1), пока не достигнет слоя £(2), который способен выполнить запрос. Эти трудности, связанные с проблемой эффективности, могут склонить к принятию структуры, в которой каждый слой Л (г), где 2 < i < л, может непосредственно обращаться к слою А (2), реализующему базо- вые механизмы ядра. Так, например, организована операционная систе- ма Windows 2000/2003/ХР/7 (рис. 2.8) из [20]. Рассмотрим теперь вариант по рис. 2.7 б). В данном случае выделено три слоя: два — в микроядре, третий — остальные модули системы. При этом если какой-либо компонент (модуль) третьего слоя потребует функцию другого компонента этого же слоя, то такая услуга может быть получена только через микроядро. В данном случае взаимодейст- вие модулей слоев организовано по принципу клиент-серверной архи- тектуры (рис. 2.9), эффективность которого может быть выше по срав- нению с вариантом по рис. 2.7 а) при условии доступа модулей слоя L(i) только к слою L(i— 1). 59
I £ П[ 5 s R Ш Cl, I Утилиты ОС, приложения Интерфейс системы ввода-вы вода Менеджер виртуальной памяти Менеджер процессов Базовые механизмы ядра Машинно-зависимые модули Средства аппаратной поддержки ОС Аппаратура а) многоуровневое ядро б) микроядернос ядро Рис. 2.7. Варианты структур операционных систем Однако производительность микроядерной ОС в целом ниже произ- водительности архитектуры, приятой в упомянутых системах Windows 2000/2003/ХР (это же относится и Windows 7/8) по причине возможного многократного переключения работы компьютера из пользовательского режима в режим ядра при выполнении системных вызовов (рис. 2.10). В заключение данного раздела отметим, что можно построить древо- видную структуру слоев системы (рис. 2.11), в которой каждый слой мо- жет иметь доступ только к тем слоям, которые соответствуют его пред- шественникам по дереву. Дерево частей такой программы представляет на самом деле дерево виртуальных машин. Такая организация была впервые использована при создании ядра операционной системы SUE, разработанной в Университете Торонто в 1972 г. для семейства машин 1ВМ/360 [24]. 2.8.2. Архитектуры, основанные на портах В отличие от метода уровней абстракции, где внимание в первую очередь уделяется правильному распределению функций между уровня- ми иерархии в системе, здесь основное внимание обращено на методы связи между подсистемами. Механизмы, обеспечивающие взаимодейст- вие между частями системы, можно разбить на три группы. Механизмы первой группы, такие как предложение GOTO, прерывание, системный вызов, передают управление от одной части программы другой, не пере- давая явно никаких данных. 60
Рис. 2.8. Архитектура ОС Windows 2000/2003/ХР/7 Рис. 2.9. Клиент-серверная архитектура ОС 61
Системный вызов Рис. 2.10. Переключение режимов работы в макроядерных и микроядерных ОС Рис. 2.11. Организация ядра операционной системы SUE Механизмы типа вызова процедуры или подпрограммы относятся ко второй группе; они передают от одной части системы к другой и управ- ление} и данные. К третьей группе относятся механизмы портов — это методы передачи данных без передачи управления [17]. При использовании механизма портов программная система делится на несколько подсистем, каждая из которых может иметь один или не- сколько входных портов (одну или несколько входных очередей). 62
Порт — это просто текущий список входных сообщений (список пара- метров) для подсистемы. Каждая подсистема рассматривается как асин- хронный процесс, т.е. все подсистемы работают параллельно. Если одна из них хочет передать некоторые данные другой, она посылает их во входной порт этой другой подсистемы. Если подсистема готова обраба- тывать какие-то данные, она читает их из одного из своих входных пор- тов. Для организации такой связи используются операции ПОСЛАТЬ vi ПОЛУЧИТЬ. Механизм портов увеличивает независимость подсистем, освобож- дая их от временной зависимости друг от друга. Более того, подсистемы не должны даже знать о взаимном расположении. Только механизм по- сылки-получения (почта) должен знать расположение каждой подсисте- мы (например, в той же виртуальной памяти, в другой виртуальной па- мяти, в другом компьютере и т.д.). На рис. 2.12 изображена подсистема программной системы (процесс), имеющая несколько первичных вход- ных портов, по одному для каждой функции процесса. Процесс может выполнять команды типа ПОЛУЧИТЬ ПОРП (А, В, Q, на основании чего из входного порта ПОРП выбирается очередное сообщение и вы- деленные из него аргументы присваиваются параметрам А, В, С. Процесс может послать данные другим процессам в результате вы- полнения команды ПОСЛАТЬ УПРДАННЫМИ (X, У). по которой из ар- гументов X, У формируется сообщение, пересылаемое в порт по имени УПРДАННЫМИ. У процесса может быть и другой набор портов — вто- ричные порты. Они используются для связи с подчиненными процесса- ми. Например, подчиненный процесс может поставлять данные из базы данных. Рис. 2.12. Архитектура системы на основе портов прямой конфигурации Процесс, показанный на рис. 2.12, - часть прямой конфигурации. Ка- ждый такой процесс осуществляет связь посылкой сообщения прямо во входной порт другого процесса. На рис. 2.13 изображен процесс, являю- щийся частью непрямой конфигурации. Процессы этого типа имеют вы- ходные порты. Вместо того чтобы посылать сообщения во входные пор- 63
ты других процессов, они посылают сообщения в свои выходные порты. Выходной порт связывается с входными портами других процессов по- средством специальных указаний (например, в компьютерах третьего поколения, на языке управления заданиями). Физически выходные пор- ты не существуют, это абстрактные наименования для входных портов других процессов. На рис. 2.14 приведена схема информационно-поисковой системы, использующей прямую конфигурацию [17]. Управление терминалами — это непрерывно выполняемый процесс, который опрашивает терминалы пользователей и, получив команду с терминала, посылает сообщение подсистеме интерпретации команд. Сообщение состоит из двух аргу- ментов: идентификатора терминала и команды. Управление терминалом выполняет также функцию выдачи на терминал выходных строк. Полу- чив команду с терминала, подсистема интерпретации команд использу- ет две подчиненные системы, чтобы подготовить ключ для поиска и осуществить поиск в базе данных. Первичные входные порты Первичные выходные порты Вторичные входные и выходные порты Рис. 2.13. Архитектура системы на основе портов непрямой конфигурации Рис. 2.14. Информационно-поисковая система прямой конфигурации 64
2.8.3. Архитектуры, основанные на потоках данных По сути, эти архитектуры являются современным вариантом рас- смотренных архитектур, управляемых портами сообщений. В общем случае архитектуры, основанные на потоках данных, представляются как совокупность процессов, каждый из которых принимает данные из различных источников, а также возвращает данные в другие источники или хранилища данных. Процессы проектируются независимо друг от друга. Такие архитектуры давно используются и изображаются с помо- щью диаграмм потоков данных (DFD-диаграммы). Метамодель диа- граммы потоков данных приведена на рис. 2.15. Рис. 2.15. Метамодель диаграммы потоков данных Как разновидность таких архитектур рассматриваются архитектуры типа «каналы и фильтры», в которых процессы называются фильтрами, а потоки направляются через каналы. 2.8.4. Архитектуры независимых компонентов Программные системы такой архитектуры состоят из независимо ра- ботающих компонентов, время от времени общающихся друг с другом. Компонент, называемый клиентом, выдает запрос для выполнения како- го-либо действия другому компоненту, называемому сервером. Такое отношение называется клиент-серверным. Интерфейс сервера должен быть собран в одном месте и четко определен. Преобладание таких от- ношений характерно для клиент-серверной архитектуры. Классическая двухуровневая архитектура «клиент — сервер» постоянно усложнялась добавлением новых уровней. Отдельный уровень может управлять про- цедурами (сервер приложений), другой — данными (сервер баз данных в трехуровневой архитектуре). Значительную роль в разработке систем на основе архитектуры неза- висимых компонентов сыграл международный консорциум OMG (Object Management Group) [5]. Дело в том, что в мире существует гро- мадное количество готовых к использованию информационно-вычисли- тельных ресурсов. Они создавались в разное время, для их разработки использовались разные подходы. Почти всегда при разработке новой информационной системы можно найти подходящие по своим функци- ям уже работающие, готовые компоненты. Проблема состоит в том, что 65
при их создании не учитывались требования интероперабельности. Эти компоненты не понимают один другого, они не могут работать совмест- но. Желательно иметь механизм или набор механизмов, которые позво- лят сделать такие независимо разработанные информационно-вычисли- тельные ресурсы интероперабельными. Одно из наиболее признанных решений проблемы унаследованных систем основывается также на объектно ориентированном подходе. Ос- новной задачей OMG является разработка архитектуры и методов реа- лизации программного обеспечения, которое в объектно ориентирован- ном стиле позволило бы выполнить интеграцию существующих и зано- во разрабатываемых (не обязательно объектно ориентированных) ин- формационно-вычислительных ресурсов. OMG регулярно выпускает спецификационные документы, которые становятся фактическими про- мышленными стандартами. Основными составляющими подхода OMG являются базовая объектная модель (Core Object Model), эталонная мо- дель архитектуры ОМА (Object Management Architecture) и более при- ближенная к реализации общая архитектура брокера объектных заявок CORBA. На рис. 2.16 приведена эталонная модель метаобъектного средства OMG. На нижнем уровне находятся отдельные объекты. Объектные ти- пы определяют общие свойства схожих объектов. Объектные типы, в свою очередь, являются экземплярами мстаобъектной модели. С помо- щью этой мета модели описываются архитектуры OMG/CORBA, Microsoft/COM и Java/RMl [25]. Рис. 2.16. Эталонная модель метаобъектного средства OMG Идея CORBA заключается в следующем. Во-первых, в каждый объ- ект, который должен быть включен в интегрированную объектную сис- тему, добавляется специальный программный код, обеспечивающий принципиальную возможность взаимодействия объектов. Этот код гене- рируется автоматически за счет использования определенного OMG- языка определения объектных интерфейсов IDL (Interface Definition Language). В исходный текст программы включаются спецификации ин- терфейса на языке IDL. Затем этот текст должен быть обработан специ- альным прекомпилятором, который и генерирует дополнительный про- граммный код. Заметим, что на сегодняшний день в документах OMG определены правила встраивания конструкций IDL в далеко не все язы- ки программирования, но, по крайней мере, определены правила встраи- вания для таких популярных языков, как С и C++. Во-вторых, для реального взаимодействия должным образом настро- енных объектов предполагается наличие специального программного обеспечения, называемого в документах OMG брокером объектных зая- 66
вок ORB (Object Request Broker). Брокер объектных заявок должен су- ществовать и на стороне вызывающего объекта, и на стороне вызывае- мого объекта. Основная задача ORB состоит в том, чтобы доставить за- явку на вызов метода вызываемого объекта и возвратить результаты вы- полнения метода вызываемому объекту. В настоящее время CORBA является индустриальным стандартом, описывающим высокоуровневые средства поддержки взаимодействия объектов в распределенных гетерогенных средах. Этот стандарт специ- фицирует инфраструктуру взаимодействия компонентов на представи- тельском уровне и уровне приложений модели OS1, позволяя рассматри- вать все приложения в распределенной системе как объекты. Практика показывает, что большинство объектов одновременно исполняет роль и клиентов, и серверов, позволяя тем самым с помощью CORBA стро- ить гораздо более гибкие системы, чем системы «клиент — сервер», ос- нованные на двухуровневой и трехуровневой архитектуре. 2.8.5. Сервис-ориентированные архитектуры (SOА) В последнее время быстро набирает вес и де-факто становится стан- дартом для создания распределенных приложений технология веб-сер- висов [21]. В основе SOA лежат принципы многократного использова- ния функциональных элементов технологий, ликвидации дублирования функциональности в программной системе, унификации типовых про- цессов, обеспечения перевода операционной модели компании на цен- трализованные процессы и функциональную организацию на основе промышленной платформы интеграции. Компоненты программы могут быть распределены по разным узлам сети и предлагаются как независимые, слабо связанные, заменяемые сервисы-приложения. Программные комплексы, разработанные в соот- ветствии с SOA, часто реализуются как набор сервисов, интегрирован- ных при помощи известных стандартных протоколов (SOAP, WSDL и т.п.). В принципе, архитектуру этого типа можно отнести к архитектуре независимых компонент. Основное достоинство такого рода сервисов — это простота создания. Упрощается и взаимодействие между компонен- тами, поскольку основным транспортным протоколом является http. Приложение может без проблем работать не только в Intranet-, но и в Internet-сетях. Кроме того, эта технология не привязана к какой-либо од- ной платформе, что открывает возможность создания распределенных приложений в гетерогенных средах. Технология интегрирована с техно- логией DOT.NET. Ключевой аспект, который отличает веб-сервисы от протоколов пе- редачи двоичных данных, — в моделях программирования - таких, как CORBA и DCOM. Веб-сервисы - это технология работы с сообщения- ми, в которой передача сообщений основана на XML (SOAP), и сами всб-сервисы описываются на XML (WSDL). Логическим продолжением 67
технологии веб-сервисов стала архитектура, ориентированная на серви- сы (SOA). Ее появление является следствием новых задач и потребностей, воз- никающих при создании и эксплуатации современных информационных систем: • оперативное реагирование на изменение требований и быстрая адаптация к новым задачам; • оптимизация управления бизнес-процессами; • эффективное обеспечение внешних взаимодействий. К основным характеристикам SOA, отличающим ее от других архи- тектур информационных систем, следует отнести: • распределенность; • слабосвязанные интерфейсы (отсутствие жестких связей между элементами системы), что упрощает конфигурирование системы и коор- динирование работы ее элементов; • базирование архитектуры на международных открытых стандар- тах; • возможность динамического поиска и подключения нужных функ- циональных модулей; • построение систем с расчетом на процессы с использованием сер- висов, ориентированных на решение определенных бизнес-задач. Метамодель SOA (рис. 2.17) включает следующие модели: • модель политик (РМ) определяет политики, связанные с архитек- турой; в основном она представляет собой ограничения, накладываемые на поведение агентов и сервисов, в том числе ограничения, связанные с требованиями безопасности и качества обслуживания; • модель, ориентированная на сервисы (SOM), сосредоточена на действиях, выполняемых сервисами; • модель, ориентированная на ресурсы (ROM), сосредоточена на системных ресурсах; • модель, ориентированная на сообщения (МОМ), сосредоточена на сообщениях, их структуре, способах транспортировки и других компо- нентах, никак не связанных с причинами обмена сообщениями и их се- мантикой. Важную роль в этой архитектуре отводится управлению веб-сер виса- ми, к основным функциям которого относятся развертывание, конфигу- рирование, обеспечение безопасности, а также функции мониторинга и диагностики. Уже реализуется скоординированное движение к обеспе- чению управления веб-сервисами как ресурсом. Стандарт, к которому стремится большинство поставщиков, — это веб Services for Distributed Management (WSDM). В стандарт входит набор веб-сервисов и опера- ций, которые должна будет обеспечивать каждая реализация веб-серви- са (операции для конфигурирования, мониторинга и других задач управления). При необходимости внести изменения в интерфейс сервиса и про- вайдер, и пользователи веб-сервиса должны изменить свои среды одно- 68
временно, что может оказаться затруднительным. Однако имеется воз- можность (и она реализована) ввести посредника между потребителем сервиса и его провайдером, что существенно повышает адаптируемость и масштабируемость систем. В сервисной архитектуре просматриваются черты традиционных ар- хитектур. С помощью сценарных языков типа В PEL можно описывать новые сервисы или описывать бизнес-процессы с участием сервисов (виртуальная машина). Системы, реализованные в других стандартах (например, CORBA), легко интегрируются в SOA (соответствующий инструментарий имеется). SO А представляет собой не только основу для новых информацион- ных технологий, но также и новую системную философию. Постепен- ный переход от мэйнфреймов к конфигурациям типа «клиент — сервер» и далее к SOA означает глубокие изменения в функциональности сис- тем и, следовательно, создает новые возможности в областях примене- ния информационных технологий. SOA имеет все шансы заменить со временем монолитные корпоративные системы, сложные как во внедре- нии, так и в модернизации. 2.9. Архитектурные представления программных систем При проектировании любая программная система может рассматри- ваться с разных точек зрения: поведенческой (динамической), структур- ной (статической), логической (удовлетворение функциональным требо- ваниям), физической (распределенность), реализации (как детали архи- тектуры представляются в коде) и т.п. Эти различные архитектурные представления, или виды (view), в совокупности дают полную информа- цию об архитектуре ПС. При этом каждое отдельное архитектурное представление может быть определено как частный аспект программной архитектуры, представляющий специфические свойства программной системы, наиболее интересные для конкретного заинтересованного ли- ца. Таким образом, можно говорить о том, что каждое архитектурное представление ПС А[ описывает некоторую сторону частного решения 69
по архитектуре ПС. В совокупности можно считать, что полное реше- ние по определению архитектуры As ПС представляется объединением вида As =иА^, i = l, 2, ..., и, (2.1) где п — число различных представлений архитектуры ПС. Как отмечалось выше, в разделе 2.1, стандарт IEEE 1471 определяет представление архитектуры как согласованный набор документов, опи- сывающий архитектуру с точки зрения определенной группы заинтере- сованных лиц с помощью набора моделей. Стандарт рекомендует для каждого представления фиксировать отраженные в нем взгляды и инте- ресы, роли лиц, которые заинтересованы в таком взгляде на систему, причины, обусловливающие необходимость такого рассмотрения систе- мы, несоответствия между элементами одного представления или меж- ду различными представлениями, а также различную служебную ин- формацию об источниках информации, датах создания документов и пр. Заметим, что каждое архитектурное представление ПС А, может иметь множество возможных и допустимых вариантов, и задача архи- тектора заключается в нахождении такого варианта И, архитектурного представления Лг, которое является оптимальным по некоторому част- ному показателю при условии непротиворечивости решений Vs по дру- гим архитектурным представлениям Aj, j i,j =1,2, и. Это усло- вие можно записать следующим соотношением: Г,пГ. = 0, i,j = l, 2, п. (2.2) Далее рассматриваются некоторые виды архитектурных представле- ний ПС, предлагаются методы формализации возможных вариантов ре- шений и определения оптимальных решений. 2.9.1. Архитектурный вид — структура многослойной программной системы Многослойные программы могут рассматриваться как результат создания машины пользователя, или виртуальной машины (и), начиная с самого низшего уровня (0) аппаратуры (или, возможно, операционной системы). Последовательность уровней, называемых абстрактными ма- шинами, определяется так, что каждая следующая машина строится на основе предыдущих, расширяя их возможности. Каждый уровень может ссылаться только на один, отличный от него самого уровень, а именно тот, который непосредственно ему предшествует. Остановимся на особенностях основных вариантов многослойных структур. Если в варианте по рис. 2.4 каждый слой имеет доступ к ко- мандам только одного слоя, разработчик должен иметь в виду только предыдущий слой. Хотя с точки зрения проектирования этот вариант кажется привлекательным, он может оказаться очень неэффективным. Например, если некоторое средство, предоставляемое слоем (2), потре- буется в слое (z), то каждый из слоев (3), (4),..., (z — 1) должен обеспе- 70
чить это средство. Это значит, что запрос данного средства слоем (/) должен «просачиваться» вниз через слой (г- 1), пока не достигнет слоя (2), который способен выполнить запрос. Такой подход связан с допол- нительными затратами времени на трансляцию запросов. Эти трудно- сти, связанные с проблемой эффективности, могут склонить к принятию структуры по рис. 2.5, в которой каждый слой (г), где 2 < i < и, может непосредственно обращаться к слою (2). Представим структуру многослойной программной системы в обоб- щенном виде, показанном на рис. 2.18. В данном случае каждый слой показан в виде одного модуля с возможностью организации связей с любым произвольным слоем системы. Такая обобщенная схема позво- ляет рассмотреть любую структуру н-слойной программной системы, лежащую в диапазоне структур, приведенных на рис. 2.4 и рис. 2.5. Произвольная структура описывается некоторым множеством буле- вых переменных: X = {ху. е {0,1} \i = 77, п-1,,,,,2; j = п -1, п -2,..., 1; z>j], (2.3) где ху = 1, если существует связь между слоями i и j, и ху = 0, если та- кой связи нет. Так как между смежными слоями всегда имеется связь, то (Vi I г = у +1)(^. = 1), i = n,n-\,...,2. (2.4) Если в многослойной структуре программы, представленной выра- жением (2.3), принимают единичное значение только переменные, опи- сываемые условием (2.4), то эта программа имеет структуру, соответст- вующую варианту по рис. 2.5. Если справедливо условие (3/1 (i = п, п-1,..., 2))& (Зу I (i - j > 2))Ц =1), (2.5) то программа имеет структуру, соответствующую промежуточному ва- рианту между вариантами структур, представленными на рис. 2.5 и рис. 2.6. Если справедливо условие (Vi i = п, п -1,..., 2)(3/1 (i - j > 2))(х# = 1), (2.6) то программа имеет структуру, соответствующую варианту, представ- ленному на рис. 2.6. 2.9.2. Архитектурный вид —размещение программной системы Этот архитектурный вид отражает распределение компонентов ПС между элементами аппаратной обработки (компьютеры, аппаратура пе- редачи данных и каналы связи). Известно, что в корпоративных систе- мах широко распространен архитектурный стиль «клиент- сервер». Рассмотрим один из возможных вариантов определения оптимального размещения компонентов ПС в двухуровневой системе «клиент - сер- вер». В решении подобной задачи заинтересован системный архитектор. 71
Постановка задачи. На предприятии внедряется программная систе- ма (ПС), разработанная в архитектурном стиле «клиент — сервер». ПС содержит следующие компоненты (функциональные части — ФП): Аппаратура Рис. 2.18. Обобщенная структура многослойной системы Рассматриваются три возможные архитектуры распределения функ- циональных частей приложения на основе двухуровневой схемы (рис. 2.19). На всех приведенных ниже вариантах символами КК обозначены клиентские компьютеры, а символами СК — серверные компьютеры; F1 — интерфейс пользователя (представление данных и логика сценария работы); F2 — логика приложения и обращения к базе данных (БД); F3 — операции базы данных; F4 - файловые операции. Каждый вариант ха- рактеризуется своим способом размещения функциональных частей ПС. А. Схема с «тонким клиентом». В этом случае клиентский компью- тер обладает незначительной вычислительной мощностью и имеет не- большой объем оперативной памяти, а внешней вообще может и не быть. В общем случае под термином «тонкий клиент» подразумевается достаточно широкий с точки зрения системной архитектуры набор уст- ройств и программ, которые объединяются общим свойством - возмож- ностью работы в терминальном режиме. Таким образом, для работы тонкого клиента необходим терминальный сервер. 72
Аппаратура и каналы связи Рис. 2.19. Схема с «тонким клиентом» F2 F3 F4 СК В. Схема с «толстым клиентом» (рис. 2.20). «Толстый», или rich, клиент в архитектуре «клиент — сервер» - это приложение, обеспечи- вающее (в противовес клиенту) расширенную функциональность неза- висимо от центрального сервера. Часто сервер в этом случае является лишь данных (файловым сервером), а вся работа по обработке и пред- ставлению этих данных переносится на машину клиента. Рис. 2.20. Схема с «толстым клиентом» С. Схема с обычным клиентом (рис. 2.21). Для расширения функцио- нальности тонкого клиента прибегают к его «утолщению», например добавляют возможности автономной работы, сохраняя главное отли- чие - работу в сессии с терминальным сервером. Когда в клиенте появ- ляются подвижные детали (жесткие диски), появляются возможности автономной работы, он перестает быть «тонким клиентом» в чистом ви- де, а становится универсальным клиентом. Во всех схемах используются предварительно выбранные компьюте- ры КК и СК (одни и те же). Известна загрузка этих компьютеров и аппа- ратуры и каналов связи для каждого варианта системы. КК Аппаратура и каналы связи Рис. 2.21. Схема с обычным клиентом СК
Для расчетного примера, который рассматривается ниже, установле- но, что предположительно оператор КК обращается к приложению с ин- тенсивностью 10 обращений в час (конкретное числовое значение, как и используемые далее). Такая работа загружает клиентский компьютер (за счет F1) на 6%, а аппаратуру и канал связи при работе по схеме А — на 1%. Реализация компонента F2 на клиентском компьютере - на 5%, а в случае реализации F2 на СК последний загружается на 2,5%. При выполнении компонента F3 (схема В) на клиентском компьюте- ре с сервера поступают полные копии файлов, над которыми произво- дятся операции базы данных, поэтому загрузка аппаратуры связи и ка- налов связи возрастает до 2%. При назначении компонента F3 (опера- ции БД) на КК он загружается на 15% (хотя это «толстый клиент», но все-таки не сервер!). Выполнение F3 на сервере загружает его на 5%. Реализация файловых операций F4 возможна только на сервере, и один набор таких операций загружает СК на 1%. Все приведенные значения относятся к случаю работы одного КК. При увеличении числа работающих клиентских компьютеров загрузка элементов системы возрастает примерно пропорционально. Очевидно, что есть некоторое предельное число КК, при котором система работает нормально, без перегрузки. Требуется выбрать оптимальное распределение компонентов про- граммной системы (а следовательно, и схему системы) по компьютерам, при котором обеспечивается нормальная работа без перегрузки систе- мы. Формализация задачи. Для формализации задачи можно использо- вать понятие двудольного графа. Граф G = (У, Е) называется двудоль- ным графом (биграфом), если множество У его вершин допускает раз- биение на два непересекающихся подмножества - И и F2 (две доли). Причем каждое ребро графа соединяет вершины из различных долей. Двудольный граф с долями У1 и F2 обозначается через G = (И, У2, Е). В нашей задаче доля И представляется как И = {Fl, F2, F3, F4}, а доля F2 соответственно F2 = {КК, СК} (рис. 2.22). Ребрами в таком графе будем обозначать возможную реализацию некоторого компонента ПС на одном из возможных компьютеров системы. Пунктирное изобра- жение ребра показывает на возможный вариант реализации, сплошное изображение ребра означает выбранный (по исходным условиям) или определенный при решении задачи вариант. 74
Рис. 2.22. Размещение, представленное графом Поставим каждому ребру в соответствие некоторую переменную х,. В нашем случае удобно считать, что такая переменная принимает два значения (0 или 1), т.е. х; е {0, 1}. При этом значение переменной, рав- ное 1, говорит о том, что выбирается соответствующее решение (ребро сплошное). Значение переменной, равное нулю, свидетельствует о том, что в решении соответствующее ребро отсутствует. Таким образом, решение задачи можно представить двоичным век- тором: Х= (хь х2, х3, х4, х5, х6). Определим для нашего случая исходные значения переменных и ог- раничения на их значения, накладываемые спецификой задачи. 1. Значение Х| = 1, поскольку F1 всегда реализуется на КК. 2. Значение х2 = 1, если F2 выполняется на КК, и х2 = 0 — в против- ном случае. 3. Значение х3 = 1, если F2 выполняется на СК, их3 = О, если F2 не выполняется на СК. 4. Значение х4 = 1, если F3 выполняется на КК, их4 = 0, если F3 не выполняется на КК. 5. Значение х5 = 1, если F3 выполняется на СК, и Х5 = 0 — в против- ном случае. 6. Значение х6 = 1, поскольку файловые операции (F4) могут быть реализованы только на СК. К перечисленным переменным необходимо добавить еще одну, с по- мощью которой учитывается в системе количество возможных клиент- ских компьютеров — х7 . Это целочисленная переменная, от которой за- висит загрузка аппаратных элементов системы, в том числе аппаратуры и каналов связи. Следует обратить внимание на тот факт, что в нашей задаче четыре булевых переменных (х2, х3, х4, х5) определяют 16 возможных топологий двудольного графа, представленного на рис. 2.23. Однако возможных вариантов ПС, как следует из рис. 2.20-2.22, только три. Анализ струк- туры графа позволяет легко дописать дополнительные ограничения сле- дующего вида: х2 + х3 = 1 и х4 + Х5 = 1. 75
Данные соотношения следуют из того факта, что компоненты F2 и F3 могут быть реализованы только или на КК или СК. Кроме того, внимательное рассмотрение возможных вариантов структур системы позволяет сделать вывод, что не может быть допустимым размещение F3 на КК (х4 = 1) при размещении F2 на СК (хз = 1). Таким образом, должно выполняться ограничение х3 х х4 = 0. Также отметим еще огра- ничение: если F2 размещается на СК, то и F3 должен размещаться на СК, т.е. должно выполняться соотношение х3 X х5 = 1. Теперь рассмотрим вопрос о выборе целевой функции (ЦФ). Что мо- жет служить в нашей задаче целевой функцией? Может быть несколько вариантов. В частности, максимальная загрузка СК, поскольку это опре- деляет максимальное число клиентских компьютеров, которые могут быть обслужены системой при допустимой загрузке аппаратуры и кана- лов связи. С другой стороны, возможно ограничение числа КК со сто- роны аппаратуры и каналов связи, и тогда следует выбрать в качестве ЦФ максимум загрузки аппаратуры и каналов связи при допустимой за- грузке СК. Если в результате решения задачи в первой постановке ока- жется, что загрузка аппаратуры связи окажется значительно ниже до- пустимой, то это свидетельствует о том, что число КК в системе ограни- чивается СК. Если в результате решения задачи во второй постановке окажется, что СК загружен ниже допустимого значения, то это говорит о том, что число КК ограничивается аппаратурой связи. Введем обозначения для загрузки элементов аппаратной системы, ко- торая обусловлена работой оператора одного клиентского компьютера: — загрузка КК компонентом F1; z% —загрузка КК компонентом F2; z2 — загрузка КК компонентом F3; z| — загрузка СК компонентом F2; zj — загрузка СК компонентом F3; z4 — загрузка СК компонентом F4; z“ — загрузка аппаратуры и КС при работе по схеме А; zf - загрузка аппаратуры и КС при работе по схеме В; z™ - загрузка аппаратуры и КС при работе по схеме С. Построим функции загрузки элементов аппаратной системы: 1. Загрузка сервера: Zc = (zf -x.+zj-x, +zf -x,)x7. (2.7) 2. Загрузка KK: Zk — z* * Xj 4- z\ ' x2 4- z2 • x4. (2.8) 76
3. Загрузка аппаратуры и каналов связи: Zce = (z™-x3+z?-x4+z™-x2)x7. (2.9) Оптимальное распределение компонентов ПС по аппаратуре систе- мы может иметь своей целью максимизацию числа клиентских компь- ютеров в системе при условии, что загрузка всех аппаратных элементов системы не превышает допустимую (т.е. < 1). В этом случае целевая функция оптимизации распределения компонентов ПС по аппаратуре системы может иметь следующий вид: (2.10) Таким образом, в условиях сформулированной задачи выбор опти- мального распределения компонентов программной системы по аппа- ратным средствам системы сводится к отысканию такого решения xopf = (xi> х4’ хб) ’ ПРИ котором достигается максимальное зна- чение функции (1), т.е. х7 =l/(z^x3+z3C‘X5 4-z4 х6) (2.11) при условии выполнения следующих ограничений: А *Х[ 4-z2 * х2 4- z3 х4 < 1 (2.12) (загрузка КК); х2)х7 <! (2-13) (загрузка аппаратуры и КС); V /х, е {0, 1}, i = 1,2, ..., 6; х2 + л'з = 1; х4 4- х5 = 1; хз х *4 = 0; х3 х х5 = 1; х7> 0. (2.14) (2-15) (2-16) (2-17) (2-18) (2.19) Заметим, что в такой постановке задачи от максимизации целевой функции (2.11) можно перейти к минимизации выражения, стоящего в знаменателе, т.е. к отысканию min (z2 -х3 +zj - х5 4-z4 * х6) при тех же ограничениях (2.12)-(2.19). Физический смысл решения задачи в такой постановке означает поиск такого решения, при котором загрузка серве- ра от одного клиентского компьютера минимальна. Решение задачи. Сформулированная задача относится к классу за- дач линейного программирования с булевыми переменными. В данном случае в связи с малой размерностью задачи ее можно легко решить полным перебором допустимых наборов переменных (хь х%, хз, х4, Х5, х6), представляющих допустимые решения в условиях принятых ограниче- ний. 77
Заметим, что во всех возможных решениях Xi = х6 = 1, что дополни- тельно упрощает поиск решения. Однако кроме простого решения зада- чи представляет интерес анализ возможных вариантов схем, приведен- ных на рис. 2.19-2.21 с целью получения ответа вида «что будет, ес- ли?». Другими словами, хорошо было бы иметь модель, которая помог- ла бы детально исследовать возможные варианты архитектур ПС. Та- кую модель легко построить, используя электронные таблицы, напри- мер Excel, современной редакции. Для этого следует воспользоваться возможностями надстройки «Поиск решения». На рис. 2.23 показано возможное представление задачи со значения- ми исходных данных, приведенными в начале статьи. Здесь всем пере- менным произвольно назначено единичное значение. На результат ре- шения задачи это никак не влияет. Решение задачи производится, как это обычно, для электронных таблиц. Результат решения представлен на рис. 2.24. Полученный вектор ре- шения имеет следующий вид: Хор( = (1, 0, 1,0, 1, 1). Это соответствует архитектуре распределения компонентов ПС в соответствии с графом, представленном на рис. 2.25. Такой граф соответствует структуре систе- мы, в которой на сервере размещаются компоненты F2, F3 и F4. Анализ полученных результатов для назначенных исходных данных позволяет сделать вывод, что аппаратура и каналы связи имеют запас по производительности (загрузка 0,706 < 1), а сервер загружен полностью. Точнее, его загрузка немного меньше единичного значения, если округ- лить число клиентских компьютеров до ближайшего меньшего значе- ния -11. —о 8 С D Е F G Н J К L M N О Размещение - архитектурный вид. отражающий распределение компонентов приложен 1 между элементами аппаратной обработки и передачи данных 2 3 Переменм ые Знаме- ние Загрузки аппаратных элементов системы компонентами ПС целевая функция 11,76471 4 XI 1 Загрузка КК компонентом F1 0.06 Величина ограничения 5 Х2 1 Загрузка кк компонентом F2 0.05 Ограничение по 0.26 1 хз 1 Загрузка кк компонентом F3 0.15 загрузке кк V 7 Х4 1 Загрузка СК компонентом F2 0.025 Ограничение по 0,705882 1 8 Х5 1 Загру з*а СК компонентом F3 0.05 загрузке алпаэ. и КС 9 Х6 1 Загрузка СК компонентом F4 0.01 10 Загрузка аппар. и КС по а. А 0.01 Ограничения на 2 1 11 Загрузка аппар. и КС по сх. В 0,02 I - рчг ляегwи > и 2 1 12 Загрузка аппар. и КС по сх. С 0.03 1 1 13 1 1 14 ИГ I ' : • 1 1 1 ' Рис. 2.23. Представление задачи в Excel 78
A В С _[ D _[ E f e H 1 J I К I M | N О P Размещение- архитектурный вид, отражающий распределение компонентов приложения 1 между элементами аппаратной обработки и передачи данных 2 Пе ременн 3 паче- 3 агру зки ап па ратны х э л еме нтое сисге м ы целевая функция 3 ые ние компонентами ПС 11,76471 4 XI 1 Загрузка КК компонентом F1 0,06 Величина ограничения 5 Х2 0 Загрузка КК компонентом F2 0,05 Ограничение по 0,06 1 6 ХЗ 1 Загрузка КК компонентом F3 0,15 загрузке КК 7 Х4 0 Загрузка СК компонентом F2 0г025 Ограничение по 0Д17647 1 8 Х5 1 Загрузка СК компонентом F3 0h05 загрузке ап пар. и КС 3 Хб 1 Загрузка СК компонентом F4 0,01 10 Загрузка ап пар. и КС по сх. А 0,01 Ограничения на 1 1 11 Загрузка ап пар. и КС по сх. В 0,02 переменные 1 1 12 Загрузка ап пар. и КСпосх. С 0,03 0 1 13 14 15 Реивние найдено, Dee огрежчежя и условия 161 опппальнкти вьгплнны- Тип отчета Риуътвты 17 ------ - “ : Угтиимивогть f tлГГОПЛНИТЬ МАИ Л ДИЫПй ПЙП1ЙН# г k 1 w nr,L’ - 1 u 13 *.. ..« 4»WApOnn 1 D “ЗР!Д“Пи& рРШОППь | 19 О Воссгзноватъ моюдше значения 20 СК Ошена Сохранить сценарий .. Справка 21 1 J ।“ Рис. 2.24. Решение задачи Рис. 2.25. Результирующий граф системы Предложенный подход решения задачи определения оптимальной архитектуры распределения компонентов программных систем по аппа- ратуре компьютерной системы может быть с успехом использован для трехуровневых систем типа «клиент — сервер», а также более сложных топологий распределенных систем, содержащих несколько серверов приложений и баз данных. Возможно построение подобных табличных моделей для анализа и оптимизации многослойных программных сис- тем и систем с параллелизмом. 79
2.9.3. Архитектурный вид — размещение программной системы, основанной на потоках данных Эти архитектуры являются современным вариантом архитектур, управляемых портами сообщений. При использовании механизма пор- тов программная система делится на несколько подсистем, каждая из которых может иметь один или несколько входных портов (очередей). Порт — это текущий список входных сообщений для подсистемы. Каж- дая подсистема рассматривается как асинхронный процесс, т.е. все под- системы работают параллельно. Если одна из них хочет передать неко- торые данные другой, она посылает их во входной порт этой другой подсистемы. Если подсистема готова обрабатывать какие-то данные, она читает их из своих входных портов. Для организации такой связи используются операции ПОСЛАТЬ и ПОЛУЧИТЬ. В общем случае архитектуры, основанные на потоках данных, пред- ставляются как совокупность процессов, каждый из которых принимает данные из различных источников, а также возвращает данные в другие источники или хранилища данных. Процессы проектируются независи- мо друг от друга. Постановка задачи. Пусть имеется некоторая программная система, состоящая из нескольких асинхронно выполняющихся программных процессов: Р — {рг| i = 1, 2,..., п}. В компьютерной системе имеется т вычислителей, на которых предполагается выполнение процессов Р. Обозначим эти вычислители множеством К = {v; | j = 1, 2,..., т}. Извест- на загрузка вычислителей каждым процессом, заданная матрицей i = 1, 2,п; у = 1, 2,..., т. Требуется выбрать оптимальное распределение компонентов программной системы по вычислителям, при котором обеспечивается нормальная работа без перегрузки системы. Формализация задачи. Размещение компонентов ПС по вычислите- лям удобно представить двудольным графом вида G = (Р, V, Е), где Е — множество дуг, отображающих размещение процессов Р по вычислите- лям. Если ограничений на размещений компонентов Р по вычислителям нет, то в таком графе каждая вершина pi е Р соединена дугами со все- ми вершинами К Поставим в соответствие дугам £ графа G множество переменных: X = {х;/ | i = 1, 2,..., л; j = 1, 2,..., т}. Каждая переменная Ху образуется по правилу Ху = 1, если будет принято решение выполнять процесс р, на вычислителе v,-. В противном случае Ху = 0. Требуется так распределить множество процессов Рпо вычислите- лям К, чтобы суммарная загрузка вычислителей была минимальной при условии того, что допустимая загрузка каждого вычислителя была < 1 и все процессы Р выполняются. Применительно к графовой интерпрета- ции задача сводится к отысканию частичного графа G = (£, И, £Д где Ео <^Е . При этом множество найденных дуг Ео определит значение бу- левых переменных: Хо {Xj I i 1, 2,..., л, j — 1, 2,,.., тл}. 80
Целевая функция в этом случае имеет следующий вид: c=X".i£“=iViy 111111 <2-20) Определим ограничения задачи. Первая группа ограничений связана с тем фактом, что каждый процесс р, в результате решения задачи дол- жен быть назначен только на один вычислитель. Это условие записыва- ется следующим соотношением: i = l,2,...,n. (2.21) Поскольку все переменные задачи — двоичные, вторая группа огра- ничений имеет следующий вид: Vxye{0,1}, / = 1,2,...,л; у = 1,2,..., т. (2.22) Третья группа ограничений связана с допустимой загрузкой каждого вычислителя. Как отмечено выше, она не должна превышать единично- го значения: Vfcj = Е ^), 7 = 1,2,...,™. (2.23) Решение задачи. Сформулированная задача (2.15)—(2.18) относится к классу задач линейного программирования с булевыми переменными. При малой размерности задачу можно легко решить полным перебором наборов переменных: — {ху 11. — 1, 2,..., л, j' — 1,2,..., /и}, (2.24) представляющих допустимые решения в условиях принятых ограниче- ний. Однако уже в пяти процессах и пяти вычислителях количество воз- можных вариантов равно 55, что приведет к более чем 3000 переборов. Однако возможности современных электронных таблиц позволяют ре- шать подобные задачи значительно большей размерности. 2.9.4. Архитектурный вид -распределениеработ по группам разработчиков Постановка задачи. Пусть в организации-разработчике имеются сле- дующие группы специалистов: • аналитики; • архитекторы; • кодировщики; • тестировщики; • интеграторы. В организации выполняется несколько проектов по созданию про- граммных продуктов. На начало текущего года сложился следующий перечень работ, требующих выполнения: • по проекту № 1 — анализ и разработка требований; • по проекту № 2 - разработка спецификаций; • по проекту № 3 — разработка архитектуры; 81
• по проекту № 4 - разработка программного кода; • по проекту № 5 - тестирование; • по проекту № 6 - анализ и разработка требований; • по проекту № 7 - разработка архитектуры. Таким образом, имеется 5 групп специалистов и 7 проектов, над ко- торыми требуется работать. Поручить по одной работе каждой группе невозможно. Какие-то группы должны работать над более чем одним проектом. Руководитель из опыта предыдущих работ примерно может оценить трудозатраты (например, в человеко-часах) каждой группы на выполнение предстоящих работ. Например, он построил таблицу воз- можных трудозатрат по предстоящим работам с учетом квалификации и опыта исполнителей (табл. 2.1). Требуется выбрать оптимальное распределение специалистов по предстоящим работам. Очевидно, оптимальным будет такое распределе- ние, при котором все работы будут выполнены при минимальных затра- тах рабочего времени. При этом возможно такое решение, что одному исполнителю будет назначено более одной работы. Таблица 2.1. Трудозатраты на предстоящие работы ПРОЕКТ Анали- тики Архитек- торы Кодиров- щики Тестиров- щики Интегра- торы № 1 - анализ и раз- работка требований 30 40 80 №2 - разработка спецификаций 35 30 65 № 3 - разработка архитектуры 50 20 №4 - разработка программного кода 50 30 75 78 №5 - тестирование 70 35 45 № 6 - анализ и раз- работка требований 25 45 №7- разработка архитектуры 40 25 45 Формализация задачи. Для формализации задачи можно использо- вать понятие двудольного графа. В таком графе множество V его вер- шин допускает разбиение на два непересекающихся подмножества И и V2 (две доли). Двудольный граф с долями II и V2 обозначается через G = (И, И2, £). В нашем случае И — это множество работ, а Т2 - множе- ство исполнителей работ. Ребрами в таком графе будем обозначать воз- можное назначение z-й работы (z = 1, 2, ...,7) для выполнения у-й груп- пой специалистов (J = 1, 2, ...,5). Максимальное число ребер в таком графе можно определить из выражения | Е |=| 1 * | V2 |. 82
Однако, анализируя табл. 2.1, можно увидеть, что реально количест- во ребер меньше, так как есть исполнители, которые не могут выпол- нять все работы. Поставим в соответствие дугам Е графа G множество переменных: X = {ху | i = 1,2,..., и;у = 1,2,..., т}. Здесь п — число работ, a/и-количе- ство исполнителей. Каждая переменная Ху образуется по правилу Ху = 1, если будет принято решение выполнять проект i группой j. В противном случае Ху = 0. Требуется так распределить множество проектов 11 по исполнителям И2, чтобы суммарная загрузка исполнителей была мини- мальной при условии того, что все проекты выполняются. Примени- тельно к графовой интерпретации задача сводится к отысканию частич- ного графа G = (Fl, И2, Е,,), где Ео <^Е. При этом множество найденных дуг Ео определит значение булевых переменных: Хо = |ху | i = 1, 2,..., и; j = 1,2,..., т}. Обозначим трудоемкость выполнения 7-й работы /-м ис- полнителем через zg. В этом случае пустая клетка в табл. 1 должна иметь достаточно большое значение, чтобы соответствующее значение не могло войти в решение. Целевая функция в этом случае имеет сле- дующий вид: с = X,"., S'2 ZU xij “* min- (2-25) Определим ограничения задачи. Первая группа ограничений связана с тем фактом, что каждый проект в результате решения задачи должен выполняться только одним исполнителем. Это условие записывается следующими соотношениями: = •- < = 1,2,...,п. (2.26) Поскольку все переменные задачи — двоичные, вторая группа огра- ничений имеет следующий вид: Х/Ху-е{0,1}, 1 = 1, 2, ...,л; / = 1, 2,...,т. (2.27) Заметим, что решение задачи должно допускать исполнение не- скольких работ одной группой при условии, что ее загрузка не превы- шает ресурс рабочего времени. Поэтому третья группа ограничений мо- жет быть записана следующим образом: VjE"=)Vx# <Zp J = l,2,...,m. (2.28) где Zj — допустимый ресурс рабочего времени у-й группы. Решение задачи. Сформулированная задача (2.25)—(2.28) относится к классу задач линейного программирования с булевыми переменными. При малой размерности задачу можно легко решить полным перебором наборов переменных: X = {х?7 | i = 1, 2,..., л; j = 1, 2,..., т}, (2.29) 83
представляющих допустимые решения в условиях принятых ограниче- ний. Однако возможности современных электронных таблиц позволяют решать подобные задачи значительно большей размерности. Приведем пример решения. Допустим, задана таблица трудозатрат по предстоящим работам с учетом квалификации и опыта исполнителей (табл. 2.1). Заданы также ограничения на ресурс рабочего времени ис- полнителей, например, в количестве 150 человеко-часов по каждой группе исполнителей. Сформированные данные для решения задачи в электронной таблице Excel представлены на рис. 2.26. С I D i t F й w . f . f к L L M ; JL Исходны» данные : г [[ВНГрИ^У Переменные Orph 1 Дл1к ЙНДЛ 1НЛ * 1 5 1 Я 1 -Wir-W iprfannaa. i эд w ЕС Ю ICOO 1 i i 1 1 Lilli ™ । । д 2ПЕ- пд'Угшепй л 35 30 Й5 ю IIHI З’ргрЛта J IfZnHTVfkJ *0 эд 11111 S 1 И4 -pirpiftnn t № 1000 50 Ж 75 11111 5 1 —MI-iacnptHBi 7 ICOO 1000 Й 35 11111 11111 1 1 5 1 £-шве 45 1Ж IWQ 1000 ] LA ipwttmTw 4Q 45 10№ LOW 1 i 1 1 1 5 1 ID :: Целемя функция Огр, 3 виде Дол. put. 12 1591?| 2iaa 1W 1210 1S& 14 12« 150 ts sm 1W М И» 1W Рис. 2.26. Пример представления исходных данных Заполненные данные для поиска решения в форме электронной таб- лице Excel приведены на рис. 2.27, Рис. 2.27. Представление данных для поиска решения 84
Найденное решение показано на рис. 2.28 и 2.29. А Е с 0 Е F 1 Исходные данные ! .AjUTI'THKH Архитекторы КД1Пф4}И1£ККЕ TeerupDEi^jtK. и Интеграторы 3 \ 1 _ 1VJTTWT и ралрл&гаа Tpwnuicuii 30 40 80 1000 1000 4 щефшлгрш 35 30 65 1000 1000 5 Xi 3 - pi^paaaro э.рхктаЕггур'ы 50 20 1МЮ 1000 1000 е № 4 - рл.:ра.5атхл пратршшкога VDEH 1000 50 30 75 78 7 Nt 5 - тестиртиниЕ 1000 1000 70 35 45 в >4 б - 1Н4ЛНГ И рирамтм требоиккй 25 43 1000 10М 1000 9 7 — рэ:рэаати эру ИТ 40 25 45 1000 1000 10 Целевая функция Огр. J вида Догг лнаи. 111 1 195. 1 55 150 и 75 150 14 ’ М 150 15 0 150 1G 0 150 17; 13 1*. пл с н 1 1 к L м м О Нерешенные Огр. 1 ВИДЙ Доп. ЗН4Ч. 1 0 0 0 0 1 1 0 1 0 0 0 1 1 0 1 0 0 0 1 1 0 0 1 0 0 1 1 0 0 0 1 0 1 1 1 0 0 0 0 1 1 -1Е-12 1 0 0 0 1 1 Рис. 2.29. Граф результата Как показано на примерах рассмотрения архитектурных представле- ний программных систем, каждое архитектурное представление может иметь множество допустимых вариантов, среди которых есть лучший по некоторому критерию. Задача архитектора заключается в такой фор- мализации возможных вариантов, которая позволит известными метода- ми и достаточно простыми инструментальными средствами выбрать оп- тимальный вариант в каждом архитектурном представлении программ- ной системы. Рассмотренные примеры решения подобных задач явля- ются подтверждением того факта, что в ряде случаев можно эффектив- но использовать известные подходы исследования операций для форма- лизации задачи и электронные таблицы для ее решения. 85
Литература к главе 2 1. A configuration of the Rational Unified Process for System Engineer- ing (RUP SE) // URL: http://www.cs.ut.ee/~kiho/TVTkonspekt/xTP165.pdf 2. 1EEE1471 [Электронный ресурс] // URL: http://en.wikipedia.org/ wiki/IEEE 1471 3. McGovern J.t Ambler S., Stevens M.t Linn J., Sharan V.t and Jo £. The Practical Guide to Enterprise Architecture. - Prentice Hall, 2003. 4. MDD. Общий обзор и концепция разработки, управляемой моде- лями [Электронный ресурс] И URL: http://www.ibm.com/developerworks/ ru/library/mdd/ch 1 /ch 1. html#author 1 5. Object Management Group [Электронный ресурс] // URL: http://en.wikipedia.org/wiki/Object_Managcment_Group 6. Software Engineering Institute, Carnegie Mellon [Электронный ре- сурс] // URL: http://www.sci.cmu.edu/architecture/definitions/html 7. S WEB OK [Электронный ресурс] // URL: http://ru.wikipedia.org/ wiki/SWEBOK. 8. Архитектура [Электронный ресурс] //URL: http://ru.wikipedia.org/ wiki/%C0%F0%F5%E8%F2%E5%EA%F2%F3%F0%E0 9. Басс Л., Клементс П., Кацман Р. Архитектура программного обес- печения на практике. — 2-е изд. — СПб.: Питер, 2006. — 575 с. 10. Брукс Ф. Мифический человеко-месяц, или как создаются про- граммные системы: пер. с англ. — СПб.: Символ-Плюс, 2001. — 304 с. 11. Буч Г., Рамбо Д., Якобсон И. Язык UML: руководство пользова- теля: пер. с англ. - 2-е изд. — М,: ДМК Пресс, 2007. — 496 с. 12. Данилин А.В., Слюсаренко А.И. Архитектура предприятия [Элек- тронный ресурс] // URL: http://www.intuit.ru/shop/ebooks/product. xhtml?id=2493646 13. Дейкстра Э. Дисциплина программирования [Электронный ре- сурс] // URL:http://khpiiip. mipk.kharkiv.edu/library/extent/dijkstra/pp/ ewdxO 1 .pdf 14. Илес П. Что такое архитектура программного обеспечения? Электронный ресурс] И URL: http://www.ibm.com/developerworks/ru/ ibrary/eeles/ 15. Крачтен Ф. Введение в Rational Unified Process. - 2-е издание. — М.: Издательский дом «Вильямс», 2002. — 240 с. 16. Леоненков А. Объектно-ориентированный анализ и проектиро- вание с использованием UML и IBM Rational Rose. — Изд. Интернет- университет информационных технологий; БИНОМ. Лаборатория зна- ний, 2006. — 320 с. 17. Майерс Г. Надежность программного обеспечения: пер. с англ. — М.: Мир, 1980.-360 с. 18. Михайловский Н. Архитектура информационной системы, оцен- ка рисков и совокупная стоимость владения [Электронный ресурс] И URL: http://kacit.ru/assets/files/soa/is_arc.pdf 86
19. Назаров CB.t Широков И.А. Современные операционные систе- мы. М.: Интернет-университет информационных технологий; БИНОМ; Лаборатория знаний, 2010. - 279 с. 20. Русинович М., Соломон Д. Внутреннее устройство Microsoft Win- dows. — 6-е изд. — СПб.: Питер, 2013. 21. Сервис-ориентированная архитектура [Электронный ресурс] // URL: http://ru.wikipedia.org/wiki/%D 1 %E5%F0%E2%E8%F 1 22. Соснин П.И. Архитектурное моделирование систем, интенсивно использующих программное обеспечение [Электронный ресурс] // URL: http://www.ict.edu.ru/ft/005651/62328el-stl5.pdf 23. Турский В. Методология программирования: пер. с англ. - М .: Мир, 1981. - 264 с. 24. Фейгин Д. Концепция SO А // «Открытые системы». - 2004. — № 6. 25. Цикритзис Д. Бернстайн Ф. Операционные системы: пер с англ. — М.: Мир, 1977. 26. Чернышов Л.Н. Эволюция программных систем: от модульной до сервис-ориентированной. Материалы конференции по вычислитель- ной механике и современным прикладным программным системам (ВМСППС), Алушта, Крым, 2005. — М.: Вузовская книга, 2005.
Глава 3 ЖИЗНЕННЫЙ ЦИКЛ ПРОГРАММНЫХ СИСТЕМ 3.1. Понятие жизненного цикла программных систем Понятие жизненного цикла программных систем является одним из базовых в программной инженерии, ЖЦ ПС определяется как период времени, который начинается с момента принятия решения о необходи- мости создания ПС и закачивается в момент ее полного изъятия из экс- плуатации. Основным нормативным документом, регламентирующим состав процессов ЖЦ ПС, является международный стандарт ISO/1EC 12207: 1995 Information Technology - Software Life Cycle Process (ISO- International Organization for Standardization - Международная организа- ция по стандартизации; IEC — International Electrotechnical Commission — Международная комиссия по электротехнике) [3]. Этот стандарт опре- деляет структуру ЖЦ, содержащую процессы, действия и задачи, кото- рые должны быть выполнены в процессе создания и дальнейшего ис- пользования ПС. В данном стандарте ПС (или программный продукт) определяется как набор компьютерных программ, процедур и, возможно, связанных с ними документацией и данными. Процесс определяется как совокуп- ность взаимосвязанных действий, преобразующих некоторые входные данные в выходные (Г. Майерс называет это трансляцией данных [20]). Каждый процесс характеризуется определенными задачами и методами их решения. В свою очередь, каждый процесс разделен на набор дейст- вий, а каждое действие — на набор задач. Каждый процесс, действие или задача инициируется и выполняется другим процессом по мере необхо- димости, причем не существует заранее определенных последователь- ностей выполнения (естественно, при сохранении связей по входным данным). Следует отметить, что в бывшем Советском Союзе, а затем в России создание программного обеспечения первоначально, в 70-е гг. прошлого столетия, регламентировалось стандартами ГОСТ ЕСПД (Единой систе- мы программной документации — серии ГОСТ 19.ХХХ [13]), которые были ориентированы на класс относительно простых программ неболь- шого объема, создаваемых отдельными программистами. В настоящее время эти стандарты устарели концептуально и по форме, их сроки дей- ствия можно считать законченными, и их использование нецелесооб- разно. Процессы создания автоматизированных систем (АС), в состав кото- рых входит и ПО, регламентированы стандартами ГОСТ 34.601—90 «Ин- формационная технология. Комплекс стандартов на автоматизирован- ные системы. Стадии создания», ГОСТ 34.602-89 «Информационная технология. Комплекс стандартов на автоматизированные системы. Тех- ническое задание на создание автоматизированной системы» и ГОСТ 34.603-92 «Информационная технология. Виды испытаний автоматизи- рованных систем» [14]. Однако многие положения этих стандартов ус- 88
тарели, а другие отражены недостаточно, чтобы их можно было исполь- зовать для серьезных проектов создания ПС. Поэтому в отечественных разработках целесообразно использовать современные международные стандарты. В соответствии со стандартом ISO/1EC 12207 [2,7] все процессы ЖЦ ПО разделены на три группы (рис. 3.1). Вспомогательные процессы Документирование Основные процессы Приобретение Поставка Организационные процессы Управление Создание инфраструктуры Управление конфигурацией Разрешение проблем Усовершенство- вание Обучение Заказчик Поставщик Заказчик Заказчик Поставщик Рис. 3.1. Процессы ЖЦ ПО В группах определено пять основных процессов: приобретение, по- ставка, разработка, эксплуатация и сопровождение. Восемь вспомога- тельных процессов обеспечивают выполнение основных процессов, а именно: документирование, управление конфигурацией, обеспечение качества, верификация, аттестация, совместная оценка, аудит, разре- шение проблем. Четыре организационных процесса обеспечивают управление, создание инфраструктуры, усовершенствование и обуче- ние. 3.2. Основные процессы ЖЦ ПС 1. Процесс приобретения состоит из действий и задач заказчика, приобретающего ПС. Данный процесс охватывает следующие действия: 1) инициирование приобретения; 2) подготовку заявочных предложений; 3) подготовку и корректировку договора; 4) надзор за деятельностью поставщика; 5) приемку и завершение работ. 89
Инициирование приобретения включает следующие задачи: 1) определение заказчиком своих потребностей в приобретении, раз- работке или усовершенствовании системы, программных продуктов или услуг; 2) анализ требований, предъявляемых к системе; 3) принятие решения относительно приобретения, разработки или усовершенствования существующей ПС; 4) проверку наличия необходимой документации, гарантий, серти- фикатов, лицензий и поддержки в случае приобретения программного продукта; 5) подготовку и утверждение плана приобретения, включающего требования к системе, тип договора, ответственность сторон и т.д. Заявочные предложения должны содержать: 1) требования, предъявляемые к системе; 2) перечень программных продуктов; 3) условия приобретения и соглашения; 4) технические ограничения (например, по среде функционирования системы). Заявочные предложения направляются выбранному поставщику или нескольким поставщикам в случае тендера. Поставщик — это организа- ция, которая заключает договор с заказчиком на поставку системы, ПС или программной услуги на условиях, оговоренных в договоре. Подготовка и корректировка договора включают в себя следующие задачи: 1) определение заказчиком процедуры выбора поставщика, вклю- чающей в себя критерии оценки предложений возможных поставщиков; 2) выбор конкретного поставщика на основе анализа предложений; 3) подготовку и заключение договора с поставщиком; 4) внесение изменений (при необходимости) в договор в процессе его выполнения. Надзор за деятельностью поставщика осуществляется в соответствии с действиями, предусмотренными в процессах совместной оценки и ау- дита. В процессе приемки подготавливаются и выполняются необходи- мые тесты. Завершение работ по договору осуществляется в случае удовлетворения всех условий приемки. 2. Процесс поставки охватывает действия и задачи, выполняемые поставщиком, который снабжает заказчика программным продуктом или услугой. Данный процесс включает в себя следующие действия: 1) инициирование поставки; 2) подготовку ответа на заявочные предложения; 3) подготовку договора; 4) планирование работ по договору; 5) выполнение и контроль договорных работ и их оценку; 6) поставку и завершение работ. Инициирование поставки заключается в рассмотрении поставщиком заявочных предложений и принятии решения о согласии с выставлен- ными требованиями и условиями или предложении своих условий. Воз- можно также согласование предложений поставщика и заказчика. 90
Планирование включает следующие задачи: 1) принятие решения поставщиком относительно выполнения работ своими силами или с привлечением субподрядчика; 2) разработку поставщиком плана управления проектом, содержаще- го организационную структуру проекта, разграничение ответственно- сти, технические требования к среде разработки и ресурсам, управление субподрядчиками и др. 3. Процесс разработки предусматривает действия и задачи, выпол- няемые разработчиком, и охватывает работы по созданию ПС и ее ком- понентов в соответствии с заданными требованиями. Сюда включается оформление проектной и эксплуатационной документации, подготовка материалов, необходимых для проверки работоспособности и качества программных продуктов, материалов, необходимых для организации обучения персонала, и др. Процесс разработки включает в себя следующие действия: 1) подготовительную работу; 2) анализ требований, предъявляемых к системе; 3) проектирование архитектуры системы; 4) анализ требований, предъявляемых к программной системе; 5) проектирование архитектуры программной системы; 6) детальное проектирование программной системы; 7) кодирование и тестирование программной системы; 8) интеграцию программной системы; 9) квалификационное тестирование программной системы; 10) интеграцию системы; 11) квалификационное тестирование системы; 12) установку программной системы; 13) приемку программной системы. Подготовительная работа начинается с выбора модели ЖЦ ПС, со- ответствующей масштабу, значимости и сложности проекта. Действия и задачи процесса разработки должны соответствовать выбранной моде- ли. Разработчик должен выбирать, адаптировать к условиям проекта и использовать согласованные с заказчиком стандарты, методы и сред- ства разработки, а также составлять план выполнения работ. Анализ требований, предъявляемых к системе, подразумевает опре- деление ее функциональных возможностей, пользовательских требова- ний, требований к надежности, безопасности, требований к внешним интерфейсам, производительности и т.д. Требования к системе оценива- ются, исходя из критериев реализуемости и возможности проверки при тестировании. Проектирование архитектуры системы заключается в определении компонентов ее оборудования (аппаратуры), программного обеспечения и операций, выполняемых эксплуатирующим систему персоналом. Ар- хитектура системы должна соответствовать требованиям, предъявляе- мым к системе, а также принятым проектным стандартам и методам. Анализ требований к программной системе предполагает определе- ние следующих характеристик для каждого компонента ПО: 91
1) функциональных возможностей, включая характеристики произ- водительности и среды функционирования компонента; 2) внешних интерфейсов; 3) спецификаций надежности и безопасности; 4) эргономических требований; 5) требований к используемым данным; 6) требований к установке и приемке; 7) требований к пользовательской документации; 8) требований к эксплуатации и сопровождению. Требования к программной системе оцениваются, исходя из крите- риев соответствия требованиям, предъявляемым к системе в целом, реа- лизуемости и возможности проверки при тестировании. Проектирование архитектуры ПС включает в себя следующие зада- чи для каждого компонента ПС: 1) трансформацию требований к ПС в архитектуру, определяющую на высоком уровне структуру ПС и состав ее компонентов; 2) разработку и документирование программных интерфейсов ПС и баз данных (БД); 3) разработку предварительной версии пользовательской документа- ции; 4) разработку и документирование предварительных требований к тестам и плана интеграции ПС. Детальное проектирование ПС включает в себя следующие задачи: 1) описание компонентов ПС и интерфейсов между ними на более низком уровне, достаточном для последующего кодирования и тестиро- вания; 2) разработку документирование детального проекта базы данных; 3) обновление (при необходимости) пользовательской документа- ции; 4) разработку и документирование требований к тестам и плана тес- тирования компонентов ПС; 5) обновление плана интеграции ПС. Кодирование и тестирование ПС включает в себя следующие задачи: 1) кодирование и документирование каждого компонента ПС и базы данных, а также подготовку совокупности тестовых процедур и данных для их тестирования; 2) тестирование каждого компонента ПС и БД на соответствие предъявляемым к ним требованиям с последующим документированием результатов тестирования; 3) обновление документации (при необходимости); 4) обновление плана интеграции ПС. Интеграция ПС предусматривает сборку разработанных компонен- тов ПС в соответствии с планом интеграции и тестирования агрегиро- ванных компонентов. Для каждого из агрегированных компонентов раз- рабатываются наборы тестов и тестовые процедуры, предназначенные для проверки каждого из квалификационных требований при последую- щем квалификационном тестировании. Квалификационное требова- ние— это набор критериев или условий, которые необходимо выпол- 92
нить, чтобы квалифицировать программный продукт как соответствую- щий своим спецификациям и готовый к использованию в условиях экс- плуатации. Квалификационное тестирование ПС проводится разработчиком в присутствии заказчика (по возможности) для демонстрации того, что ПС удовлетворяет своим спецификациям и готова к использованию в заданных условиях эксплуатации. Такое тестирование выполняется для каждого компонента программного продукта по всем разделам тре- бований при широком варьировании тестов. При этом также проверяет- ся полнота технической и пользовательской документации и ее адекват- ность самим компонентам ПС. Интеграция системы заключается в сборке всех ее компонентов, включая ПС и оборудование. После интеграции система, в свою оче- редь, подвергается квалификационному тестированию на соответствие предъявляемым к ней требованиям. При этом также производятся оформление и проверка полного комплекта документации на систему. Установка ПС осуществляется разработчиком в соответствии с пла- ном в той среде и на том оборудовании, которые предусмотрены дого- вором. В процессе установки проверяется работоспособность ПС и БД. Если устанавливаемая ПС заменяет существующую систему, разработ- чик должен обеспечить их параллельное функционирование в соответ- ствии с договором. Приемка ПС предусматривает оценку результатов квалификацион- ного тестирования ПС и системы и документирование результатов оценки, которые производятся заказчиком с помощью разработчика. Разработчик выполняет окончательную передачу ПС заказчику в соот- ветствии с договором, обеспечивая при этом необходимое обучение и поддержку. 4. Процесс эксплуатации охватывает действия и задачи организации- оператора, эксплуатирующего систему. Процесс эксплуатации включает в себя следующие действия: 1) подготовительную работу, которая включает в себя проведение оператором следующих задач: • планирования действий и работ, выполняемых в процессе эксплуа- тации, и установку эксплуатационных стандартов; • определения процедур локализации и разрешения проблем, возни- кающих в процессе эксплуатации; 2) эксплуатационное тестирование, осуществляемое для каждой оче- редной редакции программного продукта, после чего эта редакция пере- дается в эксплуатацию; 3) собственно эксплуатацию системы, которая выполняется в пред- назначенной для этого среде в соответствии с пользовательской доку- ментацией; 4) поддержку пользователей — оказание помощи и консультаций при обнаружении ошибок в процессе эксплуатации ПС. 5. Процесс сопровождения представляет собой действия и задачи, выполняемые сопровождающей организацией, при изменениях (моди- 93
фикациях) программного продукта и соответствующей документации, вызванных возникшими проблемами или потребностями в модерниза- ции или адаптации ПС. Изменения, вносимые в соответствующую ПС, не должны нарушать ее целостность. Процесс сопровождения включает ее перенос в другую среду (миграцию) и заканчивается снятием ПС с эксплуатации. Процесс сопровождения охватывает следующие действия: 1) подготовительную работу (планирование действий и работ, опре- деление процедур локализации и разрешения проблем, возникающих в процессе сопровождения); 2) анализ проблем и запросов на модификацию ПС (анализ сообще- ний о возникшей проблеме или запроса на модификацию, оценка мас- штаба, стоимости модификации, получаемого эффекта, оценка целесо- образности модификации); 3) модификацию ПС (внесение изменений в компоненты программ- ного продукта и документацию в соответствии с правилами процесса разработки); 4) проверку и приемку (в части целостности модифицируемой систе- мы); 5) перенос ПС в другую среду (конвертирование программ и данных, параллельная эксплуатация ПС в старой и новой среде в течение неко- торого периода времени); 6) снятие ПС с эксплуатации по решению заказчика при участии экс- плуатирующей организации, службы сопровождения и пользователей. При этом программные продукты и документация подлежат архивиро- ванию в соответствии с договором. 3.3. Вспомогательные процессы ЖЦ ПО 1. Процесс документирования предусматривает формализованное описание информации, созданной в течение ЖЦ ПС. Данный процесс состоит из набора действий, с помощью которых планируют, проекти- руют, разрабатывают, выпускают, редактируют, распространяют и со- провождают документы, необходимые для всех заинтересованных лиц, таких как руководство, технические специалисты и пользователи систе- мы. Процесс документирования включает в себя следующие действия: 1) подготовительную работу; 2) проектирование и разработку; 3) выпуск документации; 4) сопровождение. 2. Процесс управления конфигурацией включает в себя администра- тивные и технические процедуры на всем протяжении ЖЦ ПС для опре- деления состояния компонентов ПС, описания и подготовки отчетов о состоянии компонентов ПС и запросов на модификацию, обеспечения полноты, совместимости и корректности компонентов ПО, управления хранением и поставкой ПС. 94
Согласно стандарту IEEE-90, под конфигурацией ПС понимается со- вокупность ее функциональных и физических характеристик, установ- ленных в технической документации и реализованных в ПС. Управле- ние конфигурацией позволяет организовывать, систематически учиты- вать и контролировать внесение изменений в ПС на всех стадиях ЖЦ. Общие принципы и рекомендации по управлению конфигурацией ПС отражены в стандарте ISO/1EC 15288 Information Technology. Software Life Cycle Process. Configuration Management for Software [4]. Процесс управления конфигурацией включает в себя следующие действия: 1) подготовительную работу, заключающуюся в планировании управления конфигурацией; 2) идентификацию конфигурации, устанавливающую правила, с по- мощью которых однозначно идентифицируются компоненты ПС и их версии (при этом каждому компоненту однозначно соответствует ком- плект документации); 3) контроль конфигурации - действие, предназначенное для система- тической оценки предлагаемых модификаций ПС и координированной их реализации с учетом эффективности каждой модификации и затрат на ее выполнение; 4) учет состояния конфигурации, представляющий собой регистра- цию состояния компонентов ПС (обеспечивает подготовку отчетов о реализованных и отвергнутых модификациях версий компонентов ПС, а совокупность отчетов дает однозначное отражение текущего состоя- ния системы и ее компонентов и обеспечивает ведение истории моди- фикаций); 5) оценку конфигурации, заключающуюся в определении функцио- нальной полноты компонентов ПС, а также соответствия их физическо- го состояния текущему техническому описанию; 6) управление выпуском и поставку, охватывающие изготовление эталонных копий программ и документации, их хранение и поставку пользователям в соответствии с порядком, принятом в организации. 3. Процесс обеспечения качества должен обеспечивать гарантии то- го, что ПС и процессы ее ЖЦ соответствуют заданным требованиям и утвержденным планам. Под качеством ПС понимается совокупность свойств, которая характеризует способность ПС удовлетворять за- данным требованиям. Для получения достоверных оценок о создавае- мой ПС процесс обеспечения ее качества должен происходить независи- мо от субъектов, непосредственно связанных с разработкой программ- ного продукта. При этом могут использоваться результаты других вспо- могательных процессов, таких как верификация, аттестация, совмест- ная оценка, аудит и разрешение проблем. Процесс обеспечения качества включает в себя следующие действия: 1) подготовительную работу (координацию с другими вспомогатель- ными процессами и планирование самого процесса обеспечения качест- ва ПС с учетом используемых стандартов, методов, процедур и средств); 95
2) обеспечение качества продукта, подразумевающего гарантирован- ное полное соответствие ПС и ее документации требованиям заказчика, предусмотренным в договоре; 3) обеспечение качества процесса, предполагающее гарантированное соответствие процессов ЖЦ ПС, методов разработки, среды разработки и квалификации персонала условиям договора, установленным стандар- там и процедурам; 4) обеспечение прочих показателей качества ПС, осуществляемое в соответствии с условиями договора и стандартом качества ISO 9001. 4. Процесс верификации состоит в определении того факта, что ПС, являющаяся результатом некоторой деятельности, полностью удовле- творяет требованиям или условиям, обусловленным предшествующими действиями. Для повышения эффективности всего процесса ЖЦ ПС ве- рификация должна как можно раньше интегрироваться с использующи- ми ее процессами (т.е. с поставкой, разработкой, эксплуатацией). Про- цесс верификации может включать в себя анализ, оценку и тестирова- ние. Верификация может проводиться с различными степенями независи- мости (от самого исполнителя до специалистов другой организации, не зависящей от поставщика, разработчика и т.д.). В процессе верифика- ции проверяются следующие условия: 1) непротиворечивость требований, предъявляемых к системе, и сте- пень учета потребностей пользователей; 2) возможность поставщика выполнить заданные требования; 3) соответствие выбранных процессов ЖЦ ПС условиям договора; 4) адекватность стандартов, процедур и среды разработки процессам ЖЦ ПС; 5) соответствие проектных спецификаций ПС заданным требованиям; 6) корректность описания в проектных спецификациях входных и выходных данных, последовательности событий, интерфейсов, логики и т.д.; 7) соответствие кода проектным спецификациям и требованиям; 8) тестируемость и корректность кода, его соответствие принятым стандартам кодирования; 9) корректность интеграции компонентов ПС в систему; 10) адекватность, полнота и непротиворечивость документации. 5. Процесс аттестации предназначен для определения полноты со- ответствия заданных требований и созданной ПС их конкретному функ- циональному назначению (тому, что требуется потребителю). Под атте- стацией обычно понимается подтверждение и оценка достоверности проведенного тестирования программного продукта. Аттестация долж- на гарантировать полное соответствие ПО спецификациям, требованиям и документации, а также возможность безопасного и надежного приме- нения ПО пользователем. Аттестация, как и верификация, может осуществляться с различны- ми степенями независимости (вплоть до организации, не зависящей от поставщика, разработчика, оператора или службы сопровождения). 96
6. Процесс совместной оценки предназначен для оценки состояния работ по проекту и программному продукту, создаваемому при выпол- нении этих работ. Он сосредоточен в основном на контроле планирова- ния и управления ресурсами, персоналом, аппаратурой и инструмен- тальными средствами проекта. Оценка применяется как на уровне управления проектом, так и на уровне технической реализации проекта и проводится в течение всего срока действия договора. Данный процесс может выполняться двумя сторонами, участвующими в договоре, при этом одна сторона проверяет другую. 7. Процесс аудита представляет собой определение соответствия проекта и продукта требованиям, планам и условиям договора. Аудит может выполняться двумя любыми сторонами, участвующими в догово- ре, когда одна сторона проверяет другую. Аудит - это ревизия (проверка), проводимая компетентным органом (лицом) в целях обеспечения независимой оценки степени соответствия ПС или процессов установленным требованиям. Аудит служит для установления соответствия реальных работ и от- четов требованиям, планам и контракту. Аудиторы не должны иметь прямой зависимости от разработчиков ПС. Они определяют состояние работ, использование ресурсов, соответствие документации специфика- циям и стандартам, корректность тестирования и др. 8. Процесс разрешения проблем предусматривает анализ и разреше- ние проблем (включая обнаруженные несоответствия), которые обнару- жены в ходе разработки, эксплуатации или других процессов независи- мо от их происхождения или источника. 3.4. Организационные процессы ЖЦ ПС 1 . Процесс управления состоит из действий и задач, которые могут выполняться любой стороной, управляющей своими процессами. Дан- ная сторона (менеджер) отвечает за управление выпуском продукта, управление проектом и управление задачами соответствующих процес- сов, таких как приобретение, поставка, разработка, эксплуатация, сопро- вождение и др. Процесс управления включает в себя следующие действия: 1 ) инициирование иопределение области управления— менеджер должен убедиться, что необходимые для управления ресурсы (персонал, оборудование и технология) имеются в его распоряжении в достаточ- ном количестве; 2 ) планирование (как действие) подразумевает выполнение следую- щих задач: • составление графиков выполнения работ; • оценку затрат; • выделение требуемых ресурсов; • распределение ответственности; • оценку рисков, связанных с конкретными задачами; 97
• создание инфраструктуры управления, 2. Процесс создания инфраструктуры охватывает выбор и поддерж- ку технологий, стандартов и инструментальных средств, используемых для разработки, эксплуатации или сопровождения ПС. Инфраструктура должна модифицироваться и сопровождаться в соответствии с измене- ниями требований к соответствующим процессам. Инфраструктура, в свою очередь, является одним из объектов управления конфигураци- ей. Процесс создания инфраструктуры включает в себя следующие дей- ствия: • подготовительную работу; • создание инфраструктуры; • сопровождение инфраструктуры. 3. Процесс усовершенствования предусматривает оценку, измерение, контроль и собственно усовершенствование процессов ЖЦ ПС. Этот процесс включает в себя три основных действия: • создание процесса; • оценку процесса; • усовершенствование процесса. Усовершенствование процессов ЖЦ ПС направлено на повышение производительности труда всех участвующих в них специалистов за счет совершенствования используемой технологии, методов управле- ния, выбора инструментальных средств и обучения персонала. Усовер- шенствование основано на анализе достоинств и недостатков каждого процесса. Такому анализу способствует накопление в организации исто- рической, технической, экономической и иной информации по реализо- ванным проектам. 4. Процесс обучения включает в себя первоначальное обучение и по- следующее постоянное повышение квалификации персонала и состоит из трех действий: • подготовительной работы; • работки учебных материалов; • реализации планов обучения. 3.5. Взаимосвязь между процессами ЖЦ ПС Процессы ЖЦ ПС, регламентируемые стандартом 1SO/IEC 12207, могут использоваться различными организациями в конкретных проек- тах самым различным образом. Тем не менее стандарт предлагает неко- торый базовый набор взаимодействий между процессами с различных точек зрения (либо в различных аспектах), который показан на рис. 3.2. Такими аспектами являются: 1) договорной аспект, в котором заказчик и поставщик вступают в до- говорные отношения и реализуют процессы приобретения и поставки; 2) аспект управления, включающий в себя действия управления ли- цами, участвующими в ЖЦ ПС (поставщик, заказчик, разработчик, опе- ратор и др.); 98
3) аспект эксплуатации, включающий в себя действия оператора по предоставлению услуг пользователям системы; 4) инженерный аспект, содержащий действия разработчика или службы сопровождения по решению технических задач, связанных с разработкой или модификацией программных продуктов; 5) аспект поддержки, связанный с реализацией вспомогательных процессов, с помощью которых службы поддержки предоставляют не- обходимые услуги всем остальным участникам работ (в этом аспекте можно выделить аспект управления качеством ПС, включающий в себя процессы обеспечения качества, верификацию, аттестацию, совместную оценку и аудит). Рис. 3.2. Взаимосвязь между процессами ЖЦ ПС Организационные процессы выполняются на корпоративном уровне, или на уровне всей организации в целом, создавая базу для реализации и постоянного совершенствования процессов ЖЦ ПС. 3.6. Модели и стадии ЖЦ ПС Под моделью ЖЦ ПС понимается структура, определяющая последо- вательность выполнения и взаимосвязи процессов, действий и задач на протяжении ЖЦ ПС. Модель ЖЦ зависит от специфики, масштаба и сложности проекта и специфики условий, в которых система создает- 99
ся и функционирует. Стандарт 1SO/1EC 12207 не предлагает конкретную модель ЖЦ и методы разработки ПС. Его положения являются общими для любых моделей ЖЦ, методов и технологий разработки ПС. Стан- дарт описывает структуру процессов ЖЦ ПС, но не конкретизирует в деталях, как реализовать или выполнить действия и задачи, включен- ные в эти процессы. Модель ЖЦ любой конкретной ПС определяет характер процесса ее создания, который представляет собой совокупность упорядоченных во времени, взаимосвязанных и объединенных в стадии (фазы) работ, вы- полнение которых необходимо для создания ПС, соответствующей за- данным требованиям. Под стадией (фазой) создания ПС понимается часть процесса созда- ния ПС, ограниченная некоторыми временными рамками и заканчиваю- щаяся выпуском конкретного продукта (моделей ПС, программных ком- понентов, документации и пр.), определяемого заданными для данной стадии требованиями. Стадии создания ПС выделяются по соображени- ям рационального планирования и организации работ, заканчивающих- ся заданными результатами. В состав ЖЦ ПС обычно включаются сле- дующие стадии: 1) формирование требований к ПС; 2) проектирование (разработка системного проекта); 3) реализация (может быть разбита на подэтапы: детальное проекти- рование, кодирование); 4) тестирование (может быть разбито на автономное и комплексное тестирование и интеграцию); 5) ввод в действие (внедрение); 6) эксплуатация и сопровождение; 7) снятие с эксплуатации. Некоторые специалисты, например Б. Боэм [6], вводят дополнитель- но начальную стадию - анализ осуществимости системы, что следует считать оправданным, особенно при создании достаточно сложных сис- тем. Здесь имеется в виду программно-аппаратная система, для которой создается, приобретается или модифицируется ПС. Стадия формирования требований к ПС является одной из важней- ших и определяет в значительной (даже решающей!) степени успех все- го проекта. Началом этой стадии является получение одобренной и ут- вержденной архитектуры системы с включением основных соглашений о распределении функций между аппаратурой и программами. Этот до- кумент должен также содержать подтверждение общего представления о функционировании ПС с включением основных соглашений о распре- делении функций между человеком и системой. Стадия формирования требований к ПС включает в себя следующие этапы. 1. Планирование работ, предваряющих работы над проектом. Основ- ными задачами этапа являются: а) определение целей разработки; б) предварительная экономическая оценка проекта; в) построение плана-графика выполнения работ; 100
г) создание и обучение совместной рабочей группы. 2. Проведение обследования деятельности автоматизируемой орга- низации (объекта), в рамках которого осуществляются: а) предварительное выявление требований к будущей системе; б) определение структуры организации; в) определение перечня целевых функций организации; г) анализ распределения функций по подразделениям и сотрудникам; д) выявление функциональных взаимодействий между подразделе- ниями; е) установление информационных потоков внутри подразделений и между ними; ж) выявление внешних по отношению к организации объектов и внешних информационных воздействий; з) анализ существующих средств автоматизации деятельности орга- низации. 3. Построение модели деятельности организации (объекта), преду- сматривающее обработку материалов обследования и построение двух видов моделей: а) модели AS — IS (как есть), отражающей существующее на момент обследования положение дел в организации и позволяющей понять, ка- ким образом работает данная организация, а также выявить узкие места и сформулировать предложения по улучшению ситуации; б) модели ТО — BE (как должно быть), отражающей представление о новых технологиях работы организации. Каждая из моделей должна включать в себя полную функциональ- ную и информационную модель деятельности организации, а также (при необходимости) модель, описывающую динамику поведения орга- низации. Заметим, что построенные модели имеют самостоятельное практическое значение, независимо от того, будет ли на предприятии разрабатываться и внедряться информационная система, поскольку с их помощью можно обучать сотрудников и совершенствовать бизнес-про- цессы предприятия. Результатом завершения стадии формирования требований к ПО яв- ляются спецификации ПС — функциональные, технические и интерфейс- ные спецификации, для которых подтверждена их полнота, проверяе- мость и осуществимость. Стадия проектирования включает следующие этапы. 1. Разработку системного проекта ПС. На этом этапе дается ответ на вопрос «Что должна делать будущая система?», а именно: определя- ются архитектура системы, ее функции, внешние условия функциониро- вания, интерфейсы и распределение функций между пользователями и системой, требования к программным и информационным компонен- там, состав исполнителей и сроки разработки, план отладки ПО и кон- троль качества. Основу системного проекта составляют модели проектируемой сис- темы, которые строятся на модели ТО — BE. Результатом разработки системного проекта должна быть одобренная и подтвержденная, специ- 101
фикация требований к ПС - функциональные, технические и интер- фейсные спецификации, для которых подтверждена их полнота, прове- ряемость и осуществимость. 2. Разработку детального (технического) проекта. На этом этапе осуществляется собственно проектирование ПС, включающее в себя проектирование архитектуры системы и детальное проектирование. Та- ким образом, дается ответ на вопрос: «Как построить систему, чтобы она удовлетворяла требованиям?» Результатом детального проектирования является разработка вери- фицированной спецификации ПС, включающей в себя [6, 20]: • формирование иерархии программных компонентов, межмодуль- ных интерфейсов по данным и управлению; • спецификация каждого компонента ПС, имени, назначения, пред- положений, размеров, последовательности вызовов, входных и выход- ных данных, ошибочных выходов, алгоритмов и логических схем; • формирование физической и логической структур данных до уров- ня отдельных полей; • разработку плана распределения вычислительных ресурсов (вре- мени работы центральных процессоров, памяти и др.); • верификацию полноты, непротиворечивости, осуществимости и обоснованности требований; • предварительный план комплексирования и отладки, план руково- дства для пользователей и приемных испытаний. Завершением стадии детального проектирования является сквозной контроль проекта, или критический поблочный анализ проекта. Стадия реализации предусматривает выполнение следующих работ. 1. Разработку верифицированной детальной спецификации каждой подпрограммы (блока не более чем из 100 исходных команд языка вы- сокого уровня). Внешние спецификации должны содержать следующие сведения: • имя модуля - указывается имя, применяемое для вызова модуля (для модуля с несколькими входами для каждого входа должны быть от- дельные спецификации); • функция — дается определение функции или функций, выполняе- мых модулем; • список параметров (число и порядок следования), передаваемых модулю; • выходные параметры — точное описание всех данных, возвращае- мых модулем (должно быть определено поведение модуля при любых входных условиях); • внешние эффекты (печать сообщения, чтение запроса с терминала и т.п.). 2. Проектирование логики модулей и программирование (кодирова- ние) модулей. 3. Проверку правильности модулей. 4. Тестирование модулей. 102
5. Описание базы данных до уровня отдельных параметров, симво- лов и битов. 6. Разработку плана приемных испытаний. 7. Разработку руководства пользователя. 8. Предварительный план комплексирования и отладки. Содержание последующих стадий в основном совпадает с соответст- вующими процессами ЖЦ ПС. Вообще, технологические стадии выде- ляются, исходя из соображений разумного и рационального планирова- ния и организации работ. Возможный вариант взаимосвязи и стадий ра- бот с процессами ЖЦ ПО [7, 18] показан на рис. 3.3. 3.7. Виды моделей ЖЦ ПС и технологии создания программных систем 3.7.1. Каскадная модель (классический жизненный цикл) Эта модель обязана своим появлением У . Ройсу (1970) [16]. Модель имеет и другое название — «Водопад» (Waterfall). Особенность модели — переход на следующую ступень осуществляется только после того, как будет полностью завершена работа на предыдущей стадии, возвратов на пройденные стадии не предусматривается (рис. 3.4). Требования к разрабатываемой ПС, определенные на стадиях формирования и ана- лиза, строго документируются в виде ТЗ и фиксируются на все время разработки проекта. Каждая стадия завершается выпуском полного ком- плекта документации (ТЗ, ЭП, ТП, РП), достаточного для того, чтобы разработка могла быть продолжена другой командой разработчиков. Основные процессы Рис. 3.3. Вариант взаимосвязи и стадий работ с процессами ЖЦ ПО 103
Критерием качества разработки при таком подходе является точ- ность выполнения спецификаций ТЗ. Основное внимание разработчиков сосредотачивается на достижении оптимальных значений технических характеристик разрабатываемой ПС - производительности, объема за- нимаемой памяти и др. Преимущества каскадной модели: • на каждой стадии формируется законченный набор проектной до- кументации, отвечающей критериям полноты и согласованности; • выполняемые в логической последовательности стадии работ по- зволяют планировать сроки завершения всех работ и соответствующие затраты. Рис. 3.4. Каскадная модель ЖЦ ПС Каскадный подход хорошо зарекомендовал себя при построении ПС, для которых в самом начале проекта можно полно и четко сформулиро- вать все требования. Такие проекты характерны для организаций госу- дарственного управления, министерств и ведомств, например Мини- стерства обороны и т.п. Пока все это контролируется стандартами и раз- личными комиссиями госприемки, схема работает хорошо. Применять такой подход при создании проектов ПС для коммерческих организаций неэффективно. Именно здесь проявляются его недостатки: • выявление и устранение ошибок производится только на стадии тестирования, которое может существенно растянуться; • реальные проекты часто требуют отклонения от стандартной по- следовательности шагов; • цикл основан на точной формулировке исходных требований к ПС, но в начале проекта требования заказчика определены лишь час- тично; • результаты работ доступны заказчику только по завершении про- екта. 104
3.7.2. Итерационная модель ЖЦ ПС С ростом коммерческих проектов выяснилось, что не всегда удается детально проработать проект будущей системы, поскольку многие ас- пекты ее функционирования в динамических сферах деятельности (биз- нес) меняются, пока система создается. Потребовалось изменить про- цесс разработки так, чтобы гарантировать внесение необходимых ис- правлений после завершения какого-либо этапа разработки. Так появи- лась итерационная модель ЖЦ ПС, называемая моделью с промежуточ- ным контролем, или моделью с циклическим повторением фаз [6, 7, 9]. В итерационной модели (рис. 3.5) недостатки проектирования и программирования могут быть устранены позже путем частичного возврата на предыдущую стадию. Чем ниже уровень обнаружения ошибки, тем дороже ее исправление. Если стоимость усилий, необходи- мых для обнаружения и устранения ошибок на стадии написания кода, принять за единицу, то стоимость выявления и устранения ошибки на стадии выработки требований будет в 5-10 раз меньше, а стоимость вы- явления и устранения ошибки на стадии сопровождения в 20 раз больше (рис. 3.6). В некоторых публикациях приводятся еще более шокирую- щие результаты, такие как десятикратное возрастание затрат на устране- ние ошибок с каждым последующим этапом [25]. Рис. 3.5. Итерационная модель ЖЦ ПС 105
ЭТАП Формирование требований Проектирование Кодирование Тестирование Приемка Поддержка и обслуживание Рис. 3.6. Стоимость выявления и устранения ошибки на различных этапах ЖЦ ПС В такой ситуации огромное значение приобретает этап формулиро- вания требований, составление спецификаций и создание плана систе- мы. Программные архитекторы несут личную ответственность за все последующие изменения проектных решений. Объем документации ис- числяется тысячами страниц, число утверждающих заседаний огромно. Многие проекты так никогда и не покидают этап планирования, впав в «паралич анализа». Одним из возможных путей исключения подобных ситуаций является макетирование (прототипирование). Часто заказчик не может сформулировать требования по вводу, об- работке или выводу данных для будущего программного продукта. Раз- работчик может сомневаться в приспособленности продукта к операци- онной системе, в форме диалога с пользователем или эффективности алгоритма. В таких случаях целесообразно использовать макетирование [22]. Основная цель макетирования — снять неопределенность в требова- ниях заказчика. Макетирование (прототипирование) - процесс соз- дания модели требуемого продукта. 3.7.3. Макетирование Модель может принимать следующие формы: • бумажный макет (рисованная схема человеко-машинного диалога) или макет на основе ПК; • работающий макет, реализующий некоторую часть требуемых функций; • существующая программа, характеристики которой должны быть улучшены. Как показано на рис. 3.7, макетирование основывается на многократ- ном повторении итераций, в которых участвуют заказчик и разработчик. 106
Рис. 3.7. Итерационный процесс макетирования Последовательность действий при макетировании представлена на рис. 3.8. Макетирование начинается со сбора и уточнения требований к создаваемой программной системе. Разработчик и заказчик совместно определяют цели ПО, устанавливают, какие требования известны, а ка- кие предстоит доопределить. Затем выполняется быстрое проектирова- ние. В нем сосредотачиваются на характеристиках, которые должны быть видимыми пользователю. Быстрое проектирование приводит к по- строению макета. Макет оценивается заказчиком и используется для уточнения требований к ПО. Итерации продолжаются до тех пор, пока макет не выявит все требования заказчика и не даст возможность разра- ботчику понять, что должно быть сделано. Достоинства макетирования - возможность определения полных требований к системе. Недостатки макетирования: заказчик может при- нять макет за продукт; разработчик может принять макет за продукт. Следует пояснить суть недостатков. Когда заказчик видит работающую версию ПС, он перестает созна- вать, что в погоне за работающим вариантом ПС оставлены нерешенны- ми многие вопросы качества и удобства сопровождения системы. Когда же заказчику об этом говорит разработчик, то ответом может быть воз- мущение и требование скорейшего превращения макета в рабочий про- дукт. Это отрицательно сказывается на управлении разработкой ПС. Оценка макета заказчика Уточнение макета Рис. 3.8. Последовательность действий при макетировании 107
С другой стороны, для быстрого получения работающего макета раз- работчик часто идет на определенные компромиссы. Например, могут использоваться не самые подходящие языки программирования или операционная система. Для простой демонстрации может применяться неэффективный (простой) алгоритм. Спустя некоторое время разработ- чик забывает о причинах, по которым эти средства не подходят. В ре- зультате выбранный вариант (далеко не идеальный) интегрируется в систему. Прежде чем рассматривать другие модели ЖЦ ПС, которые пришли на смену каскадной модели, следует остановиться на стратегиях конст- руирования программных систем. Именно стратегия конструирования ПС во многом определяет модель ЖЦ ПС. 3.7.4. Стратегии конструирования ПС Существуют три стратегии конструирования программных систем [22]. 1. Однократный проход (каскадная стратегия, рассмотренная вы- ше) - линейная последовательность этапов конструирования. 2. Инкрементная стратегия. В начале процесса определяются все пользовательские и системные требования, оставшаяся часть конструи- рования выполняется в виде последовательности версий. Первая версия реализует часть запланированных возможностей, следующая версия реализует дополнительные возможности и т.д., пока не будет получена полная система. 3. Эволюционная стратегия. Система также строится в виде после- довательности версий, но в начале процесса определяются не все требо- вания. Требования уточняются в результате разработки версий. Характеристики стратегий конструирования ПС в соответствии с требованиями стандарта IEEE/EIA 12207 приведены в табл. 3.1. 3.7.5. Инкрементная модель Инкрементная модель является классическим примером инкремент- ной стратегии конструирования. Она объединяет элементы последова- тельной водопадной модели с итерационной философией макетирова- ния (предложена Б. Боэмом как усовершенствование каскадной моде- ли). Каждая линейная последовательность здесь вырабатывает постав- ляемый инкремент ПС. Например, ПС для обработки слов в 1 -м инкре- менте (версии) реализует функции базовой обработки файлов, функции редактирования и документирования; во 2-м инкременте — более слож- ные возможности редактирования и документирования; в 3-м инкремен- те - проверку орфографии и грамматики; в 4-м инкременте - возможно- сти компоновки страницы. 108
Таблица 3.1 Стратегии конструирования Стратегия конструирова- ния В начале процесса определены все требования? Множество циклов конст- руирования? Промежуточное ПО распространя- ется? Однократный проход Да Нет Нет Инкрементная (заплани- рованное улучшение про- дукта) Да Да Может быть Эволюционная Нет Да Да Первый инкремент приводит к получению базового продукта, реали- зующего базовые требования (правда, многие вспомогательные требова- ния остаются нереализованными). План следующего инкремента преду- сматривает модификацию базового продукта, обеспечивающую допол- нительные характеристики и функциональность. По своей природе инкрементный процесс итеративен, но, в отличие от макетирования, инкрементная модель обеспечивает на каждом инкре- менте работающий продукт. Схема такой модели ЖЦ ПС приведена на рис. 3.9. Одной из совре- менных реализаций инкрементного подхода является экстремальное программирование (ориентировано на очень малые приращения функ- циональности) [22]. I 1-й инкремент Поставка 1-го инкремента Рис. 3.9. Инкрементная модель ЖЦ ПС 3.7.6. Спиральная модель ЖЦ ПО Спиральная модель — классический пример применения эволюцион- ной стратегии конструирования. Модель (автор Б. Боэм, 1988) базирует- ся на лучших свойствах классического жизненного цикла и макетирова- ния, к которым добавляется новый элемент — анализ риска, отсутствую- щий в этих парадигмах. Модель определяет четыре действия, представ- ленные четырьмя квадрантами спирали (рис. 3.10): 1) планирование - определение целей, вариантов и ограничений; 2) анализ риска — анализ вариантов и распознавание/выбор риска; 3) конструирование — разработка продукта следующего уровня; 109
4) оценивание — оценка заказчиком текущих результатов конструи- рования. Интегрирующий аспект спиральной модели очевиден при учете ра- диального измерения спирали. С каждой итерацией по спирали строят- ся все более полные версии ПС. В первом витке спирали определяются начальные цели, варианты и ограничения, распознается и анализируется риск. Если анализ риска показывает неопределенность требований, на помощь разработчику и заказчику приходит макетирование, используе- мое в квадранте конструирования. Для дальнейшего определения проблемных и уточненных требова- ний может быть использовано моделирование. Заказчик оценивает ин- женерную (конструкторскую) работу и вносит предложения по модифи- кации (квадрант оценки заказчиком). Следующая фаза планирования и анализа риска базируется на предложениях заказчика. В каждом цикле по спирали результаты анализа риска формируются в виде «продол- жать - не продолжать». Если риск слишком велик, проект может быть остановлен. В большинстве случаев движение по спирали продолжается, с каж- дым шагом продвигая разработчиков к более общей модели системы. В каждом цикле движения по спирали требуется конструирование (ниж- ний правый квадрант), которое может быть реализовано классическим жизненным циклом или макетированием. Заметим, что количество дей- ствий по разработке (происходящих в правом нижнем квадранте) воз- растает по мере продвижения от центра спирали. Рис. 3.10. Спиральная модель ЖЦ ПО Эти действия пронумерованы на рис. 3.10 и имеют следующее со- держание: 1 — начальный сбор требований и планирование проекта; 2 - та же работа, но на основе рекомендаций заказчика; ПО
3 — анализ риска на основе начальных требований; 4 — анализ риска на основе реакции заказчика; 5 — переход к комплексной системе; 6 — начальный макет системы; 7 — следующий уровень макета; 8 — сконструированная система; 9 — оценивание заказчиком. Достоинства спиральной модели: 1) наиболее реально (в виде эволюции) отображает разработку про- граммного обеспечения; 2) позволяет явно учитывать риск на каждом витке эволюции разра- ботки; 3) включает шаг системного подхода в итерационную структуру раз- работки; 4) использует моделирование для уменьшения риска и совершенст- вования программного изделия. Недостатки спиральной модели: 1) сравнительная новизна (отсутствует достаточная статистика эф- фективности модели); 2) повышенные требования к заказчику; 3) трудности контроля и управления временем разработки. Модель спирального процесса разработки является наиболее рас- пространенной в настоящее время. Самыми известными ее вариантами являются RUP (Rational Unified Process) от фирмы Rational и MSF (Microsoft Solution Framework). В качестве языка моделирования ис- пользуется язык UML. Создание системы предполагается проводить итерационно, двигаясь по спирали, проходя через одни и те же стадии и на каждом витке уточняя характеристики будущего продукта. Каза- лось бы, теперь все хорошо: планируется только то, что можно предви- деть, разрабатывается то, что запланировано, и пользователи начинают знакомиться с продуктом заранее, имея возможность внести необходи- мые коррективы [25]. Однако для этого нужны большие средства. Действительно, если раньше можно было создавать и распускать группы специалистов по ме- ре необходимости, то теперь все они должны постоянно участвовать в проекте. Более того, усилия различных групп должны быть синхрони- зированы, чтобы своевременно отражать проектные решения и вносить необходимые изменения. 3.7.7. Рациональный унифицированный процесс Рациональный унифицированный процесс (Rational Unified Process, RUP) — одна из лучших методологий разработки ПО. Основываясь на опыте многих успешных программных проектов, RUP позволяет созда- вать сложные программные системы, основываясь на индустриальных методах разработки [10]. Предпосылки для разработки RUP зародились вначале 1980-х гг. в Rational Software Corporation. Вначале 2003 г. Ill
Rational приобрела IBM. Одним из основных столпов, на которые опи- рается RUP, является процесс создания моделей при помощи унифици- рованного языка моделирования (UML). RUP — одна из спиральных методологий разработки программного обеспечения. Методология поддерживается и развивается компанией Rational Software. В качестве языка моделирования в общей базе знаний используется язык UML. Итерационная и инкрементная разработка про- граммного обеспечения в RUP предполагает разделение проекта на не- сколько проектов, которые выполняются последовательно, и каждая итерация разработки четко определена набором целей, которые должны быть достигнуты в конце итерации. Конечная итерация предполагает, что набор целей итерации должен в точности совпадать с набором це- лей, указанных заказчиком продукта, т.е. все требования должны быть выполнены. Процесс предполагает эволюционирование моделей; итерация цикла разработки однозначно соответствует определенной версии модели ПО. Каждая из итераций содержит элементы управления жизненным цик- лом программного обеспечения: анализ и дизайн (моделирование), реа- лизацию, интегрирование, тестирование, внедрение. В этом смысле RUP является реализацией спиральной модели, хотя довольно часто изобра- жается в виде графика-таблицы (рис. 3.11). На данном рисунке представлены два измерения: горизонтальная ось представляет время и показывает временные аспекты жизненного цикла процесса; вертикальная ось представляет дисциплины, которые опреде- ляют физическую структуру процесса. На рис. 3.11 видно, как с течени- ем времени изменяются акценты в проекте. Например, в ранних итера- циях больше времени отводится требованиям; в поздних итерациях больше времени отводится реализации. Горизонтальная ось сформиро- вана из временных отрезков — итераций, каждая из которых является са- мостоятельным циклом разработки, цель которого - принести в конеч- ный продукт некоторую заранее определенную осязаемую доработку, полезную с точки зрения заинтересованных лиц. По оси времени жизненный цикл делится на четыре основные фазы. 1. Начало (inception) — формирование концепции проекта, понима- ние, что мы создаем, представление о продукте (vision), разработка биз- нес-плана (business case), подготовка прототипа программы или частич- ного решения. Это фаза сбора информации и анализа требований, опре- деление образа проекта в целом. Цель - получить поддержку и финан- сирование. В конечной итерации результат этого этапа — техническое задание. 2. Проектирование, разработка (elaboration) - уточнение плана, пони- мание, как мы это создаем, проектирование, планирование необходи- мых действий и ресурсов, детализация особенностей. Завершается этап исполняемой архитектурой, когда все архитектурные решения приняты и риски учтены. Исполняемая архитектура представляет собой работаю- щее программное обеспечение, которое демонстрирует реализацию ос- 112
новных архитектурных решений. В конечной итерации это технический проект. 3. Реализация, создание системы (construction)- этап расширения функциональности системы, заложенной в архитектуре. В конечной итерации это рабочий проект. 4. Внедрение, развертывание (transition) - создание конечной версии продукта, фаза внедрения продукта, поставка продукта конкретному пользователю (тиражирование, доставка и обучение). Рис. 3.11. Рациональный унифицированный процесс Вертикальная ось состоит из дисциплин, каждая из которых может быть более детально расписана с точки зрения выполняемых задач, от- ветственных за них ролей, продуктов, которые подаются задачам на вход и выпускаются в ходе их выполнения, и т.д. По этой оси распола- гаются ключевые дисциплины жизненного цикла RUP (которые часто на русском языке называют процессами, хотя это не совсем верно с точ- ки зрения данной методологии), поддерживаемые инструментальными средствами IBM (и/или третьих фирм). 1. Бизнес-анализ и моделирование (business modeling) обеспечивает реализацию принципов моделирования с целью изучения бизнеса орга- низации и накопления знаний о нем, оптимизации бизнес-процессов и принятия решения об их частичной или полной автоматизации. 2. Управление требованиями (requirements) посвящено получению информации от заинтересованных лиц и ее преобразованию в набор тре- 113
бований, определяющих содержание разрабатываемой системы и под- робно описывающих ожидания от того, что система должна делать. 3. Анализ и проектирование (analysis and design) охватывает проце- дуры преобразования требований в промежуточные описания (модели), представляющие, как эти требования должны быть реализованы. 4. Реализация (implementation) охватывает разработку кода, тестиро- вание на уровне разработчиков и интеграцию компонентов, подсистем и всей системы в соответствии с установленными спецификациями. 5. Тестирование (test) посвящено оценке качества создаваемого про- дукта. 6. Развертывание (deployment) охватывает операции, имеющие место при передаче продуктов заказчикам и обеспечении доступности продук- та конечным пользователям. 7. Конфигурационное управление и управление изменениями (configuration management) посвящено синхронизации промежуточных и конечных продуктов, управлению их развитием в ходе проекта и по- иском скрытых проблем. 8. Управление проектом (management) посвящено планированию проекта, управлению рисками, контролю хода его выполнения и непре- рывной оценке ключевых показателей. 9. Управление средой (environment) включает в себя элементы фор- мирования среды разработки информационной системы и поддержки проектной деятельности. В зависимости от специфики проекта могут быть использованы лю- бые средства IBM Rational, а также третьих фирм. В RUP рекомендова- но следовать шести практикам, позволяющим успешно разрабатывать проект: итеративной разработке; управлению требованиями; использо- ванию модульных архитектур; визуальному моделированию; проверке качества; отслеживанию изменений. Неотъемлемую часть RUP составляют артефакты (artefact), преце- денты (precedent) и роли (role). Артефакты — это некоторые продукты проекта, порождаемые или используемые в нем при работе над оконча- тельным продуктом. Прецеденты — это последовательности действий, выполняемых системой для получения наблюдаемого результата. Фак- тически любой результат работы индивидуума или группы является ар- тефактом, будь то документ анализа, элемент модели, файл кода, тесто- вый скрипт, описание ошибки и т.п. За создание того или иного вида ар- тефактов отвечают определенные специалисты. Таким образом, RUP четко определяет обязанности - роли — каждого члена группы разработ- ки на том или ином этапе, т.е. когда и кто должен создать тот или иной артефакт. Весь процесс разработки программной системы рассматрива- ется в RUP как процесс создания артефактов — начиная с первоначаль- ных документов анализа и заканчивая исполняемыми модулями, руко- водствами пользователя и т.п. Для компьютерной поддержки процессов создания программных систем в IBM разработан широкий набор инструментальных средств. Продукты и сервисы IBM Rational охватывают все стадии жизненного 114
цикла проектов — от проектирования до развертывания, а в их основе ле- жит многолетний опыт создания решений, способствующих эффектив- ной совместной работе групп разработчиков. Перечислим некоторые из них [8]. 1. Rational Rose — CASE-средство визуального моделирования ин- формационных систем, имеющее возможности генерирования элемен- тов кода. Специальная редакция продукта — Rational Rose RealTime — по- зволяет на выходе получить исполняемый модуль. 2. Rational Requisite Pro — средство управления требованиями, по- зволяющее создавать, структурировать, устанавливать приоритеты, от- слеживать, контролировать изменения требований, возникающие на лю- бом этапе разработки компонентов приложения. 3. Rational ClearQuest - продукт для управления изменениями и от- слеживания дефектов в проекте (bug tracking), тесно интегрирующийся со средствами тестирования и управления требованиями и представляю- щий собой единую среду для связывания всех ошибок и документов ме- жду собой. 4. Rational SoDA— продукт для автоматического генерирования проектной документации, позволяющий установить корпоративный стандарт на внутрифирменные документы. Возможно также приведение документации к уже существующим стандартам (ISO, СММ). 5. Rational Purify, Rational Quantify Rational PureCoverage - средст- ва тестирования и отладки. 6. Rational Visual Quantify — средство измерения характеристик для разработчиков приложений и компонентов, программирующих на C/C++, Visual Basic и Java; помогает определять и устранять узкие места в производительности ПО. 7. Rational Visual PureCoverage автоматически определяет области кода, которые не подвергаются тестированию. 8. Rational ClcarCase - продукт для управления конфигурацией программ (Rational’s Software Configuration Management, SCM), позво- ляющий производить версионный контроль всех документов проекта. С его помощью можно поддерживать несколько версий проектов одно- временно, быстро переключаясь между ними. Rational Requisite Pro под- держивает обновления и отслеживает изменения в требованиях для группы разработчиков. 9. SQA TeamTest — средство автоматизации тестирования. 10. Rational TestManager— система управления тестированием, ко- торая объединяет все связанные с тестированием инструментальные средства, артефакты, сценарии и данные. 11. Rational Robot - инструмент для создания, модификации и авто- матического запуска тестов. 12. SiteLoad, SiteCheck— средства тестирования веб-сайтов на про- изводительность и наличие неработающих ссылок. 13. Rational Performance Stud io — измерение и предсказание характе- ристик производительности систем. 115
Этот набор продуктов постоянно совершенствуется и пополняется. Так, например, недавний продукт IBM Rational Software Architect (RSA) является частью IBM Software Development Platform - набора инстру- ментов, поддерживающих жизненный цикл разработки программных систем. Продукт IBM Rational Software Architect предназначен для по- строения моделей разрабатываемых программных систем с использова- нием унифицированного языка моделирования UML 2.0, прежде всего моделей архитектуры разрабатываемого приложения. Тем не менее RSA объединяет в себе функции таких программных продуктов, как Rational Application Developer, Rational Web Developer и Rational Software Modeler, тем самым предоставляя возможность архитекторам и аналити- кам создавать различные представления разрабатываемой информаци- онной системы с использованием языка UML 2.0, а разработчикам - вы- полнять разработку J2EE, XML, веб-сервисов и т.д. Следуя принципам RUP, Rational Software Architect позволяет созда- вать необходимые модели в рамках рабочих процессов таких дисцип- лин, как: • бизнес-анализ и моделирование (business modeling); • управление требованиями (requirements); • анализ и проектирование (analysis and design); • реализация (implementation). Кроме того, Rational Software Architect поддерживает технологию разработки, управляемой моделями (model-driven development, MDD), позволяющей моделировать программное обеспечение на различных уровнях абстракции с возможностью трассируемости. Последняя версия RSA, 8.0, представляет собой основной релиз семейства продуктов IBM® Rational® Software Architect. Программный пакет RSA был соб- ран заново в виде продукта базового уровня, который можно расширять в соответствии с необходимыми предметно-ориентированным и возмож- ностями. Кроме того, для расширения поддерживаемых технологий, по- вышения продуктивности и простоты использования существенно об- новлен набор базовых функциональных возможностей [28]. IBM® Rational® Software Modeler и IBM Rational Software Architect Standard Edition теперь объединены в один основной продукт - Rational Software Architect. Он предоставляет нотацию BPMN 2 (Business Process Modeling Notation), моделирование на UML 2, визуализацию кода и со- гласованную поддержку моделирования для Java™, C# и VB.NET (Mi- crosoft® Visual Basic®.NET). Эту базовую платформу можно наращи- вать при помощи набора расширений, которые обеспечивают дополни- тельные возможности - от совместной работы и имитационного моде- лирования до моделирования развертывания и использования интегри- рованных архитектурных сред. При помощи расширения Simulation в RSA 8.0 можно имитировать любое UML-поведение (диаграммы деятельности, диаграммы последо- вательности, диаграммы коммуникации или диаграммы состояний). Можно пройти по поведению так, как будто вы пошагово проходите по коду, при этом ваша текущая позиция будет подсвечиваться на диаграм- 116
ме поведения, а также, возможно, на диаграмме композитной структуры или топологии. Это дает ряд преимуществ: • лучшее понимание поведения системы на более ранних этапах, дающее возможность устранить потенциальные дефекты поведения; • понимание того, как повлияет на статическую структуру модели аннотирование диаграммы композитной структуры; • учет влияния поведения на топологию развертывания, а также по- нимание потенциального воздействия имеющейся инфраструктуры на поведение приложения. 3.7.8. Scram-методология В большинстве случаев программирование — слабо определенный процесс, требующий творческого подхода. Scrum — одна из первых ме- тодологий циклического наращивания функциональности и корректи- ровки хода проекта на основе анализа обратной связи. Термин Scrum был впервые озвучен в работе X. Такеучи и И. Нона- ка, опубликованной в Harvard Business Review, где авторы отметили ус- пешность проектов, использующих небольшие команды без жесткой специализации. Такие команды чем-то напоминают конструкцию схват- ки (scrum) в регби, которая назначается при нарушении правил или ос- тановке игры. Д. Сазерленд использовал эту работу при создании мето- дологии для корпорации Easel в 1993 г., которую по аналогии и назвал Scrum, а К. Швабер формализовал процесс для использования в индуст- рии разработки программного обеспечения, представив в 1995 г. работу на конференции Object-Oriented Programming Systems, Lang u ages & App- lications [5]. Методология Scrum устанавливает правила управления процессом разработки и позволяет использовать уже существующие практики ко- дирования, корректируя требования или внося тактические изменения. Использование этой методологии даст возможность выявлять и устра- нять отклонения от желаемого результата на более ранних этапах разра- ботки программного продукта. Основа Scrum — итеративная разработка. Scrum определяет правила, по которым должен планироваться и управляться список требований к продукту для достижения максимальной прибыльности от реализован- ной функциональности: • правила планирования итераций для максимальной заинтересован- ности команды в результате; • основные правила взаимодействия участников команды для мак- симально быстрой реакции на существующую ситуацию; • правила анализа и корректировки процесса разработки для совер- шенствования взаимодействия внутри команды. Каждую итерацию можно представить следующим образом: плани- руем — фиксируем — реализуем — анализируем. За счет фиксирования требований на время одной итерации и изменения длины итерации мож- но управлять балансом между гибкостью и планируемостью разработки. 117
Scrum — простой каркас, который можно использовать для организа- ции команды и достижения результата более продуктивного и с более высоким качеством за счет анализа сделанной работы и корректировки направления развития между итерациями. Методология позволяет ко- манде выбрать задачи, которые должны быть выполнены, учитывая биз- нес-приоритеты и технические возможности, а также решить, как их эф- фективно реализовать. Это позволяет создать условия, при которых ко- манда работает с удовольствием и максимально продуктивно. Напри- мер, возможность самостоятельного выбора объема и пути решения за- дач (без внешнего давления) позволяет всем участникам команды по- чувствовать себя активными игроками, вовлеченными в процесс, а не простыми исполнителями, от которых требуется лишь четкая реализа- ция поручений. Scrum фокусируется на определении приоритетных задач, основыва- ясь на бизнес-целях, что увеличивает полезность и доходность проекта на его ранних стадиях. Так как при инициации проекта его доходность определить почти невозможно, Scrum предлагает концентрироваться на качестве разработки и к концу каждой итерации иметь промежуточный продукт, который можно использовать, даже пусть с минимальными возможностями. Например, результатом итерации может быть каркас сайта, который можно показать на презентации. Методология Scrum ориентирована на то, чтобы оперативно приспо- сабливаться к изменениям в требованиях, что позволяет команде быстро адаптировать продукт к нуждам заказчика. Такая адаптация достигается за счет получения обратной связи по результатам итерации: имея после каждой итерации продукт, который уже можно использовать, показы- вать и обсуждать, легче собирать информацию, делать правильные кор- ректировки и изменять приоритеты требований. Например, если каркас сайта показать потенциальным пользователям, то появится много во- просов, на основании которых можно скорректировать то, что уже напи- сано или еще не реализовано, понять, что более важно пользователю. Девиз Scrum: анализируй то, что получил, адаптируй то, что есть, к реальной ситуации, а потом анализируй снова. Чем меньше формализ- ма, тем более гибко и эффективно можно работать — это основной прин- цип данной методологии. Но это не означает, что формальных процес- сов не должно быть совсем; их должно быть достаточно для организа- ции эффективного взаимодействия и управления проектом. Формальная часть Scrum состоит из трех ролей, трех практик и трех основных доку- ментов. К ролям относятся (лучше сказать, роли выполняют) владелец про- дукта, Scrum-мастер и Scrum-ко манда. Владелец продукта - человек, поставляющий требования програм- мистам. От того, как четко написаны требования, зависит, насколько часто команде придется переключаться с задачи на задачу в связи с от- сутствием нужной информации, как много нужно будет задавать вопро- сов, на которые уходит дополнительное время, как сильно придется из- менять уже написанную функциональность от итерации к итерации 118
и, соответственно, эффективность разработки в целом. Обычно владе- лец продукта является представителем или доверенным лицом заказчи- ка, а для компаний, выпускающих коробочные продукты, он представ- ляет рынок, на котором реализуется продукт. Владелец продукта должен составить бизнес-план, показывающий ожидаемую доходность, и план развития с требованиями, отсортирован- ными по коэффициенту окупаемости инвестиций. Исходя из имеющейся информации, владелец продукта подготавливает список требований, от- сортированный по значимости. Чем лучше владелец продукта описыва- ет требования, управляет приоритетами и чем быстрее выдает информа- цию, тем больший финансовый эффект получит компания от методоло- гии. В обязанности этого сотрудника входят своевременное предостав- ление требований к продукту, определение дат и содержания релизов, эффективное управление приоритетами и корректировка требований для достижения максимальной окупаемости инвестиций в продукт. От человека, исполняющего роль Scrum-мастера, во многом зависит самостоятельность, инициативность программистов, удовлетворенность сделанной работой, атмосфера в команде и результат всей работы. Этот человек должен быть одним из членов команды разработки и участво- вать в проекте как разработчик. Он отвечает за своевременное решение текущих проблем — от ремонта сломанного стула до обеспечения необ- ходимой информацией членов команды для продолжения их работы и загруженности, за поддержание нужных технических практик, исполь- зуемых на проекте. В обязанности Scrum-мастера входит обеспечение максимальной работоспособности и продуктивности команды, четкого взаимодействия между всеми участниками проекта, своевременное реше- ние всех проблем, тормозящих или останавливающих работу любого чле- на команды, ограждение команды от всех воздействий извне во время итерации и обеспечение следования процессу всех участников проекта. Scrum-команда - это группа, состоящая из пяти-девяти самостоя- тельных, инициативных программистов. Первая задача этой команды — поставить реально достижимую, прогнозируемую, интересную и значи- мую цель для итерации. Вторая задача — сделать все для того, чтобы эта цель была достигнута в отведенные сроки и с заявленным качеством. Цель итерации считается достигнутой только в том случае, если все по- ставленные задачи реализованы, весь код написан по определенным проектом «стандартам кодирования», программа протестирована полно- стью, а все найденные дефекты устранены. В начале проекта владелец продукта готовит журнал продукта — спи- сок требований, отсортированный по значимости, а Scrum-команда до- полняет этот журнал оценками стоимости реализации требований. Спи- сок должен включать функциональные и технические требования, необ- ходимые для реализации продукта. Самые приоритетные из них должны быть достаточно детально прописаны, чтобы их можно было оценить и протестировать. О своевременной детализации требований должен за- ботиться владелец продукта, предоставляя необходимый объем в нуж- ное время. В этом смысле программисты являются заказчиками требо- 119
ваний для владельца продукта. И отношение владельца продукта долж- но быть соответствующее: каковы требования, такова и их реализация. В дальнейшем остальные требования должны постепенно уточняться и детализироваться до такого же уровня. Главное, чтобы у команды все- гда был достаточный объем подготовленных к реализации требований. После того как команда во время сессии планирования выбрала и обязалась реализовать набор требований из журнала продукта, эти требования разбиваются на более мелкие задачи, составляющие детали- зированный список требований — журнал спринта. Разбивка на задачи должна быть сделана таким образом, чтобы выполнение одной задачи занимало не больше двух дней. Разбивка на задачи поможет так спланировать итерацию, чтобы в конце не осталось ни одной невыполненной задачи. После завершения детализации оценка журнала спринта сравнивается с первичной оцен- кой в журнале продукта. Если существует значительное расхождение, команда договаривается с владельцем продукта об объеме работ, кото- рый должен быть выполнен в течение итерации, и о том, какой объем будет перенесен на следующую. Менее важные и мало влияющие на цель итерации задачи выносятся из журнала спринта. Подготовка к первой итерации, называемой спринт (sprint), начина- ется после того, как владелец продукта разработал план проекта, опре- делил требования и отсортировал их в количестве, достаточном для на- полнения одной итерации. Такой список требований называется журна- лом продукта (product backlog). При планировании итерации происхо- дит детальная разработка сессий планирования спринта (sprint planning meeting), который начинается с того, что владелец продукта, Scrum-ко- манда и Scrum-мастер проверяют план развития продукта, план релизов и список требований. Scrum-команда проверяет оценки требований, убе- ждается, что они достаточно точны, чтобы начать работать, решает, ка- кой объем работы она может успешно выполнить за спринт, основыва- ясь на размере команды, доступном времени и производительности. Важно, чтобы Scrum-команда выбирала первые по приоритету тре- бования из журнала продукта. После того как Scrum-команда обязуется реализовать выбранные требования, Scrum-мастер начинает планирова- ние спринта. Scrum-команда разбивает выбранные требования на зада- чи, необходимые для его реализации. Эта активность в идеале не долж- на занимать больше четырех часов, и ее результатом служит список тре- бований, разбитый на задачи, — журнал спринта (sprint backlog). Необхо- димо, чтобы все участники команды приняли на себя обязательство по реализации выбранной цели. После окончания планирования начинается итерация. Каждый день Scrum-мастер проводит скрам (daily scrum meeting) - 15-минутное сове- щание, цель которого — достичь понимания того, что произошло со вре- мени предыдущего совещания, скорректировать рабочий план в соот- ветствии реалиями сегодняшнего дня и обозначить пути решения с у ще- 120
ствующих проблем. Каждый участник Scrum-ко манды отвечает на три вопроса: что я сделал со времени предыдущего скрама, что меня тормо- зит или останавливает в работе, что я буду делать до следующего скра- ма? В этом совещании может принимать участие любое заинтересован- ное лицо, но только участники Scrum-команды имеют право принимать решения. Правило обосновано тем, что они давали обязательство реали- зовать цель итерации, и только это дает уверенность в том, что она бу- дет достигнута. На них лежит ответственность за их собственные слова, и если кто-то со стороны вмешивается и принимает решения за них, тем самым он снимает ответственность за результат с участников команды. В конце каждого спринта проводится демонстрационная презента- ция (sprint review meeting) продолжительностью не более четырех часов. Сначала Scrum-команда демонстрирует владельцу продукта сделанную в течение спринта работу, а тот затем продолжает презентацию и может пригласить к участию всех заинтересованных заказчиков. Владелец про- дукта определяет, какие требования из журнала спринта были выполне- ны, и обсуждает с командой и заказчиками, как лучше расставить при- оритеты в журнале продукта для следующей итерации. Во второй части презентации производится анализ прошедшего спринта, который ведет Scrum-мастер. Scrum-команда ищет использо- ванные в последнем спринте положительные и отрицательные способы работы, анализирует их, делает выводы и принимает важные для даль- нейшей работы решения. Scrum-команда также определяет программы, которые могут работать лучше, и ищет пути для увеличения эффектив- ности дальнейшей работы. Затем цикл замыкается, и начинается плани- рование следующего спринта (рис. 3.12). График спринта (burndown chart) показывает ежедневное изменение общего объема работ, оставшегося до окончания итерации. Это график позволяет команде разработчиков делать анализ текущей ситуации и своевременно реагировать на отклонения. График спринта также по- зволяет владельцу продукта наблюдать за ходом итерации. Если общий объем работ не уменьшается каждый день, значит, что-то идет не так. Во время сессии планирования команда находит и оценивает задачи, ко- торые надо выполнить для успешного завершения итерации. Сумма оценок всех задач в журнале спринта является общим объе- мом работы, который надо выполнить за итерацию. После завершения каждой задачи Scrum-мастер пересчитывает объем оставшейся работы и отмечает это на графике спринта. Только в том случае, если объем ра- бот по окончании итерации закончился (в журнале спринта не осталось незавершенных задач), итерация считается успешной. График спринта используется как вспомогательный инструмент, позволяющий коррек- тировать работу для завершения итерации вовремя, с работающим ко- дом и требуемым качеством. 121
Рис. 3.12. Структура спринта Время между итерациями - это время принятия основополагающих решений, влияющих на ход всего проекта. Во время итерации никакие изменения извне не могут быть сделаны. После того как команда дала обязательство реализовать журнал спринта, он фиксируется, и измене- ния в нем могут быть сделаны только по следующим причинам: • Scrum-команда в течение итерации получила лучшее представле- ние о требованиях и нуждается в дополнительных задачах для успешно- го завершения итерации; • найдены дефекты, которые нужно обязательно исправить для ус- пешного завершения итерации; • Scrum-мастер и Scrum-команда могут решить, что небольшие из- менения, не влияющие на общий объем работ, могут быть реализованы в связи с возникшей у владельца продукта необходимостью. Исходя из того, что журнал спринта не может быть изменен извне во время итерации, нужно выбирать ее длину, основываясь на стабильно- сти требований. Если требования стабильны, меняются или дополняют- ся редко, то можно выбрать шестинедельный цикл. В этом случае эко- номится время на переключение команды с активной разработки на пла- нирование и демонстрационные митинги. Если требования часто меня- ются и дополняются, нужно отталкиваться от двухнедельного цикла; в любом случае длина итерации — это величина экспериментальная. 3.7.9. Agile-методологии Гибкие методологии построены таким образом, что изменения при- ветствуются, а неопределенность признается. Существует несколько от- личительных признаков всех гибких методологий [27]. 1 . Разработка ведется короткими итерациями. Чем короче итера- ция, тем чаще можно демонстрировать продукт заказчику и изменять направление развития продукта. Заказчик сможет руководить разработ- кой продукта, вместо того чтобы, дождавшись окончания разработки, понять, что получилось совсем не то, что он хотел. 2 . Инкрементальная разработка. За каждую итерацию продукт до- полняется новыми функциями, оставаясь при этом полностью функцио- нальным и готовым к передаче заказчику. 122
3 . Самоорганизующаяся команда. Как показывает практика, только самоорганизующиеся команды способны гибко реагировать на измене- ния. Дело в том, что короткие итерации и часто меняющиеся требования приводят к тому, что поддерживать документацию по проекту в полном объеме технически невозможно. В этом случае команда тратила бы на работу с требованиями больше времени, чем на разработку кода. А раз документации мало, членам команды приходится чаще общаться лицом к лицу, решая повседневные проектные задачи. Следовательно, команда должна быть самоорганизующейся, чтобы справиться с потоком про- блем. 4 . Адаптирующийся процесс. В традиционном подходе к разработ- ке процесс определяется на уровне организации, а на уровне проекта происходит его подгонка. На практике, как правило, это означает, что менеджер проекта принимает решение, какие из стандартных регла- ментных требований организации неприменимы к проекту, и формули- рует свое решение в соответствующем документе. В Agile-методологи- ях процесс определяется по ходу проекта самой проектной командой. Одной из самых ранних работ, повлекших за собой появление совре- менных методов итеративной разработки, является опубликованная в 1985 г. статья Б. Боэма «Спиральная модель разработки программного обеспечения». В начале 90-х гг. XX в. Дж. Сазерленд и К. Швабср изобрели и нача- ли использовать в компании Easel Corp, методологию, которую они на- звали Scrum (см. предыдущий раздел). Метод был основан на подходе, который применялся в таких несофтверных компаниях, как Honda, Canon и Fujitsu. Для Scrum характерны 30-дневные итерации, называе- мые спринтами, а также то, что повышенное внимание должно уделять- ся практикам по самоорганизации команд. Первая работа по Scrum была опубликована в 1999 г. [27]. В середине 90-х гг. XX в. компания Rational начала разработку мето- дологии Rational Unified Process (унифицированный процесс Rational), в основу которой были положены итеративность и инкрементность раз- работки (см. раздел 3.7.6). Процесс основан на применении прецедентов использования (Use Cases) и включает набор некоторых «лучших прак- тик» софтверной разработки на все случаи жизни. В 1994-м группа людей, использовавших Rapid Application Development, собралась, чтобы обсудить описание стандартного итера- тивного процесса. Через некоторое время это описание трансформиро- валось в методологию DSDM (Dynamic Systems Development Method), которая в наши дни особенно популярна на ее родине — в Великобрита- нии. В 1996 г. к проекту Chrysler СЗ, который к тому времени уже был практически провален, присоединился К. Бек. Он вместе с Р. Джеффри- сом использовал все известные ему на тот момент практики и пришел к выводу, что их совместное применение намного превышает эффект, получаемый от каждой по отдельности. Методология, названная Extreme Programming (ХР - экстремальное программирование), быстро 123
получила широкую известность благодаря ориентации на простоту, коммуникацию и раннее тестирование, а также из-за провокационного названия. В 1997 г. П. Коад и Дж. Люка подключились к безнадежному проек- ту по построению большой логистической системы и успешно справи- лись с ним за счет применения практики итеративной разработки. Они описали свой подход к разработке в методологии Feature Driven Development (FDD). В феврале 2001 г. 17 разработчиков методологий, авторов DSDM, ХР, Scrum, FDD и др., собрались для того, чтобы попытаться обнаружить что- нибудь общее в своих подходах. Они сформировали группу под названи- ем Agile Alliance. Слово agile («быстрый, ловкий, стремительный») отра- жало в целом их подход к разработке ПО, основанный на богатом опыте участия в разнообразных проектах в течение многих лет. Результатом ра- боты группы стал Манифест гибкой разработки (Agile Manifesto; http://agilcmanifesto.org). Тогда же появился термин Agile (т.е. «гибкий, шустрый»), объединяющий все методологии под одной крышей. Этот подход под названием «Быстрая разработка ПО» (Agile software development) базируется на четырех идеях, сформулированных ими в документе «Манифест быстрой разработки» (Agile Alliance's Manifesto) и заключающихся в следующем [7]: • индивидуумы и взаимодействия между ними ценятся выше про- цессов и инструментов; • работающее ПО ценится выше всеобъемлющей документации; • сотрудничество с заказчиками ценится выше формальных догово- ров; • реагирование на изменения ценится выше строгого следования плану. Принципы, которые разъясняет Agile Manifesto [11]: • удовлетворение клиента за счет ранней и бесперебойной поставки ценного ПО; • приветствие изменений требований, даже в конце разработки (это может повысить конкурентоспособность полученного продукта); • частая поставка рабочего ПО (каждый месяц, неделю или еще ча- ще); • тесное, ежедневное общение заказчика с разработчиками на протя- жении всего проекта; • проектом занимаются мотивированные личности, которые обеспе- чены нужными условиями работы, поддержкой и доверием; • рекомендуемый метод передачи информации — личный разговор (лицом к лицу); • работающее ПО — лучший измеритель прогресса; • спонсоры, разработчики и пользователи должны иметь возмож- ность поддерживать постоянный темп на неопределенный срок; • постоянное внимание, направленное на улучшение технического мастерства и удобный дизайн; 124
• простота — искусство не делать лишней работы; • лучшие технические требования» дизайн и архитектура получают- ся у команды; • постоянная адаптация к изменяющимся обстоятельствам. При таком подходе технология занимает в процессе создания ПО вполне определенное место. Она повышает эффективность деятельно- сти разработчиков при наличии любых из следующих четырех условий: • технология позволяет людям легче выразить свои мысли; • технология выполняет задачи, невыполнимые вручную; • технология автоматизирует утомительные и подверженные ошиб- кам действия; • технология облегчает общение между людьми. При этом следует четко понимать: при всех достоинствах быстрой разработки ПО этот подход не является универсальным и применим только в проектах определенного класса. Для характеристики таких проектов А. Коберн ввел два параметра: критичность и масштаб. Кри- тичность определяется последствиями, вызываемыми дефектами в ПО, ее уровень может иметь одно из четырех значений: • С-дефекты вызывают потерю удобства; • D-дсфекты вызывают потерю возместимых средств (материальных или финансовых); • Е-дефскты вызывают потерю невозместимых средств; • L-дефскты создают угрозу человеческой жизни. Масштаб определяется количеством разработчиков, участвующих в проекте: • от 1 до 6 человек - малый масштаб; • от 6 до 20 человек — средний масштаб; • свыше 20 человек — большой масштаб. По оценке А. Коберна, быстрая разработка ПО применима только в проектах малого и среднего масштаба с низкой критичностью (С или D). Общие принципы оценки технологий в таких проектах заключаются в следующем: • интерактивное общение лицом к лицу - это самый дешевый и бы- стрый способ обмена информацией; • избыточная «тяжесть» технологии стоит дорого; • более многочисленные команды требуют более «тяжелых» и фор- мальных технологий; • большая формальность подходит для проектов с большей критич- ностью; • возрастание обратной связи и коммуникации сокращает потреб- ность в промежуточных и конечных продуктах; • дисциплина, умение и понимание противостоят процессу, фор- мальности и документированию; • потеря эффективности в некритических видах деятельности впол- не допустима. 125
Существуют методологии, которые придерживаются ценностей и принципов, заявленных в Agile Manifesto. Вот некоторые из них: Agile Modeling, Agile Unified Process, Agile Data Method, DSDM, Essential Uni- fied Process, (Extreme Programming, XP - экстремальное программирова- ние), Feature Driven Development и др. В некоторых статьях проводится сравнительное сопоставление этих методологий по большому диапазону критериев: жизненный цикл, роли, практики, метрики и т.п. В частности, в [21] предложена следующая формула для сравнения методологий: Ценности + Принципы + Практи- ки. Ценности - это набор убеждений, которые управляют разработкой и принятием решений и которые важны и значимы для каждого участ- ника проекта. Для многих очень сложно изменить свои убеждения. Если команда принимает эти ценности, у нее перестраиваются командное по- ведение, принципы работы и даже привычки. Убеждения определяют стиль мышления и способы взаимодействия в команде. Принципы - мост между ценностями и практиками, несут разъяс- няющий характер ценностям. Принципы в общей форме раскрывают со- держание той или иной ценности, выражают выработанные в успешных проектах требования, касающиеся организации проекта, структуры ко- манды и др. Практики - набор активностей, соблюдая которые можно повы- шать вероятность достижения успеха. Набор практик может меняться от команды к команде согласно ситуации в текущем проекте, но ценности и принципы остаются неизменными. Например, может быть одобрена практика парного программирования в одном проекте, а в другом она может оказаться бесполезной. Какой же расклад дают методологии, по мнению Д. Миллера, автора статьи [21]? Agile Manifesto = 4 + 12 + 0; Extreme Programming = 5+14 +24; Scrum = 5 + 5 + 7; Agile Modeling = 5 + 13 + 21; Lean = 0 + 7 + 7; Feature Driven Development = 0+8+16; Adaptive Software Development = 0 + 6 + 18; Kanban = 0 + 6 + 3; RUP = 0 + 7+ 120. Команда, успешно применяющая практики гибкой разработки, про- ходит в своем развитии следующие стадии: • рабочей группы, руководимой менеджером; • псевдокоманды; • потенциальной команды; • продуктивной команды. Из приведенного на рис. 3.13 графика видно, что производитель- ность команды на пути к действительно высоким значениям снижается. В чем причина? Это объясняется тем, что команда не может мгновенно 126
стать высокопродуктивной, а обязательно проходит несколько последо- вательных фаз развития [27]. 1. Forming — члены команды начинают осторожно присматриваться друг к другу, они учатся работать друг с другом и пытаются понять свою роль в команде. 2. Storming — по окончании разведки члены команды начинают сра- жаться за власть и контроль на проектом, практически никогда не согла- шаются друг с другом, выражают недоверие и предубеждение. На этом этапе несколько лидеров формируют вокруг себя альянсы. Это самая тя- желая и, к сожалению, неизбежная фаза формирования команды; здесь очень важно научиться правильно управлять конфликтами. 3. Norming— борьба постепенно стихает, члены команды теперь знают друг друга достаточно хорошо и начинают понемногу доверять друг другу. На совместных обсуждениях они способны достигать кон- сенсуса и принимать командные решения. 4. Performing — команда самоуправляема и наконец может отвлечься от выяснения отношений и полностью посвятить себя проекту. Члены команды полностью доверяют друг другу и чувствуют взаимную ответ- ственность. Команда начинает действовать как один человек: она может давать обещания и выполнять их, а также способна принимать решения, руководствуясь стратегическими соображениями. Сколько же времени может занять создание высокопроизводитель- ной команды? Конечно, это зависит от множества факторов, в том числе и от состава команды. По мнению А. Уразбасва, автора статьи [27], про- цесс формирования команды занимает от трех до пяти итераций незави- симо от их продолжительности. Ключевые роли в достижении командой самоуправляемости в Agile играют функции менеджера проекта и кол- ла бор ативные практики. Рис. 3.13. Фазы развития команды В Agile само понятие «менеджер проекта» трансформируется: он должен не раздавать задачи и контролировать их исполнение, а как можно быстрее превратить команду из группы разобщенных личностей в сплоченный коллектив. 127
Менеджер проекта отвечает за создание атмосферы доверия, налажи- вает коммуникации внутри команды, устраняет внешние препятствия. Роль менеджера проекта особенно важна на первых, самых опасных фа- зах формирования команды. Он должен участвовать во всех проектных совещаниях, помогать команде ставить перед собой цели и последова- тельно добиваться их исполнения, правильно управлять неизбежными конфликтами и напоминать команде о принятых решениях. Его роль — роль помощника общения и хозяина процесса. Он облегчает и ускоряет процесс самоорганизации, налаживая коммуникации внутри команды. Менеджер также отвечает за соблюдение практик гибкой разработки в проекте, поскольку именно отказ от них создает через некоторое вре- мя особые трудности. Менеджер проекта делает видимыми все пробле- мы и открытые вопросы — как для команды, так и для заказчика и дру- гих заинтересованных лиц, имеющих отношение к проекту. Пожалуй, самым близким аналогом в традиционном подходе является Process Engineer, разница лишь в том, что работает он на уровне проекта, а не организации. Практики гибкой разработки должны помочь членам команды в их повседневной работе, но они не могут мгновенно научить их работать по-новому. Этот процесс требует времени и приложения определенных усилий. Команда должна научиться совместно работать и общаться. Наиболее важными являются следующие колла бор ативные практики: ретроспектива (retrospective) и Scrum (stand-up meeting, летучка). Каждый раз по окончании итерации команда собирается и обсуждает полученные результаты. Все, что мешало продуктивной работе или де- лало ее неэффективной, выносится на рассмотрение. Предлагаются раз- личные способы решения проблемы, из которых выбираются лучшие. По сути, это механизм создания процесса на уровне проекта. Различие по сравнению с традиционными методологиями заключается в том, что автором процесса является сама команда, а не внешние специалисты, не разбирающиеся в проектной специфике. Кроме того, ретроспектива — это инструмент самоорганизации команды. В ходе ретроспективы ко- манда обсуждает только технические, конфигурационные или иные про- цессные проблемы - речь может зайти, например, о неготовности или неспособности отдельных членов проектной команды помогать осталь- ным. Scrum - это ежедневное 15-минутное собрание. Каждый член коман- ды рассказывает, что делал вчера, что будет делать сегодня, что ему ме- шает и чего не хватает для продуктивной работы. Это собрание нужно для синхронизации работы команды, а не для сбора статусов для после- дующего доклада руководству. Scrum - это и механизм самоорганиза- ции: каждый член команды должен знать, что происходит в проекте, с тем чтобы помочь всей команде добиваться нужных результатов. Он может высказать свое мнение по открытым вопросам или вызваться по- мочь отстающему коллеге. Кратковременность этого собрания очень важна, поэтому все вопросы, требующие длительного обсуждения, вы- носятся за его пределы. 128
Внедрение Agile-методологий может быть связано с рядом трудно- стей. По мнению автора статьи [27], это следующие проблемы. 1. Привычка к роли. Члены проектной команды поначалу неохотно соглашаются выполнять несвойственные им проектные роли, даже если понимают, что это принесет несомненную пользу проекту. Например, аналитики не любят тестировать систему, хотя именно им положено знать, как она должна работать. Проблемы такого рода легко заметны в проекте и, как правило, решаются на ретроспективах самой командой. 2. Привычка к документам. Поначалу разработчики ожидают от за- казчика документа требований по проекту, где будут разъяснены все во- просы. Но поскольку это не самый эффективный способ передачи ин- формации, разработчики должны научиться работать напрямую с заказ- чиком. Через какое-то время работы в проекте, пообщавшись напрямую с заказчиком, разработчики смогут ориентироваться в бизнесе и реше- ние части очевидных вопросов смогут брать на себя. 3. Новая команда. Настоящей проблемой для менеджера проекта яв- ляется новая команда, где рабочие отношения между людьми еще не сформировались. Люди не знают друг друга, стесняются обращаться за помощью и боятся открыто критиковать друг друга за неправильные проектные решения. Менеджер проекта должен помочь команде как можно скорее установить неформальные отношения. Очень полезными оказываются различные мероприятия по тимбилдингу, такие как совме- стные ужины или спортивные мероприятия. 4. Проблемы с общением. К сожалению, далеко не все люди по своей природе экстраверты и способны на открытое общение. Далеко не все- гда у них получается эффективно общаться друг с другом. На началь- ном этапе проекта проводить все собрания между членами проектной команды должен менеджер проекта, добиваясь продуктивности и эф- фективности. 5. Давление по срокам. Заказчик требует выполнения установленных сроков. Ему необходимо вовремя получить желаемую функциональ- ность. Задача команды состоит в том, чтобы уложиться в требуемые сроки, не принося в жертву качество продукта. В противном случае ско- рость разработки в долгосрочной перспективе упадет, так как стоимость изменений из-за низкого качества возрастет. Кроме того, плохое качест- во отрицательно влияет и на мотивацию внутри проектной команды. За- дача менеджера проекта - постоянно напоминать команде и заказчику о необходимости поддерживать высокое качество. 6. Креативность. Не все задачи в проекте одинаково интересны. Раз- работчикам часто хочется принимать проектные решения, которые идут в ущерб проекту, но оригинальны в техническом плане. Тут важно пом- нить о принципах KISS (keep it simple) и YAGN1 (you ain’t gonna need it). Проектные решения должны быть простыми. Не стоит делать то, что не является абсолютно необходимым в обозримом промежутке времени. Как же научить команду принимать простые решения? Может быть, по- лезно разрешить команде ошибиться один раз и затем на ретроспективе разобрать пример, чтобы разработчики сделали вывод на буду- 129
щее? Практически в любом проекте время от времени возникают иссле- довательские задачи (новые технологии, новые технические области знаний). Именно здесь место для всяких проб и экспериментов. 7. Оценка времени. При оценке сроков на выполнение задачи разра- ботчики часто забывают, что, помимо написания кода, в задачу как ми- нимум входят дизайн и тестирование. По этой причине в начале проекта программисты часто переоценивают свою производительность. На рет- роспективах эти ошибки отмечаются и делаются выводы на будущее. Впоследствии команда учится правильно оценивать свои возможности и со временем (через три-четыре итерации) точность установления сро- ков растет наряду с производительностью. 8. Проблема с менеджментом. Менеджмент ожидает получения оп- ределенной функциональности к определенному сроку. Но Agile-мето- дология не может гарантировать 100%-го выполнения планов. Можно лишь ожидать, что высокоприоритетные требования будут реализованы в первую очередь. Полезно согласовывать с менеджментом планы на уровне релизов. Высокоуровневость плана релиза позволяет менеджеру продукта в достаточно широких пределах варьировать объем разработ- ки даже на уровне отдельных функций системы. Например, в задачу разработки подсистемы поиска можно включить учет морфологии, а можно на нем и сэкономить. 9. Проблемы с некомандным поведением. При внедрении Agile ино- гда возникает следующая ситуация. Во время совещания вдруг кто-то вскакивает со своего места и начинает предлагать те или иные идеи. Возражения он с ходу отметает и навязывает собственное решение. Че- рез некоторое время он заявляет, что решение принято и пора перейти ко второму вопросу. Разумеется, команда никакого решения не прини- мала, его принял этот человек. Тут возможны разные варианты. Может быть, этот человек просто увлекся и через некоторое время придет в себя. Но есть люди, которые просто в силу своих человеческих качеств неспособны работать в ко- манде. Agile полагается на способность людей договариваться друг с другом и уметь решать проблемы в личном общении. Если этих ка- честв у человека нет, то можно с сожалением констатировать, что Agile ему не подходит и с ним лучше расстаться. 3.7.10. Управление жизненным циклом приложений Разработка ПО является довольно сложным предприятием. Создание программного продукта с достаточно четко определенными характери- стиками, выполненное с приемлемым качеством, в рамках отведенного бюджета и в срок, требует постоянной координации большого количест- ва действий между многочисленными специалистами. За последние 15 лет разработка программных продуктов стала полноценной индуст- рией, в ней нет места для недокументированного, сугубо индивидуаль- ного подхода, поэтому закономерно, что заметной тенденцией стало по- явление методологии управления жизненным циклом приложений [23]. 130
Безусловно, в процессе разработки программного обеспечения найдется место искусству талантливых программистов и профессиональному мастерству других участников процессов создания программного про- дукта, однако сегодня стало ключевым осознание того факта, что в этой деятельности нет места для бессвязности, недокументированности и диктата индивида. Одной из наиболее заметных тенденций первого десятилетия этого века в индустрии создания программных систем ста- ло появление ALM (Application Lifecycle Management, ALM) — управле- ния жизненным циклом приложений [23]. Такой подход должен привнести в разработку дисциплину управле- ния, рассматривая создание программного продукта как бизнес-процесс и учитывая его циклический характер. В соответствии с идеей ALM, ра- бота над любым программным решением не заканчивается на этапе его ввода в эксплуатацию: система модернизируется и совершенствуется, выходят ее новые версии, что каждый раз инициирует очередной виток жизненного цикла приложения. Аналитики Forrester Research сравнивают ALM с ERP для программ- ной индустрии. Правда, история ALM гораздо короче и не может пока похвастаться сравнимым списком успешных внедрений. Аналитики признают, что, несмотря на объективную необходимость в подобных решениях, средства ALM пока находят ограниченное применение, а их рынок по-прежнему фрагментирован. Обозреватели рынка считают, что ни одно из существующих на сегодняшний день предложений в области ALM не реализует в полной мере все потенциальные преимущества и возможности средств автоматизации управления жизненным циклом приложений. Однако развитие разработки в сторону управляемых, пред- сказуемых, эффективных процессов создания надежного и качественно- го ПО не может не сопровождаться появлением соответствующих плат- форм автоматизации этих процессов. Производители решений ALM предоставляют различные средства и технологии для поддержки процесса разработки ПО. Эти средства вы- ходят далеко за рамки традиционных инструментов для повышения производительности отдельного разработчика. Они направлены на пре- доставление методик и инструментов, ориентированных на коллектив- ную работу по созданию ПО. Чтобы создать жизнеспособное решение для ALM, производители должны учитывать потребности «расширен- ной» группы разработчиков ПО и включать в свои продукты роли, кото- рые участвуют в более широком процессе. Эксперт в области ИТ Д. Чеппел предостерегает от упрощенного взгляда на ALM, которое часто отождествляют лишь с жизненным цик- лом разработки программного обеспечения (Software Development LifeCycle, SDLC): инициацией, итеративным циклом разработки, выпус- ком релиза продукта и внедрением. Дисциплина ALM охватывает более широкий круг задач, рассматривая все аспекты существования такого корпоративного ресурса, как приложения. По определению Д. Чеппела, жизненный цикл приложения включает в себя все этапы, на которых ор- ганизация так или иначе вкладывает средства в этот ресурс — от исходной 131
идеи программного решения до утилизации отслужившего свой срок ПО [15]. Предельно детализируют это определение в HP — по версии компа- нии, цикл SDLC составляет лишь один из этапов полноценной модели ALM — этап доставки приложения (рис. 3.14), а кроме него есть еще пла- нирование, эксплуатация и вывод из эксплуатации [15]. Цикл замкнут: до момента, когда организация приходит к окончательному выводу о ненужности приложения, оно продолжает совершенствоваться. Гра- мотная реализация ALM направлена в том числе на то, чтобы продлить срок эффективной работы программного решения и, как следствие, обеспечить сокращение затрат на покупку или создание принципиально новых программных продуктов. Анализ потребностей бизнеса Приоритетность и инвестиции Упрдьпенне портфец*** .Мониторинг ПрПГрЛ мм Плакирование Руководящие решения По инти «с и Исправление ошибок Мониторинг Эхспяу- и настройка I атация Полный жизненный цикл приложения Повторное Инициация Вывод из экс плуатации недрение Рис. 3.14. Модель ALM Д. Чеппел развертывает картину жизненного цикла в линейную, вы- деляя три основные области ALM: руководство (governance), разработка (development) и эксплуатация (operations). Соответствующие этим об- ластям процессы протекают, перекрываясь, от зарождения идеи нового приложения или модернизации существующего к этапу его развертыва- ния и до полного завершения функционирования. Руководство в ALM реализуется на протяжении всего жизненного цикла приложения и включает в себя все процессы и процедуры, кото- рые относятся к принятию решений и управлению проектом. Главная задача здесь - обеспечение соответствия приложения тем или иным це- лям бизнеса, что определяет значимость этого компонента ALM. К про- цессам руководства, Д. Чеппел относит разработку детального инвести- ционного предложения (бизнес-кейс, содержащий анализ затрат, выгод и рисков, связанных с будущим приложением), которое предшествует стадии разработки; управление разработкой с помощью методов 132
и средств управления проектами и портфелями (Project Portfolio Management, PPM); управление работающим приложением в рамках управления портфелем приложений предприятия (Application Portfolio Management, АРМ). Разработка приложения происходит между моментом возникновения идеи и развертыванием готового решения. Процессы разработки реали- зуются также после развертывания при появлении необходимости в мо- дернизации приложения или выпуске новых версий. Разработка включа- ет в себя определение требований, проектирование, кодирование и тес- тирование, причем все эти процессы, как правило, реализуются в не- сколько итераций. К эксплуатации относятся процессы мониторинга и управления ра- ботающим приложением, которые планируются и стартуют незадолго до окончания разработки и продолжаются до утилизации. Включение в жизненный цикл ПО эксплуатационных процессов является ключевым моментом: именно разрозненность команд разработчиков и операцион- ного персонала считается одной из самых острых проблем корпоратив- ных приложений, а их интеграция с помощью ALM обещает серьезное повышение эффективности использования программного обеспечения бизнеса. Беда лишь в том, что в ALM-средах такая интеграция пока ос- тается благой целью, а не реальным воплощением. Описанная общая картина ALM на практике трансформируется в не- обходимость планирования и автоматизации множества этапов жизнен- ного цикла ПО. Идеальная среда ALM позволяет интегрировать всех участников жизненного цикла приложения, обеспечить им согласован- ный доступ к соответствующим ресурсам и задачам и при этом пони- мать контекст работы каждой отдельной роли, предоставляя ее исполни- телям нужные инструменты. В расширенный список ролей участников процессов ALM и выпол- няемых ими задач, которые должны поддерживаться соответствующим инструментарием, входят: • топ-менеджеры — управляют портфелями проектов и с помощью инструментальных панелей контролируют ключевые метрики жизнен- ного цикла ПО, включая риски и качество продукта; • менеджеры проектов — планируют и контролируют выполнение проекта, анализируют возможные риски и отвечают за распределение ресурсов; • аналитики — осуществляют взаимодействие с бизнес-пользователя- ми, определяют требования к программному продукту, управляют тре- бованиями и их изменениями на протяжении всего проекта; • архитекторы— моделируют архитектуру программной системы, включая ее функциональные компоненты, данные и процессы; • разработчики - пишут код, используя интегрированные среды раз- работки и различные инструменты обеспечения качества ПО на этапе кодирования; • инженеры отдела качества - создают и управляют тестами, выпол- няют функциональное, регрессионное тестирование, тестирование про- 133
изводительности, в том числе с помощью средств автоматизированного тестирования; • операционный персонал — выполняет мониторинг и управление приложением и осуществляет обратную связь с командой разработки по поводу возникающих проблем; • бизнес-пользователи - с помощью специализированных средств получают возможность формулировать требования, сообщать о дефек- тах приложения и отслеживать статус вносимых изменений. Однако «традиционный» процесс ALM не способен полностью рас- крыть свой потенциал в получении прибыли для организации. Дело в том, что многие производители агрессивно проталкивают на рынок ог- раниченные сквозные решения для ALM, которые нацелены на то, что- бы привязать заказчиков к закрытым технологическим платформам. Вскоре клиенты обнаруживают, что эти решения не интегрируются с их существующими процессами, средствами и платформами для разработ- ки. К несчастью, это оставляет коллективы разработчиков наедине с разрозненными процессами и мешаниной данных ALM, что, в свою очередь, не дает им полностью реализовать возможности ALM. Единая программная среда ALM призвана обеспечить инструменты для работы и руководства процессами на базе управления конфигура- циями и изменениями и контроля версий ПО. В целом внедрение подхо- дов и инструментов ALM позволяет сформировать стандартные процес- сы создания и эксплуатации приложений, контролировать соответствие им во всех проектах, реализовать строгий процесс управления измене- ниями, прогнозировать их влияние на ИТ-среду и бизнес в целом, сфор- мировать систему метрик качества, продуктивности и рисков разработ- ки, отслеживать и анализировать эти метрики на протяжении всего жиз- ненного цикла и в конечном итоге обеспечить реальное соответствие создаваемых приложений задачам бизнеса. Изначально одними из немногих новаторов, которые поняли важ- ность ALM и изменили свои стратегии выпуска продуктов в сторону яв- ной ее поддержки, были Borland и IBM Rational. Отреагировав на оче- видные возможности, к победившей концепции ALM примкнули и дру- гие компании: Microsoft, Тс 1сlogic, Mercury, Serena, Compuware, CollabNet и Mercury [15]. Сегодня ALM - это установившаяся тенден- ция и растущая индустрия, признанная аналитиками. Производители ре- шений ALM предоставляют различные средства и технологии для под- держки процесса разработки ПО. Эти средства выходят далеко за рамки традиционных инструментов для повышения производительности от- дельного разработчика. Они направлены на предоставление методик и инструментов, ориентированных на коллективную работу по созда- нию ПО. Чтобы создать жизнеспособное решение для ALM, производи- тели должны учитывать потребности расширенной группы разработчи- ков ПО и включать в свои продукты роли, которые участвуют в более широком процессе. Общим недостатком первых ALM-систсм была слабая интеграция модулей для разных этапов жизненного цикла как в рамках платформы 134
одного производителя, так и в рамках решений разных поставщиков. Не имея возможности использовать комплексную ALM-платформу, заказ- чики собирали ее из разрозненных частей, что в результате вынуждало их реализовывать сквозное управление процессами жизненного цикла вручную, тем самым нивелируя основное потенциальное преимущество автоматизации ALM. Поэтому в качестве главного направления совер- шенствования сред ALM четыре года назад аналитики Forrester прогно- зировали появление интегрированных платформ ALM 2.0, которые бы предоставляли общие сервисы средствам поддержки разных ролей в жизненном цикле, использовали единый физический или виртуальный репозиторий артефактов разработки, управляли микро- и макропроцес- сами жизненного цикла, обеспечивали интеграцию в единую среду ин- струментария для разных ролей, поддерживали сквозные возможности отчетности для разных этапов жизненного цикла. Сегодня возникают новые требования к ALM, и определяющую роль в этом играет широкое распространение скорых (agile) методов разра- ботки. Несколько лет назад создатель одной из наиболее известных ско- рых методик Scrum Д. Сазерленд заявил о грядущей тотальной адапта- ции идей скорой разработки. Это казалось преувеличением, но прогноз оказался верным. По данным совместного исследования аналитиков Capgemini Group и подразделения HP Software&Solutions, в 2010 г. свы- ше 60% компаний уже использовали или планировали использовать раз- работку в стиле agile, а среди участников опроса Forrester лишь 6% при- знались, что пока еще только присматриваются к скорым методам, все же остальные применяют их в той или иной степени, причем 39% счита- ют свои реализации вполне зрелыми [15]. Разработчики применяют скорые методы и передают продукт в экс- плуатацию, которая не учитывает реалии скорой разработки, что созда- ет серьезные препятствия для скорости реакции работающих приложе- ний на изменения требований бизнеса и, как следствие, гибкости (agility) самого бизнеса. Невозможность или нежелание операционного персонала реагировать на изменения прикладной среды, вносимые раз- работчиками, часто связаны с недостатками документации, которая пе- редается с этапа на этап, не отражая ключевых зависимостей между компонентами выпускаемого программного релиза, и, более глобально, с отсутствием надежного и автоматизированного канала связи между разработчиками и операционным персоналом. Эта проблема только усу- губляется с распространением современных средств автоматизации управления ЦОД иновых подходов к реализации ИТ-инфраструктур, включая облака. Предельно автоматизированные и рассчитанные на максимально быстрое развертывание приложений, такие среды не смо- гут реагировать на изменения при отсутствии автоматизированного ка- нала передачи информации и без реализации сквозных процессов между этапами разработки и эксплуатации. Осознание остроты проблемы и тенденция поиска средств ее реше- ния даже породили новый термин DevOps, применяемый для обозначе- ния концепций и технологий улучшения взаимодействия между разра- 135
боткой и эксплуатацией. Основные надежды на реализацию этих идей аналитики возлагают на ALM-среды нового поколения, которые на де- ле, а не в теории обеспечат интеграцию ключевых этапов жизненного цикла приложений. Создаваемые приложения сегодня во многих случа- ях композитны и интегрируют на сервисных принципах компоненты, реализованные на разных языках программирования для разных плат- форм, а также код внешних систем и унаследованные решения. Для управления их жизненным циклом среда ALM должна поддерживать различные среды разработки и платформы выполнения (например, NET и J2EE), а также обеспечивать возможность контроля исходного кода, лицензирования и статуса разработки внешних компонентов приложе- ния. Среди признаков широкой адаптации Agile-процессов аналитики от- мечают отход организаций от ортодоксальности в отношении этих ме- тодов. Разработчики не боятся сочетаний разных процессов, если это позволяет им оптимизировать работу над новыми системами, поэтому среда ALM 2.0 должна поддерживать различные процессы и методики в области разработки, управления портфелями и обеспечения качества продукта. Последнее особенно важно: автоматизация сквозных процес- сов управления качеством — от определения требований до тестирова- ния и эксплуатации — может стать одной из наиболее сильных сторон комплексной платформы ALM. Линейка продуктов Rational для поддержки различных этапов жиз- ненного цикла ПО всегда выделялась широтой и фокусом на интегриро- ванность модулей между собой. Аналитики Butler Group оценили ком- плекс решений IBM Rational Software and Systems Delivery как наиболее полный из представленных на рынке по спектру реализованных компо- нентов ALM. Этот комплекс включает в себя продукты для управления портфелями проектов, проектирования и разработки на базе моделей, управления требованиями, управления конфигурациями и изменениями, управления качеством, управления сборками и релизами; оркестровки процессов жизненного цикла ПО и обеспечения отчетности и докумен- тации по этим процессам. Слово Systems в названии появилось после приобретения компании Telclogic, решения которой ориентированы на поддержку процессов системной инженерии и теперь интегрированы в портфель Rational. Их включение в ALM-среду IBM отражает тенден- цию сближения процессов разработки ПО и систем и формирования для них единой среды управления жизненным циклом. Но наиболее существенным вкладом компании IBM в развитие тех- нологий ALM является долгосрочный проект Jazz по созданию инфра- структуры для реализации интегрированной корпоративной платформы управления жизненным циклом приложений. На данный момент целый ряд продуктов семейства Rational уже интегрирован с платформой Jazz, выпущено несколько новых решений, изначально созданных для работы на базе Jazz, и в перспективе будет обеспечена поддержка инфраструк- туры Jazz во всех компонентах линейки Rational. 136
Ядром Jazz является платформа Jazz Foundation, объединяющая сер- вер Jazz Team Server и ряд дополнительных модулей интеграции. Jazz Team Server демонстрирует новый для ALM подход к интеграции ком- понентов для разных этапов жизненного цикла (рис. 3.15, [17]). Если традиционно такая интеграция базировалась на точечной связи между отдельными продуктами, то в Jazz реализована открытая распределен- ная сервисная архитектура на базе стандарта REST, которая обеспечива- ет простое взаимодействие инструментальных компонентов между со- бой (своего рода ALM Web). Интерфейс RESTful позволяет представ- лять в виде сервисов данные и функциональность разнообразных моду- лей. Использование подхода на базе стандартов Web обеспечивает хоро- шую масштабируемость Jazz, делая платформу универсальным решени- ем, способным поддерживать задачи ALM в небольших командах и в крупных коллективах разработчиков [24]. Eclipse Client Platform Visual Studio Client Platform Project and Team Structure Process Enactment Security and Access Jazz Repository Items and relationships Event history, Item history trends Jazz Team Server Storage Search Рис. 3.15. Интегрированная корпоративная платформа управления жиз- ненным циклом приложений Jazz Foundation предоставляет общие для всех компонентов ALM сервисы, позволяющие реализовать ключевые возможности современ- ной среды управления жизненным циклом приложений. Это, например, сервисы совместной работы, обеспечивающие взаимодействие различ- ных участников команды в процессе решения общих задач, поддержи- вающие взаимосвязи между разными этапами жизненного цикла и при этом учитывающие контекст каждой конкретной роли в ALM. Инстру- менты сотрудничества на базе Jazz используют средства мгновенных со- общений, средства организации длительных обсуждений, механизмы 137
вики и другие популярные возможности Web 2.0. При этом все взаимо- действия между членами команды рассматриваются как проектные ре- сурсы, которые хранятся в связи с теми артефактами, которые послужи- ли источником этих взаимодействий (например, дефектами или тесто- выми примерами). Сервисы Jazz Foundation также позволяют определять и выполнять процессы в соответствии с различными методиками, включая Rational Unified Process и разные варианты скорой разработки. Для этого предос- тавляются средства нотификации о событиях, поддержка связи членов команды в выполнении определенных потоков работ, задание и провер- ка выполнения правил, автоматизация базовых задач, организация пото- ков работ с использованием инструментария для разных этапов жизнен- ного цикла. Большое внимание уделяется обеспечению прозрачности процессов жизненного цикла и руководству процессами, для чего вво- дятся точные процессные метрики по статусу, проблемам и рискам про- екта и предоставляются инструментальные панели для их отслежива- ния, в том числе в реальном времени, на различных уровнях, от отдель- ных участников процессов до команды и уровня управления портфеля- ми. Среди других сервисов Jazz Foundation можно отмстить поисковые механизмы, средства безопасности, ролевой доступ, распределенный ре- позиторий для всех ресурсов разработки. Платформа Jazz обеспечивает интеграцию со средой разработки Eclipse, предоставляя ряд представлений и проекций. Некоторыми ком- понентами Jazz поддерживаются также веб-клиенты. Платформой Jazz предоставляются два значимых представления для Eclipse: Team Central и Team Artifacts. Оба представления служат для сбора информации и могут дополняться компонентами платформы Jazz. Разработки Eclipse, некоторые компоненты платформы Jazz позволяют пользователям обра- щаться к серверу Jazz непосредственно из веб-обозревателя [26]. Такую возможность обеспечивает пользовательский всб-интерфейс Jazz, Этот интерфейс больше подходит для временных или эпизодиче- ских пользователей, а не для интегрированной среды разработки, пото- му что он не требует установки никакого специального программного обеспечения на клиентском компьютере; все, что необходимо — веб-обо- зреватель. Каждый сервер Jazz имеет главную веб-страницу, на которой пользователь может выбрать область проекта и войти в систему. После входа пользователь может взаимодействовать с сервером Jazz и изучать информацию в репозитории Jazz, включая ознакомление с последними событиями, ввод и обновление элементов потока операций, а также за- грузку сборок. Из наиболее ярких новинок в семействе Rational, созданных специ- ально для работы на базе Jazz, — система Rational Team Concert (RTC), представляющая собой комплекс продуктов для организации совмест- ной работы и автоматизации процессов жизненного цикла ПО, полно- стью построенный в архитектуре Jazz. IBM Rational Team Concert явля- ется полноценной средой, предназначенной для организации разработки информационных систем в мультипроектном окружении, в котором уча- 138
ствует множество разработчиков. Инструмент позволяет объединить усилия специалистов в области разработки, организовать их эффектив- ное взаимодействие и сохранить высочайший уровень контроля над всей проектной деятельностью на всем протяжении проекта [19]. Система RTC реализует управление конфигурациями ПО, управле- ние задачами и сборками, а также планирование итераций и отчетность по проекту, обеспечивает определение различных типов процессов раз- работки и интегрируется с другими продуктами Rational для поддержки полного жизненного цикла ПО. В 2009 г. IBM также выпустила Rational Quality Manager (портал для управления тестированием на базе Jazz) и инструмент управления эффективностью Rational Insight, реализован- ный для платформы Jazz с использованием аналитических технологий Cognos и предназначенный для задач высокоуровневого управления портфелями проектов разработки. Широкие возможности в области интеграции IBM Rational Team Concert делают данный инструмент абсолютно уникальным. Среди су- ществующих интеграций следует отметить следующее. 1. Интеграцию с IBM Rational Requirements Composer в рамках со- вместной разработки приложений (Collaborative application lifecycle management, или CALM), которая позволяет связывать рабочие задания с требованиями, порожденными или измененными на основе этих зада- ний, и наоборот, требования с заданиями, созданными для планирова- ния работ по реализации данных требований. 2. Интеграцию с IBM Rational Quality Manager в рамках совместной разработки приложений (Collaborative application lifecycle management), на основе чего становится возможным организовать дефект-трекинг по результатам выполненных тестов в ходе тестирования выпускаемых программных продуктов. 3. Интеграцию с IBM Rational ClearQuest для синхронизации рабочих заданий и запросов на изменения, определенных в классическом средст- ве управления разработкой IBM Rational ClearQuest. 4. Интеграцию с IBM Rational ClearCase для синхронизации артефак- тов версионного и конфигурационного управления между двумя указан- ными средствами. Открытая архитектура Jazz Integration Architecture, лежащая в основе IBM Rational Team Concert, позволяет вести дополнительную разработ- ку новых механизмов интеграции с другими системами, которые могут быть развернуты и активно использоваться в организации. Одним из ва- риантов интеграции с данными системами может являться использова- ние продукта RTC Email Reader от компании «Финэко Софт», который обеспечивает синхронизацию рабочих заданий IBM Rational Team Concert в соответствии с email-сообщениями предопределенного форма- та. При этом возможна и обратная синхронизация благодаря встроенной подсистеме оповещений IBM Rational Team Concert. Надо также отметить, что управление версиями и конфигурациями на базе IBM Rational Team Concert может быть организовано практиче- ски в любом проекте, даже если среда разработки (IDE) не имеет непо- 139
средственной интеграции с этим инструментом. Это становится возмож- ным благодаря совместному использованию «толстого клиента» IBM Rational Team Concert и неинтегрируемого IDE. Так, если для Eclipse IDE, IBM Rational Software Architect, IBM Rational Application Developer и Microsoft Visual Studio такие интеграции существуют, то уже, напри- мер, с Delphi придется дополнительно использовать «толстый клиент» IBM Rational Team Concert, что не представляет больших трудностей. Литература к главе 3 1. Application lifecycle management [Электронный ресурс]// URL: http://en.wikipedia.org/wiki/ 2. 1S0/1EC 12207 Standard for Information Technology - Software Life Cycle Processes [Электронный ресурс] // URL: http://www.business- process.ru/software_engineering/software_engineering_iso_-iec_12207.html 3. 1S0/IEC 12207:1995 1SO/1EC 12207:1995 [Электронный ресурс] // URL: http://www.iso.org/iso/iso_catalogue/cataloguc_tc/cataloguc_detail. htm?csnumber=21208 4. 1S0/IEC 15288:2008 [Электронный ресурс] П URL: http://www.iso. org/iso/catalogue_detail?csnumber=43 5 64 5. Борисов M. Scrum: гибкое управление разработкой. Открытые сис- темы», № 04, 2007 [Электронный ресурс] // URL: http://www.osp.ru/ os/2007/04/4220063/ 6. Боэм Б. У. Инженерное проектирование программного обеспече- ния: пер. с англ. — М.: Радио и связь, 1985. - 512 с. 7. Вендров А.М. Современные технологии создания программного обеспечения. Обзор [Электронный ресурс] // URL: http://citforum.ru/ programming/ application/program 8. Вы начинаете работать с Rational? [Электронный ресурс] // URL: http://www. ibm.com/devcloperworks/ru/rational/newto/ 9. Гагарина Л.Г., Кокорева Е.В., Виснадул Б.Д. Технология разработ- ки программного обеспечения: учебное пособие / под ред. Л.Г. Гагари- ной. - М.: ФОРУМ; ИНФРА-М, 2008. - 400 с. 10. Гиббс Р. Управление проектами с помощью IBM Rational Uni- fied Process: пер. с англ. - М.: КУДИЦ-ПРЕСС, 2007 11. Гибкая методология разработки [Электронный ресурс] // URL: http://ru.wikipedia.org/wiki/%D0%930%BA%D0%B8 12. Гибкая методология разработки Agile [Электронный ресурс] // URL: http://www.methodlab.ru/technology/agile.shtml 13. ГОСТ 19.ххх [Электронный ресурс] // URL: http://www.rugost. com/index.php?option=com_content&task=category&sectionid=6&id=19&It emid=50 14. ГОСТ 34.601-90 Автоматизированные системы. Стадии создания [Электронный ресурс] // URL: http://www.rugost.com/index.php?option= com_content&task=view&id=95&ltcmid=53 140
15. Дубова Н. Программный круговорот [Электронный ресурс] Открытые системы». — 2011.— №1.— URL: http://www.osp.ru/os/2011/ 011 1/13006960/ 16. Каскадная модель [Электронный ресурс] // URL: http:7/ru. wikipedia.org/wiki/%D0%9A%D0%B0%D 1 %81 %D0%BA%D0% B0%D0%B4%D0%BD%D0%B0%D 1 %8F_%D0%BC%D0%BE%D0%B4 %D0%B5%D0%BB%D 1 %8C 17. Климов А. Открытая платформа Jazz — новый подход к разработке и развитию ПО [Электронный ресурс] // URL: http://www.luxoft.ru/ do wnloads/ed и/ ib m%20j azz%2 0 -%2 0 a%2 One w%2 0 appro ach%20for%2 0 so ftware%2 Odevclopmcnt.pdf 18. Крылов E.B., Острейковский В.А., Типикин И. Г. Техника разра- ботки программ. В 2 кн. Кн. 2: Технология, надежность и качество про- граммного обеспечения. - М.: Высшая шк., 2008. — 469 с. 19. Лесин Д. IBM Rational Team Concert// URL: http://www.institutio.ru/rtc 20. Майерс Г. Надежность программного обеспечения: пер. с англ. — М.: Мир, 1980.-360 с. 21. Миллер Д. Сравнение Agile-методологий // URL: http://agileguru 22. Орлов С.А. Технологии разработки программного обеспечения: учебник. - СПб.: Питер, 2002. - 464 с. 23. Открытое управление жизненным циклом приложений (ALM) [Электронный ресурс] // URL: http://www.interface.ru/hoine.asp?artld= 8633 24. Разработка при помощи IBM Rational Jazz: превращаем команду в оркестр! [Электронный ресурс] И URL: http://habrahabr.ru/company/ ibm/blog/70016/ 25. Родыгин А. Процесс разработки, или Разрабатываем процесс [Электронный ресурс] // URL: http://citforum.univ.kiev.ua/prograinming/ theory/devprocess. shtm 26. Трофимов А. Обзор нового продукта IBM Rational Team Concert и открытой платформы Jazz [Электронный ресурс] // URL: http: //www. inter fac е. r u/homc. asp ? art Id= 16750 27. Уразбаев А. Внедрение Agile [Электронный ресурс] // Компью- терПресс. - 2007. - № 5. - URL: http://www.compress.ru/article.aspx?id= 17661&iid=817 28. Что нового в IBM Rational Software Architect 8.0 [Электронный ресурс] // URL: http://www.ibm.com/developerworks/ru/library/r-whats- new-in-rational-software-architect-8/#authorl 29. Эллингсуорт M. Как использовать метод управления проектами Scrum, работая с IBM Rational Team Concert и платформой Jazz [Элек- тронный ресурс] // URL: http://www.ibin.com/developerworks/ru/library/r- 0701_ellingsworth/index.html
Глава 4 ПРОЕКТИРОВАНИЕ ПРОГРАММНЫХ СИСТЕМ. ОПРЕДЕЛЕ- НИЕ ТРЕБОВАНИЙ И ЦЕЛЕЙ ПРОГРАММНОГО ПРОДУКТА 4.1. Процесс проектирования как последовательная трансляция требований, предъявляемых к системе Процесс создания программной системы начинается с постановки задачи, в ходе которой определяются требования к программному про- дукту. Это один из наиболее важных этапов при создании ПС, так как от того, насколько полно, точно и ясно определены требования к разраба- тываемой ПС, ее функции и предполагаемые возможности, во многом зависит качество и стоимость разработки. В процессе постановки задачи четко формулируется назначение раз- рабатываемой ПО и определяется список основных требований к ней. Каждое требование, по сути, есть описание необходимого заказчику свойства ПС. Различают функциональные требования, определяющие функции, которые будут выполняться разрабатываемой ПС, и эксплуа- тационные (нефункциональные) требования, определяющие особенно- сти ее работы. Результатом постановки задачи должно быть техниче- ское задание на создание программной системы. Однако разработать техническое задание по начальным данным заказчика (пользователя) ПС достаточно сложно, а порой и невозможно. По многолетнему опыту крупных организаций — разработчиков про- граммных систем (как зарубежных, так и отечественных), разработке технического задания должна предшествовать оценка осуществимости программной системы, которая необходима заказчику. Как отмечает ав- тор книги [19], обычно заказчик выдаст 2—3 страницы текста задания и сразу же просит оценить время выполнения заказа и его стоимость. Нередки случаи, когда целые коллективы ошибаются в 5-10 раз и попа- дают в кабалу или теряют профессиональную репутацию. Чтобы избе- жать такой ситуации, нужно предложить заказчику оформить началь- ный договор на 2—4 недели, с тем чтобы 2—3 системных аналитика разо- брались в задаче, с помощью каких-либо инструментальных средств вы- полнили декомпозицию системы на компоненты, прикинули объем этих компонентов, время их реализации и другие параметры проекта. Об этом говорит и опыт создания программных систем, накоплен- ный в ранних проектах. Так, Г. Майерс отмечает, что не столь уж неве- роятно встретить документ, в котором требования формулируются в следующей форме: «Обеспечить максимальную надежность, эффек- тивность, адаптируемость, общность и безопасность системы, миними- зируя стоимость и время разработки, требуемую память и время реак- ции терминала» [10]. Ясно, что такого рода постановки задачи бессмыс- ленны. Поэтому разработка реального технического задания на созда- ние программной системы является сложным, трудоемким и достаточно затратным процессом, требующим проведения в ряде случаев серьезных научных и инженерных исследований. 142
Проектирование любой программной системы включает в себя не- сколько различных процессов. При хорошо поставленном руководстве проектом эти процессы явно выражены, так что могут быть установле- ны контрольные сроки, выбрана методология и по завершении каждого процесса можно проверить «доброкачественность его результатов». На рис. 4.1 представлена модель процессов проектирования типич- ной большой программной системы, предложенная в своей основе более 30 лет назад и до сих пор не потерявшая актуальности [11]. Заметим, что модель не зависит от методологии проектирования, все указанные в ней действия должны выполняться в той или иной форме независимо от языка программирования, писал ли исходные требования пользова- тель или заказчик, использовалось ли технология ООП, CASE-средства ит.д. На первом шаге формулируется предварительная постановка задачи и составляется перечень требований, т.е. четкое определение того, что пользователь ожидает от готового продукта. Часто заказчик (пользова- тель) составляет этот перечень самостоятельно. В ряде случаев это дела- ет разработчик в результате бесед с пользователем — или исследуя его потребности, или оценивая эти потребности самостоятельно. Здесь за- ключены большие возможности для ошибок. Например, заказчик не мо- жет адекватно сформулировать свои потребности, они могут быть не- правильно поняты разработчиком или не учтены в полном объеме. После предварительной постановки задачи и ее согласования разра- батывается концепция проекта. Концепция (от лат. conceptio — «пони- мание, система») — определенный способ понимания, трактовки какого- либо предмета, явления, процесса, основная точка зрения на предмет и др., руководящая идея для их систематического освещения. Концепция проекта разрабатывается на основе анализа потребностей бизнеса. Главная функция документа - подтверждение и согласование единого видения целей, задач и результатов всеми участниками проекта. Концепция определяет, что и зачем делается в проекте. Концепция про- екта - это ключевой документ, который используется для принятия ре- шений в ходе всего проекта, а также на фазе приемки - для подтвержде- ния результата. Она содержит, как правило, следующие разделы: • название проекта; • цели проекта; • результаты проекта (программные продукты); • допущения и ограничения; • ключевые участники и заинтересованные стороны; • ресурсы проекта; • сроки реализации проекта; • риски проекта; • критерии приемки; • обоснование полезности проекта. 143
Заказчик Пользователь | Оператор Спецификация аппаратуры Предварительный внешний проект Спецификация базового ПО Постановка задачи. Требования Разработка | /^налнз Проектирование подсистем, компонентов, модулей Концепция проекта.ТЗ Разработчик 5 Архитектура ПС Спецификация инструментальных средств и языки программирования Проектирование логики модулей Внешний проект программных модулей Детальный внешний проект Кодирование. Рис. 4.1. Модель процессов проектирования большой программной системы На основе разработанной концепции подготавливается техническое задание на разработку программной системы. ТЗ включает в себя вы- полнение следующих работ: 1) проведение аналитического обследования предприятия; 2) разработка функциональных и нефункциональных требований к ПС; 3) разработка (учет) требований к базовому ПО; 4) разработка (учет) требований по оборудованию, операционной системе, системному программному обеспечению и др. Следующий шаг — постановка целей — задач, которые ставятся перед окончательным результатом, т.е, программным продуктом и самим про- ектом. Здесь требуется выявить и оценить ряд компромиссных решений. Могут быть ошибки из-за неправильной интерпретации требований или невыявления всего того, что требует компромиссных решений. Третий шаг — выполнение предварительного внешнего проекта высо- кого уровня. На этом шаге определяется взаимодействие с пользовате- лем (оператором), но не рассматриваются многие детали проекта, на- пример форматы ввода-вывода данных. На этом шаге цели программно- 144
го продукта и проекта преобразуются во внешние спецификации, т.е. точное описание поведения всей системы с точки зрения пользователя. Внешний проект приводит к двум параллельным процессам. 1. Детальное внешнее проектирование - определение взаимодейст- вия с пользователем до мельчайших подробностей. 2. Разработка архитектуры системы - разложение ее на множест- во подсистем, программ или компонентов и определение сопряжений между ними. Эти два шага ведут к процессу проектирования структуры программ- ной системы, во время которого проектируются подсистемы, компонен- ты подсистем, модули и их сопряжения, а также взаимосвязи для каж- дой программы. Следующий процесс — внешнее проектирование каж- дого модуля — это точное определение всех сопряжений модулей. За- ключительный шаг проектирования модулей - разработка их внутрен- ней логики. Он также включает выражение этой логики текстом про- граммы. Четвертый шаг представляет собой несколько процессов перевода от внешнего проекта системы (архитектуры системы) к проектам архитек- туры подсистем, модулей (компонентов). На этом же шаге проектиру- ется база данных. Это процесс определения всех внешних для про- граммной системы структур данных, например записей в файлах или в базе данных, структуры этих записей, включая их поля, и т.п. Следующий шаг— внешнее проектирование модулей (компонен- тов) — точное определение всех сопряжений модулей и проектирование логики каждого компонента. Следует отметить, что между процессами проектирования есть суще- ственная обратная связь. Например, во время одного из шагов внешнего проектирования могут быть обнаружены погрешности в формулировке целей, тогда нужно немедленно вернуться к предыдущему шагу и устра- нить недостатки. Серьезный учет такой связи соответствует современ- ным инкрементным и эволюционным подходам к конструированию программных систем. В целом процесс создания ПС можно описать как ряд процессов пе- ревода информации из одного представления в другое, начинающихся с постановки задачи и заканчивающихся набором подробных инструк- ций, управляющих компьютером при решении задачи. Создание ПС в этом случае представляет собой совокупность процессов трансляции, т.е. перевода исходной задачи в различные промежуточные решения, пока наконец не будет получен подробный набор команд. Поэтому фор- мулу, изображенную ниже, можно назвать макромоделью перевода сле- дующего вида [13]: <Z,L„Pz >^PR' < TR,L,r < C ,LC >^>PR> —As ДЛ >—> • PRn —>< 5, PsLM >. (4.1) Здесь приняты следующие обозначения: • Z — предварительная постановка задачи на разработку ПС; 145
• Р- — параметры (характеристика) задачи; • Lz — язык описания задачи; • PRj — процедура трансляции на /-м этапе перевода, i = 1,2, .. • N — количество различных этапов разработки системы; • TRS — требования, предъявляемые к программной системе; • TRs = TRfuTRnf; • 77?z -функциональные требования, предъявляемые к системе; • TRnj — нефункциональные требования, предъявляемые к системе; • L}}. — язык описания требований; • Cs — цели программной системы (программного продукта); • Le — язык описания целей; • As — архитектура программной системы; • La — язык описания архитектуры программной системы; • S — код разработанной программной системы; • РЛ. — параметры кода программной системы; • LM — машинный язык. Заметим, что на каждом этапе процесса создания ПС предполагает- ся использование определенного языка описания (постановки) задачи. Степень формализации этого языка растет по мере продвижения от од- ного этапа к следующему. Если на начальном этапе постановка задачи на разработку ПС заказчик использует чаще всего естественный разго- ворный язык, то с каждым следующим этаном язык становится все бо- лее формализованным, и, наконец, созданный программный продукт на машинном языке исключает его неоднозначную трактовку. Отсюда становится ясными следующие три положения, характерные для разра- ботки ПС: 1) недопонимание и разногласия между заказчиком и разработчиком связаны не только с неумением заказчика сформулировать проблему и незнанием предметной области разработчиком, но и низкой формали- зацией языка описания проблемы, которая приводит к неоднозначной трактовке задачи; 2) наиболее крупные ошибки и просчеты допускаются на начальных этапах разработки ПС, их сложнее и дороже устранить, особенно на по- следующих этапах; 3) необходимо повышать уровень формализации языка постановки задачи каждого этапа разработки системы с целью исключения или сни- жения ее неоднозначной трактовки всеми заинтересованными лицами. Сложность - основная причина ошибок перевода и одна из главных причин ненадежности программных систем [5, И]. В общем случае сложность объекта является функцией взаимодействия между его ком- понентами. Сложность архитектуры системы определяется связями ме- жду подсистемами. Сложность проекта ПС — функция связи между мо- дулями (как ее оценить, рассмотрим ниже). Сложность отдельного мо- дуля — функция связи между его командами. В борьбе со сложностью применимы две концепции общей теории систем. Первая — независимость. В соответствии с этой концепцией для 146
минимизации сложности необходимо усилить независимость компонен- тов системы. По существу это означает такое разбиение системы, что- бы высокочастотная динамика (взаимодействие) системы была заключе- на в единых компонентах, а межкомпонентные взаимодействия пред- ставляли лишь низкочастотную динамику системы. Вторая концепция— иерархическая структура. Иерархия позволяет стратифицировать систему по уровням понимания. Каждый уровень представляет собой совокупность структурных отношений между эле- ментами нижних уровней. Концепция уровней позволяет понять систе- му, скрывая несущественные уровни детализации. Иерархия позволяет проектировать, описывать и понимать сложные системы. К этим двум концепциям Г. Майерс [11] предлагает добавить тре- тью: проявление связей всюду, где они возникают (точнее сказать, вы- явление результатов или возможных последствий этих связей). Основ- ная проблема многих больших ПС — огромное количество независимых побочных эффектов, создаваемых компонентами системы. Из-за них систему зачастую невозможно понять. И можно быть уверенным, что систему, в которой нельзя разобраться, трудно проектировать даже с минимальной гарантией надежности. Проектное решение любого уровня имеет некоторую внутреннюю организацию, или форму. Для минимизации сложности нужен метод проявления этой формы, с тем чтобы в соответствии с ней разбить про- ект на части. При внешнем проектировании разбиение системы в соот- ветствии с ее внутренней формой в [6] считается концептуальной цело- стностью системы. Две самые распространенные ошибки при работе над программными проектами — это отказ от вовлечения пользователя системы в процессы принятия решений и неспособность (нежелание) понять его культурный уровень (подготовленность) и окружающую его обстановку. Имеется тенденция умышленно исключать пользователя из процесса принятия решения. Основная причина заключается в том, что разработчик чувст- вует: если привлечь пользователя, то тот никогда не придет к оконча- тельному решению, его требования будут постоянно меняться. Действи- тельно, для такой тревоги есть основания, но преимущества от участия пользователя значительно превышают возможные неудобства. Разработчик слабо знает обстановку (или вовсе не знает), в которой находится пользователь, с какими трудностями он сталкивается и как будет применять программную систему. Бывает, что в проектировании операционной системы участвуют люди, никогда не использовавшие та- кие системы. Есть разработчики языков высокого уровня, никогда не пробовавшие реализовать прикладную программу на языке высокого уровня [11]. Это может привести к серьезным ошибкам в программной системе. Единственный способ избежать этих ошибок — поддерживать прочный контакт с пользователем в течение всего цикла разработки сис- темы. 147
4.2. Методология решения задач проектирования ПС по Г. Майерсу Хотя может быть, что в этом разделе читатель не увидит ничего принципиально нового, стоит обратить на него внимание. Большинство процессов разработки программного обеспечения — это процессы решения некоторых задач. Например, в схеме по рис. 4.1 внешнее проектирование сводится к решению такой задачи: «Переведи- те множество целей системы во внешние спецификации», где цели — ис- ходные данные, а внешние спецификации — неизвестные. В задаче про- ектирования логики модуля даны внешние спецификации, а неизвест- ное - текст его программы. Удивительно, по словам Г. Майерса, что разработчики программных систем не получили никакой специальной подготовки по общим мето- дам решения задач. В наше время, по происшествии 35 лет с момента издания его книги, ситуация практически не изменилась. По мнению Г. Майерса, общая схема решения задачи может быть следующей. Решение задачи. 1. Поймите задачу: • изучите данные; • изучите неизвестные; • достаточно ли данных для решения; • не противоречивы ли они. 2. Составьте план: • чего вы должны добиваться; • какие методы проектирования будут использоваться; • встречалась ли вам уже такая задача; • не знаете ли вы близкой (подобной) задачи; • можете ли вы воспользоваться ее результатом; • можете ли вы решить более специализированную или аналогич- ную задачу; • можете ли вы решить часть задачи. 3. Выполните план: • следуйте своему плану решения задачи; • проверяйте правильность каждого шага. 4. Проанализируйте решение: • все ли данные вы использовали; • проверьте правильность решения; • можете ли вы воспользоваться полученным результатом или примененным методом при решении других задач? Рассмотрим кратко элементы этой схемы. Худшая из ошибок, кото- рые могут быть сделаны при решении задачи, - не вполне разобраться в ее постановке. Чтобы хорошо понять задачу, нужно уяснить два ее компонента: данные и неизвестное. Данные — это все факты, касающие- ся задачи, и связи между фактами неизвестными. Уяснение всех данных 148
о сложной задаче - абсолютно неизбежная и большая работа. При этом в первую очередь необходимо охватить общую картину данных без де- талей, которые, однако, тоже запоминаются «в сторонке», чтобы их можно было легко вспомнить позже. Это позволяет увидеть общую кар- тину и определить место каждой детали. Вторая часть задачи — неизвестное. Проектировщику следует пони- мать, какую форму должно иметь решение. Если, например, задача — де- тальное внешнее проектирование, то проектировщик должен ясно пред- ставлять назначение внешних спецификаций, их потенциальных читате- лей, формат представления результатов и т.д. Прежде чем приступить к решению, следует разработать его план. Отсутствие плана — очень распространенная ошибка. В плане нужно оп- ределить, чего вы хотите добиться. Десять человек могут иметь десять разных мнений относительно правильного ответа на задачу проектиро- вания. Идея целей проекта (рассмотрим ее дальше) является решением этой проблемы. Ее суть состоит в том, что на уровне всего проекта оп- ределяются общие цели, которыми следует руководствоваться во всех решениях при проектировании. Ключевым компонентом успешного проектирования является мето- дология, выбор которой для каждого процесса проектирования должен быть зафиксирован в качестве одной их составляющих плана. Накоп- ленный опыт, образование и имеющиеся решения проблем также влия- ют на успех дела. Обычно проектировщик крайне редко сталкивается с задачей, которая уже не была бы решена полностью или частично. Да- же если решение найти не удается, вероятно, когда-то была решена близкая задача. Если и этого не удается найти, может оказаться эффек- тивным решение более специализированной задачи или части задачи. Наконец, можно упростить задачу, найти ее решение, которое поможет лучше увидеть решение первоначальной задачи. После этого можно прекратить заниматься упрощенным вариантом и начать решение снача- ла (рис. 4.2). Следующий шаг — решение в соответствии с запланированным под- ходом. Поскольку решение состоит из последовательности действий, разработчик должен проверять правильность каждого шага. После получения результата нужно еще его проверить. Разработчик должен просмотреть все данные, чтобы убедиться, что учтено все, что имеет отношение к проекту. Для этого полезно перечитать буквально каждое слово постановки задачи, вычеркивая каждый использованный в решении факт, а затем проверить, насколько существенно для задачи то, что осталось незачеркнутым. Явно выделенным этапом всякого процесса проектирования должна быть проверка правильности результатов, т.е. попытка найти ошибки перевода, возникшие в этом процессе. Стоимость исправления ошибки тем ниже, чем раньше она обнаружена [5, 6]. К тому же вероятность правильного исправления ошибки на ранней стадии работы над проек- том значительно выше, чем в случае обнаружения ошибки на более поздних этапах. 149
Рис. 4.2. Последовательность решения задачи Майерс предлагает сформулировать общую философию проверки в виде правила «N плюс - минус один». Вопрос первостепенной важно- сти при проверке — привлечение к этому подходящих людей. Наиболее подходящие люди - это те, в чьих интересах обнаружить все ошибки. Правило «N плюс - минус один» состоит в следующем: проверка пра- вильности фазы N проекта должна осуществляться проектировщиками фаз N + 1 и N - 1. В основе философии правильности проверки лежит утверждение, что всякий процесс проверки правильности (тестирова- ния) должен иметь разрушительный, даже садистский характер [11]. Цель должна состоять в том, чтобы обнаружить любой мыслимый де- фект, любую слабость в проекте, а не в том, чтобы просмотреть проект и показать, что он правилен. 4.3. Уровни требований к программным системам Можно выделить следующие уровни требований к ПС [20]: бизнес- требования, требования пользователей, функциональные и нефункцио- нальные требования. К этому нужно еще добавить системные требова- ния. Модель на рис. 4.3 иллюстрирует способ представления этих типов требований. Как и все модели, она не полная, но схематично показывает организацию требований. Овалы обозначают типы информации для тре- бований, а прямоугольники - способ хранения информации (документы, диаграммы). 150
Бизнес-требования содержат высокоуровневые цели организации или заказчиков системы. Как правило, их высказывают тс, кто финанси- рует проект, покупатели системы, менеджер реальных пользователей, отдел маркетинга и т.п. В документе об образе и границах проекта объ- ясняется, почему организации нужна такая система, т.е. описаны биз- нес-цели, которые организация намерена достичь с ее помощью. Опре- деление границ проекта представляет собой первый этап управления об- щими проблемами расползания границ. Рис. 4.3. Модель типов требований Требования пользователей описывают цели и задачи, которые поль- зователям позволит решить система. К отличным способам представле- ния этого вида требований относятся варианты использования, сцена- рии и таблицы «событие — отклик». В документе о вариантах использо- вания указано, что клиенты смогут делать с помощью системы. Кто бы и когда бы ни предложил новые характеристики, варианты использова- ния или функциональные требования, аналитик должен спросить: «Они попадают в указанные границы?» Если ответ «Да», то они соответству- ют спецификациям. Если «Нет», то их и учитывать не надо. А если от- вет такой: «Нет, но они должны там быть», то заказчик или тот, кто фи- нансирует проект, должен решить, готов ли он раздвинуть границы про- екта, чтобы принять новые требования. Функциональные требования определяют функциональность про- граммной системы, которую разработчики должны построить, чтобы пользователи смогли выполнить свои задачи в рамках бизнес-трсбова- ний. Последние иногда именуют требованиями поведения, они содер- жат положения с традиционным «должен» или «должна». Например, 151
«система должна по электронной почте отправлять пользователю под- тверждение о заказе». Системные требования обозначают высокоуровневые требования к продукту, который содержит многие подсистемы, т.е. к системе. Сис- тема - это совокупность программного обеспечения или подсистем ПО и оборудования. Люди — часть системы, поэтому определенные функ- ции системы могут распространяться и на них. Бизнес-правила включают в себя корпоративные политики, прави- тельственные постановления, промышленные стандарты и вычислитель- ные алгоритмы. Бизнес-правила не являются требованиями к ПО, пото- му что они находятся снаружи границ любой системы ПО. Однако они часто налагают ограничения, определяя, кто может выполнять конкрет- ные варианты использования или диктовать, какими функциями должна обладать система, подчиняющаяся соответствующим правилам. Иногда бизнес-правила становятся источником атрибутов качества, которые реализуются в функциональности. Функциональные требования документируются в спецификации тре- бований к программной системе, где описывается так полно, как это не- обходимо, ожидаемое поведение системы. Спецификация требований к ПО используется при разработке, тестировании, гарантии качества продукта, управлении проектом, приемке проекта и программного про- дукта. Атрибуты качества определяют нефункциональные требования, предъявляемые к программной системе. Они представляют собой до- полнительное описание функций продукта, выраженное через описание его характеристик, важных для пользователей или разработчиков. К та- ким характеристикам относятся легкость и простота использования, лег- кость перемещения, целостность, эффективность и устойчивость к сбо- ям. Другие нефункциональные требования описывают внешние взаимо- действия между системой и внешним миром, а также ограничения ди- зайна и реализации. Ограничения касаются выбора возможности разра- ботки внешнего вида и структуры продукта. Хотя в модели на рисунке показан поток требований в направлении сверху вниз, следует ожидать и циклов, и итераций между бизнес-тре- бованиями, требованиями пользователей и функциональными требова- ниями. В определении и управлении требованиями существует много про- блем, связанных с трудностью получения правильных и законченных требований до окончательного выпуска программного продукта [4]. Во многих исследованиях отмечена корреляция проблем, возникающих с требованиями, с высоким процентом неудачных завершений ИТ-про- ектов. Более того, в некоторых работах указывается, что от 60 до 70% ИТ-проектов завершаются неудачей из-за слишком плохого сбора, ана- лиза и управления требованиями. Отсутствие четко определенных и ак- туальных требований является наиболее веской причиной этих неудач. 152
Выпущенная программная система может оказаться неудачной по разным причинам, но чаще это вызвано такими причинами, как наличие кода, который был написан, но никогда не выполнялся, или тем, что слишком плохо был организован процесс сбора, анализа и управления требованиями (рис. 4.4). По данным исследовательской организации Standish Group, наиболь- шее количество ошибок происходит на этапе сбора, анализа и докумен- тирования требований. Доля ошибок в различных артефактах при разра- ботке программных систем представлена на рис. 4.5 [4]. Вследствие ошибок на разных этапах разработки программных систем приходится затрачивать 30—50% средств общего бюджета проекта на их исправле- ние. Причем 70-85% общего числа исправлений связано именно с ошибками, допущенными на этапе сбора, анализа и документирования требований. При определении требований зачастую бывает трудно выявить окон- чательные требования до поставки программного продукта. Обычно этот процесс организован очень неформально. Даже после того, как не- обходимая информация была собрана, зачастую она не обновляется и не отражает изменения, происходящие в процессе разработки. Для многих организаций совершенствование процесса определения требований спо- собно повысить показатели возврата инвестиций (RO1) даже больше, чем совершенствование процесса управления требованиями. Процент редко выполняемого кода. Процент кода, который был написан, но никогда не выполнялся. Повышение прибыли из-за сокращения времени выхода на рынок на один месяц. Процент потерь валовой прибыли, если компания промедлила на 6 месяцев с выход на рынок нового продукта. Общие трудозатраты на проект разработки ПО, вызванные необходимо- стью переделки. Неудачное завершение ИТ- проекта как результат плохого сбора, анализа и управления требованиями. 0% 10% 20% 30% 40% 50% 60% 70% Рис. 4.4. Причины неудачной разработки программной системы 153
1 — Требования 2 - Проектирование 3 - Интерфейс 4 - Данные 5-ПО б - Человек 7 - Документация 8 — Другие Рис. 4.5. Доля ошибок в различных артефактах при разработке программных систем 4.4. Определение требований к программным системам 4.4.1. Постановка задачи и принципы разработки требований Как отмечалось выше, один из наиболее ответственных этапов созда- ния ПС - этап постановки задачи. Как правило, заказчик выдает не- сколько страниц текста задания и просит оценить время исполнения за- каза и его стоимость. Автор пособия по технологии программирования [20] замечает, что надо быть сумасшедшим, чтобы на это согласиться. Чтобы избежать такой ситуации, нужно предложить заказчику офор- мить начальный договор с оценкой осуществимости. Организация-раз- работчик может, конечно, выполнить эту работу за свой счет (и многие крупные предприятия так и делают), но, во-первых, отношение к внут- ренним разработкам более спокойное, а во-вторых, оплаченный договор гарантирует серьезность намерений сторон. Постановка задачи - наиболее творческая часть разработки про- граммной системы, которая поднимает почти философские проблемы [6, 11]. В процессе постановки задачи четко формулируется назначение программной системы и определяются основные требования к ней. Каж- дое требование представляет собой описание необходимого или желае- мого свойства ПС. Различают функциональные требования, определяю- щие функции, которые должна выполнять разрабатываемая система, и эксплуатационные (нефункциональные) требования, определяющие особенности функционирования ПС. IEEE Standard Glossary of Software Engineering Terminology [2] опре- деляет требования как: 1) условия или возможности, необходимые пользователю для реше- ния проблем или достижения целей; 154
2) условия или возможности, которыми должна обладать система или системные компоненты, чтобы выполнить контракт или удовлетво- рять стандартам, спецификациям или другим формальным документам; 3) документированное представление условий или возможностей для п. 1 и 2. Требования, предъявляемые к разрабатываемой программной систе- ме, будем обозначать следующим образом [13]: TR, = ТРГ и TR„f, TRf = {Lf,rf,i = 1, 2,..., Nf} TRn) ={L„fpf,j = l,2,...,N„f}, (4.2) где TRS - множество требований, предъявляемых к разрабатываемой системе; TRf — множество функциональных требований; TRnj — множест- во нефункциональных требований; Lf — язык описания функциональных требований; Lnf— язык описания нефункциональных требований; г/ — некоторое функциональное требование; гУ — некоторое нефункцио- нальное требование; Nf— количество различных функциональных требо- ваний; N„f- количество различных нефункциональных требований; Остановимся более подробно на типах требований. В [14] предлага- ется рассматривать следующие типы требований. 1. Запрос заинтересованного лица или совладельцев (Stakeholder Request - STRQ) — общие требования заинтересованных лиц к системе. Используется для разметки всех требований, содержащихся во всех до- кументах, поступивших от заинтересованных лиц и пользователей. Эти документы могут не соответствовать словарю проекта. STRQ являются основой для создания в проекте трех основных документов, в которых требования (при необходимости) переформулируются в терминах слова- ря проекта: «Концепция системы» (Vision), «Дополнительные техниче- ские требования», «Спецификация сценариев использования» (для каж- дого сценария). 2. Свойство системы (Feature — FEAT) - используется для описания требований, относящихся к определению высокоуровневых функцио- нальных и нефункциональных требований системы (подсистемы), в до- кументе «Концепция системы» (Vision). 3. Вариант, или сценарий, использования (Use Case— UC)— функ- циональные требования. Используется для описания сценариев исполь- зования (формирование функциональных требований). 4. Дополнительные спецификации (Supplementary Requirement - SUPP)— нефункциональные требования. Используются для описания нефункциональных требований в документах типа «Дополнительные технические требования». 5. Термин (Term- TERM). Терминологические требования. Типы требований определяются в самом начале проекта, и на число типов нет никаких ограничений. Каждый тип требования должен иметь уникаль- ный набор атрибутов, связанных с требованиями. Например, атрибута- ми запроса заинтересованного лица могут быть приоритет, статус, тип 155
запроса и т.д. Каждый из атрибутов может иметь одно или несколько состояний. Например, атрибут «Приоритет» может иметь три состоя- ния: высокий, средний и низкий. Характеристики качественных требований по-разному определены различными источниками. Характеристики требований, являющиеся об- щепризнанными, приведены в табл. 4.1. Таблица 4.1. Характеристики требований Характеристика Объяснение Единичность Требование описывает одну и только одну вещь Завершенность Требование полностью определено в одном месте, и вся необходимая информация присутствует Последовательность Требование не противоречит другим требованиям и полностью соответствует внешней документации Атомарность Требование «атомарно», т.е. оно не может быть разби- то на ряд более детальных требований без потери за- вершенности Отслеживаемость Требование полностью или частично соответствует деловым нуждам, как заявлено заинтересованными лицами и документировано Актуальность Требование не стало устаревшим с течением времени Выполнимость Требование может быть реализовано в пределах про- екта Недвусмысленность Требование кратко определено без обращения к тех- ническому жаргону, акронимам и другим скрытым формулировкам. Оно выражает объективные факты, а не субъективные мнения. Возможна одна, и только од- на интерпретация. Определение не содержит нечетких фраз. Использование отрицательных утверждений и составных утверждений запрещено Обязательность Требование представляет определенную заинтересо- ванным лицом характеристику, отсутствие которой приведет к неполноценности решения, которая не мо- жет быть проигнорирована. Необязательное требова- ние - противоречие самому понятию требования Проверяемость Реализуемость требования может быть определена од- ним из четырех возможных методов: осмотром, де- монстрацией, тестом или анализом В проекте, контролируемом пользователем, требования к программ- ному обеспечению формулируются либо его разработчиком, либо со- вместными усилиями разработчика и пользователя. В таких проектах организация-пользователь имеет право утверждать требования и, как правило, спецификации следующих уровней. В не зависящем от пользователя проекте вся ответственность за оп- ределение требований ложится на разработчика ПС (большинство ПС, поставляемых на рынок). 156
Требования к программной системе дают возможность пользовате- лям сформулировать свои потребности в отношении конкретного про- граммного продукта. С этой точки зрения привлекателен проект, кон- тролируемый пользователем. Участие в этом процессе организации-раз- работчика, безусловно, увеличивает ее шансы правильно понять и ин- терпретировать требования. Рис. 4.6. Варианты организации контроля над ходом выполнения проекта Требования к программной системе, имеющей прототипы, обычно определяются по аналогии, учитывая структуру и характеристики уже существующих систем. Для формулировки требований к ПС, не имею- щей аналогов, бывает необходимо провести специальные исследования, называемые пр ед проектными (см. модели As 1s и As То Be). В процессе таких исследований определяют разрешимость задачи, возможно, разра- батываются методы ее решения (если требуются новые) и устанавлива- ют наиболее существенные характеристики разрабатываемой программ- ной системы. В общем случае разработку системных требований выполняют пред- ставители организации-пользователя (заказчика) и организации-разра- ботчика (проектировщика), решающих следующие задачи: 1) выявление наличия информации, необходимой для выполнения планируемых функций; 2) обеспечение полноты и точности определения функций, подлежа- щих выполнению программной системой, и взаимосвязей этих функ- ций; 3) выработка заключения по трудоемкости предстоящей работы. Требования для ПС среднего и большого размера должны разраба- тываться небольшой группой лиц, например по два представителя от пользователей и разработчиков. Пользователи должны быть представле- 157
ны ведущими специалистами, наделенными правом принятия оконча- тельного решения, и специалистом, являющимся непосредственным пользователем проектируемой системы. Разработчики должны быть представлены специалистом, который будет играть ведущую роль во внешнем проектировании системы, и ли- цом, которое будет в последующем участвовать в одном из процессов внутреннего проектирования (кодирования). Подобный подход гаранти- рует, что системные требования пользователя были установлены по воз- можности более точно при условии, что проектировщики могут пере- вести эти требования в программную систему с минимальным числом ошибок. Сам процесс определения требований предполагает анализ сущест- вующих систем, беседы с пользователями, проведение исследований осуществимости и оценки достоинств проекта. Основная цель процесса определения требований - направить процесс разработки на получение правильной системы, которая делает то, что необходимо, и ничего боль- ше. Описание требований должно быть достаточно хорошим и полным, чтобы между пользователями и разработчиками могло быть достигнуто понимание того, что система должна делать и чего не должна. В против- ном случае пользователи будут считать, что система может делать для них все, а программисты не будут понимать, какие функции будущей системы обязательно должны быть включены в первую версию и без них нельзя обойтись, а какие можно отложить до будущего релиза. Можно определить следующие шаги процесса разработки требова- ний: 1) перечисление (сбор) возможных требований и идей насчет буду- щей системы; 2) осознание контекста системы (моделирование предметной облас- ти или бизнес-моделирование); 3) определение функциональных требований; 4) определение нефункциональных требований. Первое, что нужно сделать, — это собрать всевозможные требования и идеи, которые можно будет использовать при проектировании систе- мы. Это будет несистематизированный список, в который должно по- пасть все, что касается системы и приходит в голову разработчикам, аналитикам и пользователям. Эти идеи будут кандидатами в будущих версиях системы и используются для планирования работ. Основными методами выявления требований являются: • интервью, опросы, анкетирование; • мозговой штурм, семинар; • наблюдение за производственной деятельностью, «фотографиро- вание» рабочего дня; • анализ нормативной документации; • анализ моделей деятельности; • анализ конкурентных продуктов; • анализ статистики использования предыдущих версий системы. 158
Каждое собранное предложение в списке должно иметь название и краткое описание, в чем оно заключается. Для дальнейшей работы не- обходима дополнительная информация для планирования и последую- щей реализации требований, в которую могут входить: • состояние предложения (одобрено, включено в план, утверждено); • трудоемкость в человеко-часах или стоимость реализации; • приоритет (критический, важный, вспомогательный); • уровень риска, связанный с реализацией предложения (критиче- ский, значительный, обычный). Этот список в ходе работ может уменьшаться (когда требования пре- образуются в другие артефакты, например варианты использования или нефункциональные требования) и увеличиваться, когда выдвигаются новые предложения. Для того чтобы правильно определить требования, разработчики ПС должны понимать контекст (часть предметной области), в котором ра- ботает система. Существует два подхода к описанию контекста систе- мы: моделирование предметной области и бизнес-моделирование. Мо- дель предметной области описывает важные понятия предметной облас- ти и их связи между собой. Нельзя путать модель предметной области с логической или физической моделями системы. Модель предметной области описывает только объекты предметной области, но не показы- вает, как система будет работать с ними. Такая модель позволяет соста- вить глоссарий системы для лучшего ее понимания разработчиками и пользователями. В отличие от модели предметной области, бизнес-модель описывает процессы (существующие - AS IS - или будущие - AS ТО BE), которые должна поддерживать система. Кроме бизнес-объектов, вовлеченных в процесс, эта модель определяет работников, их обязанности и дейст- вия, которые они должны выполнять. Для целей моделирования бизнес- процессов могут быть использованы различные специализированные нотации, такие как ARIS, BPML, 1DEF0 и др., и, соответственно, инст- рументы, их поддерживающие, а также инструменты, поддерживающие унифицированный язык моделирования UML, предназначенный прежде всего для разработки программных систем. В этом отношении следует отметить широкие возможности продукта IBM Rational Software Architect, предназначенного для построения моделей разрабатываемых программных систем с использованием унифицированного языка моде- лирования UML 2.0. 4.4.2. Бизнес-моделирование Прежде чем приступить к разработке программной системы и к формированию требований к ней, необходимо (за редким исключением) выполнить обследование автоматизируемого предприятия и провести анализ результатов обследования с целью получения описания сущест- вующих на предприятии бизнес-процессов и разработки рекомендаций по их улучшению. Рассмотрим порядок действий, подлежащих выпол- 159
нению, цели и задачи, решаемые в ходе проведения пред проектного анализа, а также способы оформления получаемых результатов. Любое предприятие и любую предметную область можно предста- вить в виде системы, обладающей некоторой структурой и поведени- ем. Если говорить о предприятии, то оно имеет организационную структуру, включающую в себя набор подразделений, связанных друг с другом определенным образом. Что касается предметной области, то она состоит из множества объектов и взаимосвязей между ними. Как объекты предметной области, так и подразделения предприятия явля- ются участниками или непосредственными исполнителями некоторых процессов. Объекты могут сами обладать некоторым поведением либо могут изменяться под воздействием других объектов в ходе того или иного процесса. Применительно к процессу автоматизации деятельности предпри- ятия его подразделения могут осуществлять какие-то бизнес-процессы или действия в рамках процессов, либо могут играть роль вспомогатель- ных единиц, предоставляющих необходимые для осуществления рас- сматриваемого процесса ресурсы, либо они могут являться потребителя- ми результатов выполнения процесса. Такие те же роли могут играть и внешние по отношению к данному предприятию субъекты (другие ор- ганизации или физические лица) и объекты (например, информацион- ные системы). Часть осуществляемых процессов может быть автомати- зирована. В этих случаях можно говорить о том, что данный процесс выполняется данным подразделением с использованием некоторой ин- формационной системы или программно-инструментального средства. Считается, что автоматизация того или иного процесса должна повы- шать его эффективность. Пути повышения эффективности могут быть различны в зависимости от конкретной ситуации. Иногда достаточно передать выполнение части каких-то действий той или иной информа- ционной системе (ИС). В некоторых случаях такая система позволит значительно сократить набор выполняемых действий за счет исключе- ния лишних или ненужных. Однако часто необходимо существенным образом модифицировать весь процесс, прежде чем передать все входя- щие в его состав действия либо необходимую их часть информационной системе. Не вдаваясь в детали, в способы модификации процессов, остано- вимся на технологии, обеспечивающей выполнение всех необходимых действий по описанию бизнес-процессов с целью последующей их авто- матизации [16]. Основными компонентами любой технологии, как известно, являют- ся следующие: 1) процесс, определяющий набор осуществляемых действий и их последовательность; 2) получаемые на выходе результаты; 3) средства и ресурсы, необходимые для выполнения указанных действий и получения нужных результатов (в том числе инструменталь- ные средства); 160
4) состав исполнителей. В [16] предлагается такая последовательность действий по описанию и моделированию бизнес-процессев автоматизируемой предметной об- ласти: 1) составляется описание существующих на предприятии процес- сов, выполняется построение модели процессов «Как есть» (AS IS); 2) на основе определенных критериев выявляются проблемные про- цессы и (или) действия, или, иначе, проблемы; 3) намечаются пути решения выявленных проблем, отдельно выде- ляются те, которые могут быть решены посредством автоматизации; 4) строятся модели процессов «Как будет» (AS ТО BE), отмечаются те процессы, которые должны быть автоматизированы. Описание существующих бизнес-процессов должно удовлетворять следующим требованиям: 1) описание выполняется в виде набора действий и входов/выходов; 2) указываются исполнители (сотрудники конкретных подразделе- ний, программные средства, другие механизмы и т.д.); 3) определенные в процессе описания действия, составляющие про- цесс, должны быть классифицированы в соответствии с необходимыми критериями; 4) описание желательно выполнять с использованием специализи- рованного инструментального средства, позволяющего отобразить все необходимые атрибуты бизнес-процесс а. Результатами деятельности по описанию бизнес-процессов должны являться модели бизнес-процессов автоматизируемой предметной об- ласти. Выявление проблемных процессов может быть выполнено посредст- вом использования специализированного инструментального средства, либо проблемы могут быть просто перечислены в отдельном документе. Желательно, чтобы выявленные проблемы были явным образом связаны с вызывающими их действиями и (или) процессами. Еще лучше, чтобы тут же была продемонстрирована связь проблем с путями их решения. Что касается модели процесса «Как будет», то его описание должно удовлетворять тем же самым требованиям, что и описание существую- щих бизнес-процессов. Единственным дополнительным атрибутом яв- ляется четкое разделение всех действий, входящих в состав процесса, на те, которые будут подлежать автоматизации, и те, которые разрабаты- ваемой информационной системой не будут автоматизированы. Можно утверждать, что существует некоторая достаточно общая концептуальная схема, в которую можно уложить практически любой бизнес-процесс. Наглядным примером такой схемы является следую- щая: «Инициация (возникновение) — развитие (становление) — существо- вание (стадия жизни) — исчезновение (гибель)». Если говорить о процес- се управления, то для него общераспространенной является схема «Пла- нировать— делать — контролировать (проверять) — действовать», поло- женная в основу стандарта ГОСТ Р ИСО 9001-2001. Стандарт ИСО 9000, как и вся серия ИСО 9000, — международная разработка, ко- 161
торой мы обязаны Международной организации по стандартизации (ISO). Положения стандарта нашли широкое применение в России, бы- ли официально переведены на русский язык и включены в националь- ный стандарт с названием ГОСТ Р ИСО 9000 [8]. При этом, если мы хотим применить описанную схему процесса управления к тому или иному объекту или процессу, нам необходимо обеспечить выполнение следующего набора действий: сохранение ин- формации о планируемых показателях, учет информации о текущем со- стоянии объекта, анализ плановой информации и информации о теку- щем состоянии управляемого объекта и регулирование. Нетрудно заме- тить, что своего рода стандартные процессы, или, иначе, так называе- мые шаблоны процессов, присутствуют в любой предметной области. Например, если говорить о деятельности любого магазина, то ее можно уложить в следующую схему: «Обеспечение необходимого това- ра в наличии - продажа товара». Если говорить о строительстве зданий, то схема будет выглядеть приблизительно следующим образом: «Выбор и оформление места строительства — строительство — сдача готового объекта». И, наконец, всем нам хорошо известно, что процесс разработ- ки программного обеспечения также укладывается в соответствующую концептуальную схему. Такая общая схема какого-либо процесса играет роль шаблона или каркаса. При выполнении анализа предметной области понимание такой общей схемы аналитиком (и вообще ее наличие) существенно упрощает стоящую перед ним задачу, которая сводится к необходимости опреде- лить детали конкретной реализации данного схематичного процесса на данном предприятии. Диаграммы деятельности (Activity Diagram) унифицированного язы- ка моделирования UML как раз и позволяют отобразить не только сам процесс, но и каркас, в который его можно уложить, позволяя создать так называемые контейнеры для составляющих процесс действий. Кро- ме того, действия, составляющие анализируемый процесс, могут быть классифицированы на диаграмме деятельности в соответствии с други- ми критериями, набор которых может зависеть как от специфики анали- зируемой предметной области, так и целей, преследуемых аналитиком. К наиболее общим критериям тем не менее можно отнести следующие: • диапазон себестоимости/затрат на выполнение; • частота выполнения/периодичность; • диапазон эффективности (низкая, средняя, высокая); • исполнители (задействованные подразделения, организации; поль- зователи, информационная система/системы). Возможны и многие другие критерии. В случае необходимости отобразить классификацию выполняемых в ходе анализируемого процесса действий по нескольким критериям можно изобразить несколько диаграмм деятельности для одного и того же процесса, каждая из которых будет отдельным представлением для анализируемого процесса. Нотация диаграммы деятельности такова, что ее элементы позволяют выполнить полноценное описание бизнес- 162
процессов. Более того, с помощью диаграмм деятельности можно вы- полнить всю необходимую последовательность действий по моделиро- ванию бизнес-процессов предметной области с целью последующей их автоматизации в соответствии с описанной выше технологией (табл. 4.2). Таблица 4.2. Возможности элементов диаграмм деятельности по пред- ставлению бизнес-процессов Компонент описания бизнес-процесса Элемент диаграммы деятельности (Activity Diagram) Бизнес-процесс (как набор взаимосвя- занных видов деятельности, преобра- зующих входы в выходы) Диаграмма деятельности, основными элемента- ми которой являются элементы Action (дейст- вие), Activity Edge (связи между элементами Action, изображающие либо передаваемые объ- екты либо потоки управления), Partition (отде- ления) Модель бизнес-процесса (графиче- ское, табличное, текстовое, символь- ное описание бизнес-процесса либо их взаимосвязанная совокупность) Набор необходимых диаграмм деятельности, являющихся различными представлениями биз- нес-процесса Владелец бизнес-процесса (должност- ное лицо, которое управляет выполне- нием бизнес-процесса и несет ответст- венность за его результаты и эффек- тивность) Может быть изображен при помощи элемента Partition либо указан в виде текстового описа- ния Потребитель бизнес-процесса (тот, кто использует или потребляет результаты деятельности) Может быть изображен при помощи элемента Partition Дополнительные атрибуты бизнес- процесса (например, используемые ре- сурсы, исполняющие механизмы и т.д.) Могут быть изображены при помощи элемен- тов Partition либо указаны при помощи элемен- та Note, прикрепляемого к необходимому эле- менту диаграммы деятельности Классификация действий, составляю- щих бизнес-процесс, в соответствии с заданными критериями Может быть выполнена при помощи элементов Partition Идентификация проблемных действий Выделение соответствующих элементов Action цветом, классификация с использованием эле- ментов Partition Назначение исполнителей отдельных действий в рамках бизнес-процесса Может быть выполнено при помощи элементов Partition Возможность задать каркас выполне- ния процесса Может быть выполнено при помощи элементов Partition и Structured Activity Деятельность по выявлению и анализу бизнес-процессов автомати- зируемой предметной области, безусловно, несколько выбивается из на- бора «обязательных для выполнения» процессов в ходе проекта по раз- работке программной системы. Более того, анализ существующих и проектирование на его основе новых бизнес-процессов может выпол- няться и не в рамках проекта по разработке программной системы, а, на- пример, просто в рамках проекта по повышению эффективности дея- 163
дельности организации и (или) качества выпускаемой ею продукции и/или услуг. Для целей моделирования бизнес-процессов могут быть ис- пользованы различные специализированные нотации, такие как ARIS, BPML, IDEF0 и др., и, соответственно, инструменты, их поддерживаю- щие, а также инструменты, поддерживающие унифицированный язык моделирования UML, предназначенный прежде всего для разработки программных систем. IBM Rational Software Architect (RSA) является частью IBM Software Development Platform - набора инструментов, поддерживающих жизнен- ный цикл разработки программных систем. RSA предназначен для по- строения моделей разрабатываемых программных систем с использовани- ем унифицированного языка моделирования UML 2.0, прежде всего моде- лей архитектуры разрабатываемого приложения. Этот инструмент пре- доставляет возможность архитекторам и аналитикам создавать различные представления разрабатываемой информационной системы с использова- нием языка UML 2.0, а разработчикам - выполнять разработку J2EE, XML, веб-сервисов и т.д. Кроме того, Rational Software Architect поддер- живает технологию разработки, управляемой моделями (model-driven development, MDD), позволяющую моделировать программное обеспече- ние на различных уровнях абстракции с возможностью трассируемости. Хороший пример использования RSA для целей моделирования биз- неса приведен в [16]. Однако, если возможностей RSA, как правило, достаточно для построения моделей типа AS IS, то для построения мо- делей типа AS ТО BE часто их недостаточно. Дело в том, что средства RSA позволяют построить только статическую модель существующей на предприятии схемы бизнес-процессов и не позволяют промоделиро- вать производственный (или иной) процесс в динамике. Это не позволя- ет получить количественные характеристики (показатели) отдельных бизнес-процессов и всего производственного цикла, а также выявить уз- кие места как в организации отдельных бизнес-процессов, так и в целом во всем производственном цикле. IBM предлагает другой инструмент - WebSphere® Business Modeler (далее - Modeler), который считается лучшим в отрасли инструментом для моделирования и имитации бизнес-процессов [8]. С помощью Modeler бизнес-аналитики и другие пользователи (даже далекие от про- грамммирования) могут создавать бизнес-мо дел и для документирова- ния своих процессов, а затем осуществлять их имитационное моделиро- вание, чтобы понять поведение этих процессов в динамике. Пользовате- ли могут генерировать отчеты на основе модели процесса и по результа- там имитационного моделирования. Имитация позволит оценить эффективность бизнес-процессов, со- брать статистику выполнения процессов, выявить потенциальные облас- ти для их улучшения. Имитация бизнес-процессов — это, по сути, оценка эффективности реального бизнеса в виртуальной среде. 164
Имеется возможность экспортирования разработанных моделей в такие среды, как WebSphere Integration Developer (Integration De- veloper), WebSphere Process Server (Process Server) и IBM File Net P8 и хранения их в таких системах, как Rational® ClearCase и Rational Asset Repository. Модели могут быть опубликованы с помощью компонента WebSphere Business Modeler Publishing Server (Publishing Server), что по- зволяет авторизованным пользователям просматривать эти модели с по- мощью веб-браузер а. Эти модели могут также быть связаны с требова- ниями в продукте Rational RequisitePro и повторно использованы в про- дукте Rational Software Architect. В версии Modeler 6.2 в дополнение к перечисленным возможностям реализован ряд важных усовершенст- вований [21]. В частности, реализована новая возможность, позволяющая бизнес- пользователям развертывать свои проекты для моделирования и мони- торинга бизнес-процессов непосредственно на продуктах Process Server и WebSphere Business Monitor (Monitor). После того как проекты развер- нуты, автоматически создастся новое бизнес-пространство под названи- ем test space (пространство тестирования), в состав которого входят эле- менты (виджеты) для исполнения, управления и мониторинга процес- сов. Помимо прочего, эта новая функциональность под названием design to deploy (от проектирования до развертывания) поддерживает некото- рые сценарии для рабочих процессов персонала. Пользователи продукта Integration Developer могут вносить свой вклад в отладку проектов design to deploy. Этот сценарий под названием time to value сокращает число итераций между бизнесом и ИТ, благода- ря чему ускоряется проведение процесса. ИТ-специалисты по-прежнему должны участвовать в процессе, однако объем возлагаемой на них обя- зательной работы может быть уменьшен, в результате чего увеличива- ются возможности бизнес-пользователей и сокращается время до полу- чения экономического эффекта. После того как модель будет полностью протестирована, можно ис- пользовать функциональность design to deploy для проверки возможно- стей мониторинга модели с использованием одного из готовых шабло- нов мониторинга. 4.4.3. Определение функциональных требований Функциональные требования описывают сервисы, представляемые программной системой, ее поведение в определенных ситуациях, реак- цию на тс или иные входные данные и действия, которые система по- зволит выполнить пользователям. Иногда сюда добавляются сведения о том, что система не должна делать. При написании функциональных требований необходимо учитывать, что чем подробнее они будут, тем более точная оценка работ по стоимо- сти и срокам будет дана в техническом задании. Если на дальнейших этапах разработки ПС не возникнет дополнений к изначально с фор му- 165
лированным функциональным требованиям, то эта оценка будет доста- точно точной. Функциональные требования удобно представлять в виде диаграмм вариантов использования. Вариант использования представляет собой по- следовательность действий, выполняемых системой, в ответ на события, инициируемые внешним объектом (действующим лицом — актором по терминологии, принятой в ряде публикаций [10]). Такие диаграммы ис- пользуются в RUP-технологии разработки программных проектов [17]. Вариант использования описывает типичное взаимодействие между пользователем и системой и отражает представление о поведении систе- мы с точки зрения пользователя. В простейшем случае вариант исполь- зования определяется в процессе обсуждения с пользователем тех функ- ций, которые он хотел бы реализовать, или целей, которые он преследу- ет по отношению к разрабатываемой системе. Модель варианта использования дает подробную информацию о по- ведении системы или приложения. Она определяет требования системы в терминах требующейся функциональности (варианты использования) для достижения целей или для решения проблемы, определенной поль- зователем, и она же описывает окружение (агенты) и отношения между вариантами использования и агентами (диаграммы вариантов использо- вания). Модель варианта использования также включает диаграммы ва- риантов и диаграммы действий, которые описывают то, как пользовате- ли общаются с системой. Модель вариантов использования имеет следующие достоинства: • определяет пользователей и границы системы; • определяет системный интерфейс; • удобна для общения пользователей с разработчиком; • используется для написания тестов; • является основой для написания пользовательской документации; • хорошо вписывается в любые методы проектирования (как объект- но ориентированные, так и структурные). В дополнение к вариантам использования необходимо определить, как должен выглядеть пользовательский интерфейс для реализации каж- дого варианта использования, подготовить эскизы, обсудить их с поль- зователями, возможно, создать прототипы и отдать их на тестирование пользователям. Следует также иметь в виду, что каждый конкретный пользователь работает с системой по-своему, поэтому для определения действительных вариантов использования системы необходимо доско- нально узнать потребности всех заинтересованных пользователей, про- вести анализ полученной информации и систематизировать ее в дейст- вительные варианты использования системы, т.е. абстрагироваться от конкретных пользователей и исходить из бизнес-задач организации-за- казчика. Требования могут быть представлены в различных формах - от не- структурированного текста до выражений формального языка. Боль- шинство функциональных требований к системе могут быть выражены в виде вариантов использования. 166
Диаграммы вариантов использования— один из видов диаграмм UML, предназначенных для моделирования динамических аспектов сис- тем. Это основной вид диаграмм при моделировании поведения систе- мы и требований, предъявляемых к системе. Формулирование требова- ний к системе, по сути, представляет собой составление контракта меж- ду ней и теми сущностями, которые находятся вне ее. Этот контракт декларирует, что система должна делать. Хорошая система выполняет требования точно, предсказуемо и надежно. Удобным инструментом по- строения диаграмм использования является IBM Rational Software Architect — часть IBM Software Development Platform- набора инстру- ментов, поддерживающих жизненный цикл разработки программных систем. Продукт IBM Rational Software Architect предназначен для построе- ния моделей разрабатываемых программных систем с использованием унифицированного языка моделирования UML 2.0, прежде всего моде- лей архитектуры разрабатываемого приложения. RSA объединяет в себе функции таких программных продуктов, как Rational Application Developer, Rational Web Developer и Rational Software Modeler, тем са- мым предоставляя возможность архитекторам и аналитикам создавать различные представления разрабатываемой информационной системы с использованием языка UML 2.0, а разработчикам — выполнять разра- ботку J2EE, XML, веб-сервисов и т.д. Следуя принципам RUP, Rational Software Architect позволяет создавать необходимые модели в рамках рабочих процессов таких дисциплин, как: • бизнес-анализ и моделирование (business modeling); • управление требованиями (requirements); • анализ и проектирование (analysis and design); • реализация (implementation). Кроме того, Rational Software Architect поддерживает технологию разработки, управляемой моделями (model-driven development, MDD), позволяющую моделировать программное обеспечение на различных уровнях абстракции с возможностью трассируемости. Правильно сформулированные функциональные требования должны обладать следующими характеристиками. 1. Полнота. Каждое требование должно полно описывать функцио- нальность, которую следует реализовать в продукте. Оно должно содер- жать всю информацию, необходимую для разработчиков, чтобы им уда- лось создать этот фрагмент функциональности. Если данных определен- ного рода не хватает, следует помещать такие требования (необходимо определить) на полях, как стандартный флаг для выделения такого мес- та. Все пробелы в каждом фрагменте требований следует восполнить, прежде чем приступать к конструированию этой функции. 2. Корректность. Каждое требование должно точно описывать же- лаемую функциональность. Для соблюдения корректности необходима связь с источниками требований, например с пожеланиями пользовате- лей или высокоуровневыми системами. Требования к ПО, которые кон- фликтуют с родительскими требованиями, нельзя считать корректными. 167
Однако основная оценка здесь - за представителями пользователей, вот почему им или их непосредственным заместителям необходимо предос- тавлять требования для просмотра. 3. Осуществимость. Необходима возможность реализовать каждое требование при известных условиях и ограничениях системы и операци- онной среды. Чтобы не придумывать недостижимые положения, нужно обеспечить взаимодействие разработчиков с маркетологами и аналити- ками требований на весь период определения требований. Разработчики реально оценят, что можно выполнить технически, а что нет, или что сделать можно, но при дополнительном финансировании. Инкремен- тальная разработка и подтверждающие концепцию прототипы позволя- ют проверить осуществимость требования. 4. Необходимость. Каждое требование должно отражать возмож- ность, которая действительно необходима пользователям или которая нужна для соответствия внешним системным требованиям или стандар- там. Кроме того, требование должно исходить от лица, которое имеет полномочия на запись положения. Необходимо отследить каждое требо- вание, вплоть до стадии сбора мнений пользователей, когда выявлялись варианты использования, бизнес-правила или другие источники. 5. Назначение приоритетов. Каждому функциональному требованию, характеристике или варианту использования необходимо назначить приоритеты, чтобы определить, что нужно для каждой версии продукта. Если все положения одинаково важны, менеджеру проекта будет трудно справиться с уменьшением бюджета, нарушением сроков, потерей пер- сонала или добавлением новых требований в процессе разработки. 6. Однозначность. Все читатели требований должны интерпретиро- вать их одинаково, но естественный язык зачастую грешит многознач- ностью. Документация должна быть написана просто, кратко и точно, лексика должна быть понятна пользователям. Ясность - цель качества требований, связанная с точностью: читатели должны четко понимать каждое положение. 7. Проверяемость. Если требование не проверяется, вопрос его кор- ректной реализации становится предметом заключения, а не целью ана- лиза. Неполные, несогласованные, невыполнимые или двусмысленные требования также не проверяются. В 80-х - 90-х годах XX в. для описания функциональных требований широко использовался полуформальный метод Н1РО-диаграмм (Hierarchy plus Input — Process — Output): иерархия плюс ввод — обработ- ка — вывод [11, 12]. HlPO-диаграмма строится для каждой основной тре- буемой функции. В ней дается обобщенная характеристика входных и выходных данных для этой функции и основных шагов обработки. Для отображения иерархической организации таких функций строится оглавление. При использовании этого метода диаграммы обычно готовит органи- зация-разработчик, а затем предъявляет их пользователю для проверки. Задача Н1РО-диаграмм - дать пользователю наглядное представление о будущем продукте в надежде услышать от него: «Да, я хотел, чтобы 168
программа делала именно это». Важно понимать, что HIP О-диаграммы, так же как и диаграммы вариантов использования, отображают в основ- ном функциональные требования. Другие требования должны быть опи- саны отдельно. 4.4.4. Определение нефункциональных (эксплуатационных) требований Варианты использования системы охватывают часть нефункцио- нальных требований, специфичных для конкретного варианта использо- вания. Однако большая часть нефункциональных требований не может быть привязана к конкретному варианту использования системы. Такие требования должны быть вынесены в отдельный список дополнитель- ных требований к системе. Нефункциональные (эксплуатационные) требования определяют ха- рактеристики программной системы, проявляемые в процессе ее ис- пользования. К таким характеристикам относятся: • правильность, т.е. функционирование программной системы в со- ответствии с техническим заданием (никакое тестирование не дает 100%-й гарантии правильности, поэтому может идти речь об определе- нии вероятности наличия ошибок); • обеспечение правильности работы при любых допустимых данных и защиты от неправильных данных; • надежность и помехозащищенность — обеспечение полной повто- ряемости результатов, т.е. обеспечение их правильности при наличии различного рода сбоев и отказов технических и программных средств системы в целом, а также неправильных действий людей, работающих с этими средствами; • проверяемость— возможность проверки получаемых результатов (например, путем фиксации исходных данных, режимов работы и дру- гой информации, которая влияет на результаты); • точность результатов — обеспечение погрешности результатов не выше заданной; • защищенность (безопасность) - обеспечение конфиденциальности информации; • программная совместимость - возможность совместного функцио- нирования с другим программным обеспечением (в основном это связа- но с работой ПС под управлением заданной операционной системы, но может быть связано с обменом данными с другой программной систе- мой); • аппаратная совместимость— возможность совместной работы с определенным оборудованием. Это требование может быть сформули- ровано в форме минимально возможной конфигурации оборудования, на котором будет работать программная система (например, объем па- мяти, тип процессора и др.); • эффективность (производительность), например время ответа тер- минала, пропускная способность (некоторое количество полных работ, 169
выполняемых за определенный период времени), время обработки сооб- щения и др.; • адаптивность — возможность быстрой модификации с целью при- способления к изменяющимся условиям функционирования; • повторная входимость — возможность повторного выполнения без перезагрузки с диска (требование, обычно предъявляемое к резидент- ным программам, например драйверам); • реентерабельность — возможность параллельного пользования не- сколькими процессами (для удовлетворения этого требования реентера- бельная программа должна создавать копию изменяемых данных для каждого процесса). 4.5. Анализ и управление требованиями Требования склонны носить характер двусмысленности, неполноты, и несогласованности. Их устранение на этапе разработки требований стоит на несколько порядков меньше, чем устранение этих те же про- блемы на поздних стадиях разработки. Анализ требований направлен на решение данных проблем. Единственной важной функцией анализа тре- бований является выявление таких проблем. Правильное решение этого вопроса привело бы к полному и непротиворечивому набору требова- ний. Требования должны однозначно определять конечный продукт раз- работки и методы разработки, но не подход к проектированию. Это про- тиворечие лежит в используемом языке, на котором представляются ме- тоды разработки, а не в подходе к проектированию, что является глав- ной причиной трудности разработки хорошего набора требований. Требования служат для описания общих целей всех участников раз- работки проекта. Язык требований должен иметь такие семантические качества, которые лучшим образом связывают всех участников проекта, даже если между ними имеются разногласия по конкретным путям раз- работки. Пожалуй, одним из таких языков в настоящее время является язык UML [7]. Требования анализируются на протяжении всего времени разработки проекта. Анализ можно разбить на три шага. Первым шагом является набор требований, основанный на определенных начальных условиях. Этот шаг - наиважнейший из трех и самый сложный. Второй шаг соот- ветствует последовательному распределению требований по более низ- шим уровням иерархии системы с возрастающей детализацией. Именно на этом шаге возникают детализированные требования к ПС. И, нако- нец, когда начинается процесс разработки, то, исходя из реальных сооб- ражений разработки, кодирования и тестирования, происходит итера- тивная модификация требований, идущая в направлении снизу вверх. Одной из причин плохой разработки систем программного обеспече- ния является неумелая реализация двух первых шагов анализа требова- ний. При этом могут возникнуть следующие проблемы [11]: • декомпозиция архитектуры системы на подсистемы (компоненты) и разработка многоуровневой (послойной) архитектуры на основе кон- 170
цепций уровней абстракции и метода проектирования сверху вниз ста- нет невозможной; • тестирование становится невозможным; • из разработки исключается пользователь (заказчик); • управление теряет силу контроля. Все это результат слабого управления. Хорошее управление разра- боткой ПС невозможно без каких-либо направляющих требований. Если установленные требования туманны, то перед разработчиком возникает множество вопросов. Для эффективного использования труда разработ- чиков необходимо разбить процесс разработки на части. Если этого не сделать формально, разбиение произойдет само собой. Каждый разра- ботчик, приступая к первоначальной или конечной разработке, будет принимать массу решений, основанных на его узком собственном вос- приятии собственной части разработки. Наконец, когда отдельные части разработки стыкуются некоторыми методами, то в результате получает- ся разработка, возникшая на основе синтеза снизу вверх. Возможно, при наличии достаточных средств и времени с помощью насильственной реализации некоторых написанных требований подоб- ную разработку и можно будет довести до рабочего состояния, однако сделать это очень сложно. Синтезированная методом снизу вверх про- граммная система неизбежно становится неэффективной по следующим причинам: • имеет место высокая степень ненаправленной работы, приводящей к снижению продуктивности; • определенные функции дублируются (перекрывающаяся разра- ботка); • отсутствие необходимых функций остается необнаруженным вплоть до завершающих шагов разработки (недостаточность разработки). Еще одним последствием плохого определения требований является то, что персонал, проводящий тестирование, должен будет перед нача- лом тестирования сам выполнить анализ требований. Было бы значи- тельно лучше проводить анализ требований, используя системных ана- литиков, чем впоследствии для этой цели использовать специалистов по тестированию. Анализ требований тесно связан с управлением разработкой ПС, так как набор обоснованных требований является средством управления разработкой программной системы. Без правильного набора требований управление разработкой сталкивается со следующими объективными препятствиями [22]. 1. Перерасход первоначальных средств и времени является неуправ- ляемым, так как необходимые средства и время являются результатом личной оценки разработчиков по мере продвижения проекта, которая неизбежно бывает оптимистической. Если разработчик должен будет отчитываться в том, как он выполняет требования, то при этом возмож- но получение объективных оценок. 2. Пользователь исключается из процесса разработки набора требова- ний. Он может только наблюдать за процессом и воспринимать то, что 171
ему будет говорить разработчик о содержимом системы. Пользователь во многом зависит от разработчика, который по ряду вопросов может ока- заться некомпетентным, что и приведет его к неправильным решениям. Результатом неправильного предсказания возможностей разрабаты- ваемой системы, как правило, является то, что на завершающих этапах приходится многое делать заново. Этого можно избежать, если пользо- ватель будет иметь доступ к понятному ему набору требований и преоб- разовывать их по собственному желанию. Он заинтересован в этом в наибольшей степени, так как в случае ошибочной разработки страдать от этого больше всех придется ему. Скоординированный набор требований— лучшее средство взаимо- увязки ожидаемых разработчиками и пользователями возможностей бу- дущей системы. Язык требований является достаточно детализирован- ным для предварительной оценки стоимости и времени разработки и достаточно общим для выбора лучшего подхода к разработке. Без на- бора требований достижение согласия между разработчиками и пользо- вателями является проблематичным. Для эффективного управления требованиями необходимо: • проанализировать проблему (сформулировать ее совместно с заин- тересованными лицами; определить границы, в рамках которых решает- ся проблема, и ограничения, накладываемые на ее решение); • определить потребности заинтересованных лиц (результатом оп- ределения потребностей заинтересованных лиц является описание их потребностей в различной форме); • описать систему (результатом описания является ее описание на естественном языке и в графике с использованием моделей); • управлять проектом (определить ресурсы для управления: время, людей, финансы); • управлять изменяющимися требованиями (управление требова- ниями включает в себя отслеживание истории изменений требований, установление связей между требованиями, поддержку различных вер- сий требований). Не секрет, что заказчик может поменять требования по тем или иным причинам. Для проекта важно оценить, какие требования влияют на это требование и как данное требование влияет на другие. После того как требования определены и одобрены, изменения должны подпадать под контроль внесения изменений. Для многих проектов требования из- меняются до завершения создания системы. Это происходит частично из-за сложности программного обеспечения и того факта, что пользова- тели не знают, что им нужно на самом деле. Эта особенность требова- ний привела к появлению управления требованиями. Инструментальное средство IBM Rational Requisite Pro [ 1 ] позволя- ет очень легко получить матрицу, в которой видно, что повлечет за со- бой изменение того или иного требования. Прослеживаемость, или, по-другому, трассируемость (traceability), требований является воз- можностью проследить связь между требованиями различных типов. 172
Целями прослеживания связей между требованиями и элементами проекта являются: • определение источников требований; • управление областью применения проекта; • управление изменениями требований; • определение влияния на проект изменения требований; • определение влияния отказов и сбоев при тестировании требова- ний; • подтверждение правильности определения требований к системе; • подтверждение того, что система поддерживает только те функ- ции, которые были запланированы. Трассировка позволит оценить стоимость требуемых изменений, что очень важно. IBM Rational RequisitePro позволяет снизить трудоемкость процесса и повысить качество управления требованиями. Этот инстру- мент позволяет фиксировать различные типы требований, формировать для них необходимые атрибуты, осуществлять их структуризацию и трассировку. В RequisitePro требования хранятся в базе данных. Для каждого требования хранятся полная история его изменений и расши- ряемый набор атрибутов. RequisitePro предоставляет удобный графиче- ский интерфейс для ввода требований и работы с ними, который позво- ляет легко выбирать и сортировать требования по различным критери- ям. RequisitePro позволяет легко выбирать требования из текстовых до- кументов (в формате MS Word). Выделение требований из текста доку- ментов может производиться вручную или автоматически по структуре документа (например, каждый абзац — требование) или по ключевым словам (только предложения, содержащие слово «должна»). Более того, требования могут храниться в виде текстовых документов. При этом из- менения, внесенные в текстовый документ, автоматически воспроизво- дятся в базе данных. При работе с текстовым документом доступен не только текст требований, но и все его атрибуты. Такой способ хранения позволяет использовать требования в качестве составной части доку- ментов произвольной формы. Тексты таких требований могут редактироваться в базе данных с ис- пользованием графического интерфейса RequisitePro. Эти изменения ав- томатически переносятся в текст соответствующих документов. Доку- менты с требованиями целиком хранятся в базе данных проекта. Однако при необходимости они могут быть взяты из базы данных и использо- ваться как обычные электронные документы. В них могут вноситься но- вые или редактироваться старые требования. При возвращении доку- мента в базу данных проекта все изменения автоматически переносятся в базу данных требований. RequisitePro предлагает несколько различ- ных представлений для требований в виде матрицы трассировки, или дерева трассировки. При модификации текста требования или других его атрибутов та- кая структура позволяет легко отследить другие требования, связанные с измененным и, возможно, также нуждающиеся в изменении. 173
RequisitePro позволяет использовать для работы с требованиями различ- ные типы документов: описания сценариев использования, нефункцио- нальные требования, концепцию (Vision) и др. При необходимости мож- но создавать собственные типы документов, требований и определять набор атрибутов для описания этих требований (типовыми атрибутами требований являются, например, приоритет, сложность реализации и статус). Можно, например, разрабатывать шаблоны документов, соответст- вующие требованиям ГОСТ 34 или ГОСТ 19 [3]. RequisitePro поддержи- вает групповую работу с требованиями. Разработчики могут быть раз- биты на несколько групп, и для каждой группы могут быть назначены различные права на создание, редактирование и просмотр требований. Для хранения требований RequisitePro может использовать различные СУБД (в зависимости от объема требований и числа участников разра- ботки): MS Access, MS SQL Server или Oracle. Это обеспечивает воз- можность масштабирования — от использования RequisitePro в группах из нескольких разработчиков до самых крупных проектов. Так как отсутствие анализа требований совершенно очевидно приве- дет к неудаче, то возникает вопрос, почему он не всегда проводится. Анализ требований — это сложная работа. Аналитическая деятель- ность включает в себя рассудительность, интуицию, способность к обобщению, эвристический подход. Очень редко имеется возмож- ность дедуктивного или математического подтверждения. Многие ин- женеры характеризуют эту работу как невещественную. Если им пре- доставить выбор, то они подойдут к разработке на основе принципов, строго обоснованных физическими науками и математическими закона- ми, а не на основе анализа требований и именно поэтому неизбежно по- ведут разработку снизу вверх. Другая причина отказа от анализа требований связана с тем, что этот анализ не является необходимым шагом. Необходимыми являются толь- ко разработка и ее продукт. Подобно тестированию, которое включается только благодаря тому, что программисты делают ошибки, анализ тре- бований включается потому, что он является средством установления целей, для достижения которых все будут работать. Теоретически, если бы разработчики были достаточно опытны и подготовлены, то для них не требовалось бы установления направле- ния разработки и осуществления контроля над ними. Применение по- добных аргументов для сложных программных систем абсурдно. На практике, если человек пропускает формулировку требований и начина- ет разработку, то сначала возникает чувство удивительно быстрого и ус- пешного продвижения работы, а кончается серьезным разочарованием, за которым следует переработка готового программного продукта. Анализ требований может быть улучшен за счет глубины изучения субъективных свойств процессов. Существуют две возможности улуч- шения. Первая - количественная оценка процесса, вторая - тестирова- ние требований перед началом разработки. Другая проблема заключает- ся в том, чтобы требования были последовательными и чтобы они по- 174
этапно реализовывались в проекте. Для этого необходима согласован- ность действий разработчиков по этапам проекта. Улучшение процесса сбора, анализа, документирования, проверки и управления требованиями дает следующие ощутимые преимущества: • уменьшение ошибок и издержек при выпуске ПО; • повышение удовлетворенности заказчика и качества ПО; • уменьшение времени разработки ПО; • ужесточение контроля над изменениями; • повышение точности планирования; • повышение точности стратегического развития комплекса ПО на предприятии; • использование требований на разных стадиях разработки ПО; • повышение производительности аналитиков и других членов ко- манды; • улучшение обмена информацией по проектам; • повышение заинтересованности заказчика; • вовлечение всей команды в разработку. 4.6. Требования и риски Анализ требований главным образом имеет отношение к взаимодей- ствию и связи четырех элементов: стоимости, времени, полноты и риска реализации проекта. Взаимодействия между этими элементами образу- ют систему, в которой каждый параметр функционально зависит от дру- гих. Физический смысл этих элементов достаточно прозрачен. Остано- вимся на наиболее важном, который представляет собой ключевую кон- цепцию в разработке программных систем, — это риск [17]. Под риском понимается реально существующий или потенциально возможный фактор, обладающий большой вероятностью неблагоприят- ного влияния на успешное завершение основных этапов проекта. Други- ми словами, риски — это то, из-за чего проект может завершиться неуда- чей. Список рисков является одним из основных артефактов в техноло- гии RUP. Всегда есть желание создавать и внедрять полезные программы как можно быстрее. Однако, пытаясь завершить проект быстрее, есть риск, что будет выпущена программа, имеющая слишком много дефектов для того, чтобы быть полезной. Двигаясь слишком медленно, рискуем, что выпустим программу слишком поздно и она потеряет актуальность, или не выпустим ее вообще, или заказчик расторгнет контракт (рис. 4.7). Ос- ваивая новую технологию, технику, инструментальные средства, риску- ем наделать много ошибок. Не осваивая, рискуем проиграть в конку- рентной борьбе. Представление рисков можно формализовать, например определить ранг риска (важность), дать описание, стратегию исключения и др. Спи- сок рисков может быть и неформальным. Тем не менее рекомендуется оформить его в письменном виде и хранить в доступном месте, может быть, на веб-сайте проекта. 175
Рис. 4.7. Возможные риски в реализации проекта Чем выше сложность проекта, тем больше рисков при его разработ- ке. И число рисков будет возрастать, если у разработчика не налажен процесс управления требованиями. Следующая диаграмма показывает соотношение числа рисков и сложности проекта с наложенными на них пятью уровнями модели зрелости для управления требованиями (рис. 4.8). Рассмотрим эти уровни более подробно [4]. Уровень 0 харак- теризуется отсутствием требований (хаос). Данный уровень характерен для большинства организаций постсоветского пространства, так как ошибочно считается, что главное во всем процессе разработки - это именно программирование (кодирование) разрабатываемой системы. В данном случае организация делает предположение, что ей все по- нятно и известно для того, чтобы приступить к разработке программной системы, и что благодаря сэкономленному времени на этапе сбора, ана- лиза и документирования требований в дальнейшем будет возможность уделять больше внимания программированию. Результатом такого под- хода может быть: • продукт с пропущенной функциональностью; • продукт с никому не нужной функциональностью; • продукт низкого качества; • проекты никогда не заканчиваются в срок. Уровень 1. Документирование требований дает сразу неоспоримые преимущества. Во-первых, у разработчика появляется сразу некий доку- мент требований для разговора с заказчиком, в котором описывается, как он понял его потребности, и, читая данный документ, заказчик со- глашается или не соглашается с тем, что предложил разработчик. Во- вторых, появляется документ требований, который является неким бази- сом для всех членов команды разработки. Например, проектировщики и архитекторы могут продумать и построить более правильный дизайн системы, а тестировщики будут иметь эталон для тестирования разрабо- танной функциональности. В-третьих, документ требований может яв- ляться основой для новых людей, которые войдут в проект. Прочитав 176
данный документ, новый человек в команде будет знать, что система должна делать, а это уменьшит риск ошибки нового члена команды. Начав документировать требования, проектная команда сталкивается с новыми задачами - получить актуальный набор требований к про- граммной системе, не пропустить требования, не выполнять работу два- жды и т.д. Эти и другие задачи решаются на следующих уровнях моде- ли зрелости для управления требованиями. Рис. 4.8. Соотношение рисков и сложности проекта Уровень 2. Организация требований. Данный уровень модели зрело- сти для управления требованиями уже говорит о качестве требований — их формате, сохранности и версионности. Качественные требования по- нятны как заказчику, который их формирует, так и всем членам проект- ной команды, которые разрабатывают программное обеспечение, удов- летворяющее потребностям заказчика. Для этого требования должны быть не только читаемыми, но и однозначными и непротиворечивыми. Требования должны быть хорошо отформатированы. Сквозная нуме- рация, постоянные схемы, заголовки, шрифты и хорошее оглавление де- лают документы легкими для прочтения, понимания и дальнейшего ис- пользования. Если требования хорошо описаны, но плохо отформатиро- ваны, то документ может стать бесполезным в использовании. Формати- рование - это простой прием, но почему-то часто игнорируется. Здесь могут помочь корпоративные или международные шаблоны специфика- ций, такие как «Концепция» (Vision), «Спецификация требований» (Software Requirement Specification) и др. Причем во всей организации должны применяться единые форматирование и шаблоны. Писать качественные требования — непростая задача, поэтому руко- водитель должен учить своих аналитиков, организовывать процессы 177
проверки требований более опытными аналитиками, а также поручить им согласования требований с заказчиком. Уровень 3. Структурирование требований. Третий уровень модели зрелости для управления требованиями концентрируется на атомарно- сти и типах требований: бизнес-требования или системные, функцио- нальные или нефункциональные и т.д. Также данный уровень добавляет необходимую информацию к требованиям (кроме текста). Во-первых, разработчик должен выделять требования как атомар- ные единицы для того, чтобы было легче: • понять, что данное требование описывает; • ссылаться на конкретное требование, а не на страницу со сплош- ным текстом; • воспринимать изменения требований; • разрабатывать и тестировать конкретную функциональность; • присваивать атрибуты требованиям. Во-вторых, необходимо различать разные типы требований. Если все требования идут одним сплошным списком, то, скорее всего, там пе- ремешаны различные уровни их представления (бизнес-требования, пользовательские требования, системные требования), а также виды требований (функциональные, ограничения, бизнес-правила и т.д.). Та- кой документ, с одной стороны, плохо воспринимается заказчиком (биз- несом), который сосредоточен на формулировании бизнес-трсбований, а с другой стороны, непонятен для разработчиков, которые реализуют системные требования. В-третьих, следует добавлять атрибуты к атомарным требованиям, так как на самом деле все требования не равны между собой: одни более важные, чем другие, третьи сложнее реализовать, чем первые, и т.д. Не- обходимые атрибуты, добавленные к требованиям, позволяют легче ими управлять: • определять их приоритеты; • понимать, какие требования описаны, какие разработаны, а какие протестированы, • понимать, в какой версии продукта они реализованы, и т.д. Не следует делать распространенную ошибку — брать все возможные атрибуты из другого проекта. Нужно понять, какие атрибуты необходи- мы, исходя из сложности проекта, требований заказчика, методологии разработки и других условий. Третий уровень модели зрелости для управления требованиями дает лучшее понимание требований всем участникам проекта и облегчает управление ими. Атрибуты позволяют более четко очертить зону ответ- ственности каждого участника проекта, делать меньше работы по поис- ку нужных требований и уменьшить предположения. Разделение на ти- пы требований также позволяет быть уверенным в том, что аналитики не пропустили необходимую информацию и указали ее в требованиях. Поскольку типы и атрибуты требований сильно зависят от вида про- екта, должны быть выработаны соглашения перед началом каждого про- екта, которые оформляются в документе, называемом планом управле- 178
ния требованиями. На данном уровне вам уже будет трудно управлять требованиями с помощью простого текстового редактора (MS Word, Office Writer и др,). На помощь могут прийти специализированные ин- струменты: IBM Rational RequisitePro и IBM Tele logic Doors. Даже идеально структурированные и организованные требования могут не дать полной картины потребностей заказчика, потому что 20— 50 страниц текста воспринять очень трудно. На помощь могут прийти разного рода модели, которые позволяют активизировать второе полу- шарие мозга и взглянуть на требования по-новому. При изучении и документировании бизнес-требований могут помочь модель объектов предметной области (Business/Domain Object Model), модель бизнес-вариантов использования (Business Use Case Model) или модель бизнес-процессов (Business Process Model). При более детальном изучении потребностей заказчика (пользовательских требований) помо- гут модель системных вариантов использования (Use Case Model), диа- граммы действий, состояний и взаимодействия. Естественно, нельзя ув- лекаться только текстовым или только модельным представлением тре- бований. Нужно их комбинировать, четко осознавая, где лучше приме- нить текст и (или) модели для представления информации. Уровень 4. Трассировка требований. Реализация предыдущих трех уровней модели зрелости для управления требованиями не дает возмож- ности определять и прослеживать взаимосвязь между требованиями. Система любой сложности должна иметь иерархичность требований. Например, в RUP иерархия требований прослеживается между бизнес- потребностями, характеристиками программной системы, вариантами использования и системными требованиями. Возможность отслеживать данную взаимосвязь обычно называется трассировкой. Трассировка да- ет возможность проследить влияние требований друг на друга и провес- ти анализ покрытия требований. При изменении одного требования аналитику важно знать, какие еще требования должны при этом измениться. Это и называется прослежи- ванием влияния требований друг на друга. А после того как описан на- бор требований, необходимо знать, все ли требования верхнего уровня были детализированы и описаны на более низком уровне. Последнее на- зывается «анализ покрытия требований». Процедуру взаимосвязей и анализ покрытия обычно описывают так- же в плане управления требованиями. На данном этапе становится оче- видно, что управлять требованиями в полном объеме очень трудно без специализированных средств. Решить эту задачу можно с помощью ин- струментальных средств управления требованиями, таких как IBM Rational RequisitePro, IBM Rational ArchitectIBM, IBM Rational Composer, IBM Telelogic Doors или IBM Telelogic Focal Point. Уровень 5. Интеграция требований. Как отмечалось выше, требова- ния служат неким соглашением между тем, что заказчик хочет, и тем, что должно делать программное обеспечение. Но до этого момента нельзя было четко проследить связь между потребностями заказчика и кодом в программном продукте. На пятом уровне необходимо интегри- 179
ровать процесс управления требованиями с процессами управления из- менениями, проектирования, разработки, тестирования и управления проектом. Требования служат первичным входом для разработки программной системы. Поэтому архитекторы программной системы должны быть уверены (должны проследить), что все требования реализованы в дизай- не проекта. А разработчики должны понять, как требования соотносятся с кодом в программной системе. Интеграция требований в процесс управления изменениями даст уверенность, что требования не добавля- ются без тестирования и утверждения, а также что любое изменение не будет добавлено в программный продукт, не пройдя стадию анализа. Тестирование должно основываться на требованиях точно так же, как дизайн разрабатывается в соответствии с ними. Данный процесс даст уверенность, что программная система обеспечивает всю необхо- димую заказчику функциональность. Менеджеры проекта должны планировать работу по разработке сис- темы, опираясь на требования, так как они являются основой процесса разработки программного обеспечения. Менеджеры проекта должны иметь прямой доступ к требованиям, чтобы управлять проектом и сни- мать метрики с требований. Это позволит определить, насколько качест- венны требования, как часто они меняются, в каком статусе находятся, какие есть риски по реализации данных требований и т.д. Безусловно, такая плотная интеграция нужна в больших проектах и требует немалых затрат на закупку специальных средств по разработ- ке программной системы на всех ее стадиях. И, безусловно, пальму пер- венства в инструментарии полного жизненного цикла программного обеспечения держат линейки IBM Rational и IBM Tele logic. 4.7. Проверка правильности требовании Поскольку требования есть основа любого проекта, то специалисты, вовлеченные в работу над этим проектом, должны одинаково понимать критерии хорошего (правильно сформированного) требования. Опыт показывает, что наилучшим требованием считается такое, которое мо- жет быть охарактеризовано как [18]: • корректное (с технической и юридической точек зрения); • полное (выражать утверждение или законченную идею); • четкое, однозначное (недвусмысленное и не сбивающее с толку); • совместимое, согласующееся (не конфликтующее с другими тре- бованиями); • проверяемое (чтобы подтвердить, что результат соответствует тре- бованию); • трассируемое (уникально идентифицированное и отслеживаемое); • выполнимое (может быть реализовано в рамках запланированного бюджета и сроков); • модульное, блочное (может быть изменено без чрезмерных по- следствий для всего проекта); 180
• инженерно-независимое (не должно содержать описания конкрет- ного решения). Каждое требование должно выглядеть как законченное предложе- ние, содержащее подлежащее и сказуемое, другими словами - содер- жать предметную часть и утверждение (логическое условие или выска- зывание). При этом в зависимости от обстоятельств в этом предложении необходимо использовать либо глагол «должен», чтобы подчеркнуть, что требование является обязательным, либо «может», чтобы показать дополнительность или факультативность данного требования. Не воз- браняется использовать и смысловые вариации этих глаголов, но при соблюдении смысловых мер предосторожности, чтобы не исказить сути требования. Помимо того что законченное требование должно точно формулиро- вать конечную цель или определять желаемый результат, оно должно содержать критерии и оценки его успешной реализации или другие ана- логичные индикаторы качества, которые можно было бы измерить, по- скольку невозможно контролировать то, что нельзя измерить. Все тре- бования должны быть поддающимися проверке. При этом пользователь несет ответственность за проверку требований на полноту и точность, а разработчик - за проверку осуществимости и понятности. Общеприня- тая методика проверки— тесты. Если проверка тестами невозможна, должен использоваться другой метод проверки (анализ, демонстрация, осмотр или обзор дизайна). Определенные (установленные, зафиксированные) требования, по своей сути, не являются поддающимися проверке. Они включают требо- вания, которые говорят, что система никогда не должна или всегда должна показывать специфическое свойство. Надлежащее тестирование этих требований потребовало бы бесконечного цикла тестирования. Та- кие требования должны быть переопределены так, чтобы они стали под- дающимися проверке. Как указано выше, все требования должны быть поддающимися проверке. Но некоторые требования (как и ассоциированные с ними тесты) должны также отображать и то, что система не должна делать ни в коем случае, или описывать, как должна функционировать система, если бу- дет достигнуто какое-либо из оговоренных ограничений (режим ограни- ченного функционирования). По аналогии это правило применимо и к ограничениям (нефункциональным требованиям), в отношении которых бывает затруднительно построить тест в прямом смысле слова. Но если найти возможность сформулировать нефункциональное требование та- ким образом, что его можно было бы проверить, то это и будет наилуч- шим способом работы с требованиями. Так, вместо требования «Прило- жение должно обеспечить высокую степень использования» будет луч- ше и правильнее написать, например, следующее: «Неподготовленный пользователь должен иметь возможность создать отчет менее чем за 3 минуты». Нефункциональные требования, которые являются не поддающими- ся проверке на программном уровне, должны быть сохранены как доку- 181
ментация намерений клиента. Такие требования к продукту могут быть преобразованы в требования к процессу. Например, нефункциональное требование, чтобы программное обеспечение не содержало «потайных ходов», может быть удовлетворено заменой на требование использовать парное программирование. Возможность тестирования требований на всех стадиях разработки проекта является необходимым условием их подтверждения. Преобла- дающим подходом подтверждения является моделирование системы с учетом ее основных операционных характеристик. Путем моделирова- ния система подвергается экспериментальному тестированию с исполь- зованием строгого диапазона представления параметров, которые полу- чаются на основе различных наборов требований. Если результаты мо- делирования достаточно достоверны, то аналитик сможет сразу увидеть операционные последствия входных требований и точно выбрать луч- ший их набор для управления системой. Возможны следующие подходы к проверке требований: количест- венное вычисление, моделирование известным способом, новое моде- лирование, разработка прототипов и двойной счет [22]. Количественные вычисления по аналитическим моделям или при использовании прибли- женных, выведенных на основе статистики математических выраже- ний — самый дешевый и наименее надежный метод. Его используют в крайних случаях. Разница между этими методами и имитационной моделью системы важна потому, что применение специально созданной для данной систе- мы модели ведет к снижению стоимостных и временных затрат. Однако создание модели, адекватной будущей системе, — процесс сложный и дорогой, сопоставимый по сложности и стоимости с самой системой. Поэтому при недостатке средств и времени используются по возможно- сти уже разработанные методы и средства моделирования. Большинство специалистов, работающих с требованиями, находят моделирование весьма удобным и полезным дополнением к текстовым требованиям. Причем в данном случае совершенно непринципиально, что именно подразумевается под термином «моделирование»: это могут быть и рисунок, схема, алгоритм, нарисованные на доске или листке бу- маги, и использование MS PowerPoint, и создание реальной модели (программное приложение, мнемонический прототип и т.д.). Необходи- мо лишь соблюдать одно важное условие: эти модели и представления должны существовать в тесной связи (параллели) с требованиями, что- бы гарантировать их общую совместимость, корректность, трассируе- мость и изменяемость (если последнее вдруг потребуется). Визуальное представление требований привносит в работу над про- ектом огромные преимущества, поскольку является не только простым и в то же время мощным средством общения между всеми заинтересо- ванными лицами, но и обеспечивает дополнительные возможности по извлечению и формированию требований заказчиков и конечных поль- зователей, а также способствует более четкому и однозначному понима- нию этих требований всеми членами проектной команды. Следует заме- 182
тить, что нет необходимости всегда, везде и полностью заменять ясные, четкие и однозначные текстовые требования на модели и визуальные представления, но использование такого подхода там, где это уместно, даст организации дополнительную возможность значительно повысить уровень общения с заказчиком и эффективность взаимодействия в ко- манде. 4.8. Цели программного продукта Вторым процессом начального этапа проектирования программных систем является разработка (постановка) и описание целей для создавае- мой программной системы. При правильной постановке целей не дела- ется никаких предположений о конкретной реализации, но указывается, каким образом на последующих этапах проектирования следует прини- мать компромиссные решения. Например, невероятно важно иметь в ка- честве целей конкретного проекта такую формулировку [И]: «Обеспе- чить максимальную надежность, эффективность, адаптируемость и безопасность системы, минимизируя стоимость и время разработки, требуемую память и время реакции терминала». Такого рода формули- ровки бессмысленны, поскольку многие из перечисленных факторов противоречат друг другу. Таким образом, назначение раздела проекта постановки целей программного продукта — не только сами цели, но и, если нужно, компромиссы между этими целями. К общим ошибкам, совершаемым в процессе разработки и описания целей, относятся следующие: 1) цели не формулируются явно; 2) противоречивость в описании сформулированных целей (кон- фликт целей); 3) наличие поверхностно выявленных целей, не отражающих специ- фические особенности разрабатываемой программной системы; 4) цели программной системы с точки зрения пользователя (цели продукта) и цели проекта с точки зрения разработчика противоречивы. Работы по созданию программной системы должны обеспечить вы- полнение двух различных множеств задач (целей): задач продукта раз- работки, определяющих его цели с точки зрения пользователя (цели продукта), и задач проекта, отображающих цели в рамках проекта (гра- фик выполнения работ, стоимость, степень тестирования, степень на- дежности и др.). В силу противоречивости целей ошибка, выражающая- ся в отсутствии необходимого уровня достижения компромисса на сис- темной основе, является достаточно распространенной. Для понимания путей нахождения компромисса между противоречивыми целями следу- ет рассмотреть отношения между различными категориями компромис- сов. Поскольку одной из важнейших задач разработки программных систем является достижение надежной работы системы, далее рассмот- рим компромиссы между надежностью и другими наиболее важными требованиями. 183
1. Универсальность (общность) выступает в качестве меры количе- ства, мощности и объема функций, запрашиваемых пользователем. Не следует ставить перед проектировщиком задачу достижения максималь- ной универсальности, необходимо представить ему перечень специфи- ческих функций применения продукта. Следует помнить, что универ- сальность и надежность работы ПС противоречат друг другу. Уже одно увеличение объема ПС при росте числа выполняемых функций приве- дет к росту числа ошибок при программировании. Поэтому каждая функция должна быть взвешена в смысле ее реальной пользы для потре- бителя и сопоставления с ее влиянием на надежность. 2. Человеческие факторы являются мерой легкости понимания ре- зультатов, легкости использования, защищенности от неправильного употребления и, как результат, частоты ошибок пользователя. Хотя ин- теллектуальность интерфейса пользователя может увеличить сложность системы и, таким образом, иметь отрицательное влияние на надежность, психологические факторы и надежность обычно не вступают в кон- фликт. Хороший учет психологических факторов позволяет свести к ми- нимуму возможность неожиданных действий пользователя, что умень- шает возможность проявления скрытых ошибок в программном коде. 3. Адаптируемость ПС к различным условиям применения и классам пользователей является качественной мерой легкости использования и расширения за счет добавления новых функций. Адаптируемость и на- дежность в общем случае могут рассматриваться как совместимые. Бо- лее того, реализация мер по повышению надежности оказывает положи- тельное влияние на адаптируемость и расширяемость системы в процес- се ее сопровождения. 4. Удобство сопровождения характеризует меру затрат времени и средств на исправление ошибок в работающей системе. Это требова- ние согласуется с требованиями надежности, поскольку оно тесно связа- но с адаптируемостью. Кроме того, такие методы обеспечения надежно- сти, как обнаружение и изоляция ошибок, способствуют повышению надежности. 5. Безопасность определяется мерой вероятности того, что один пользователь может случайно или преднамеренно уничтожить данные, являющиеся собственностью другого пользователя, или препятствовать действию системы. Меры безопасности включают анализ ожидаемых угроз, тщательную взаимную изоляцию пользователей по данным и программам, обособление программ пользователей от операционной системы, разработку контрмер по противодействию проникновения в систему. Разработка и внедрение мер безопасности положительно ска- зывается на надежности. 6. Документация. Задачи документации связаны с количеством и ка- чеством публикаций в интересах пользователей. Документация и чело- веческие факторы сходны в том, что они связаны с облегчением пони- мания и использования программной системы. В связи с этим решение проблем документирования не противоречит достижению заданного уровня надежности. 184
7. Стоимость программной системы включает затраты, связанные с созданием и сопровождением системы. Стоимость в значительной ме- ре связана с количеством ошибок в программном продукте, а следова- тельно, с затратами на их исправление. Поэтому достижение высокой надежности и минимизация затрат на проектирование и разработку ПС в известной степени противоречат друг другу. 8. График работ. Одной из ключевых задач любого проекта является получение программного продукта к определенной дате. При этом пред- полагается, что экономический эффект продукта связан с датой его дос- тупности. Многие из связей между надежностью и стоимостью также существенны и для графика работы и надежности. Одной из причин из- менения графика работ является задача обеспечения заданной надежно- сти. Например, имеет место тенденция недооценивать время, необходи- мое для тестирования. Фактическое время тестирования связано с чис- лом ошибок, оставшихся в продукте. Отсюда задача довести до миниму- ма время, затрачиваемое на разработку, и до максимума — надежность является совместимой при условии, что сроки не сокращены до такой крайности, когда на надлежащее проектирование просто не остается времени. 9. Эффективность (производительность). Связи между производи- тельностью ПС и надежностью достаточно сложны. Многие приемы по- вышения надежности работы программ связаны с увеличением объема системы в числе таких команд, как, например, контроль и обработка ис- ключений, контрольные точки, двойной (а иногда тройной) просчет и др. Это приводит к увеличению времени выполнения программ и рос- ту объема необходимой памяти. С другой стороны, ненадежная про- граммная система не может считаться эффективной независимо от того, как быстро она работает. К этому надо добавить стремительный рост производительности компьютеров и их ресурсов, в том числе памяти, что ставит на первое место надежность работы ПС, так как именно ненадежность программ становится основной причиной, сдерживающей дальнейшее развитие информационных технологий во всех сферах деятельности человеческо- го общества. Практика отработки и сопровождения программных сис- тем показывает, что неэффективность системы, если в этом есть необхо- димость, может быть исправлена позднее, в то время как ненадежность достаточно трудно устранить. Абсолютная надежность программных систем была и остается недостижимой целью. В порядке обсуждения ниже автором предлагается подход, который может быть использован для выбора тех требований, предъявляемых к системе, которые наиболее совместимы с другими требованиями [14]. Для наглядности рассмотрения и анализа рассмотренных зависимостей между основными требованиями, предъявляемыми к программным сис- темам, и установления оптимального компромисса между конфликтую- щими требованиями, можно построить матрицу оценки уровня совмес- тимости (или конфликтности) требований конкретного проекта 185
(рис. 4.9). Элементы ту матрицы М принимают конкретные значения в зависимости от совместимости требований: • ту = 1, если 7-е требование Tj полностью совместимо с требовани- ем 7); • niy =0,75, если ?-е требование Tj скорее совместимо с требовани- ем Tj; • nijj = 0,5, если ?-е требование 7} частично совместимо с требова- нием 7); • ту = 0,25, если i-e требование Т, скорее несовместимо с требова- нием 7); • ту = 0, если i-e требование Т, несовместимо с требованием 7). Матрица совместимости строится экспертом (опытным высококва- лифицированным разработчиком) следующим образом. Рассматривает- ся /'-я строка матрицы и, учитывая введенную шкалу совместимости, для каждого /-го требования 7} определяется, по мнению эксперта, значение определяющее его совместимость с требованием Tj. Заметим, что матрица не является симметричной относительно диагонали. Это связа- но с тем, что, рассматривая некоторое требование Tj как основное, мож- но определить его совместимость с некоторым другим требованием Tj иначе, чем в том случае, когда требование 7} рассматривается как основ- ное. Сбалансированность требований можно оценить следующим обра- зом. Если для каждой z-й строки матрицы получить сумму ее элементов, то значение этой суммы R, будет характеризовать степень (рейтинг) со- вместимости требований 7} (где j = 1, 2, ..., л, j i- I) с требованием 7), которое считается основным. Таким образом, для каждой строки матри- цы i = 1,2, ..., п находим значение: (4.3) С другой стороны, получив сумму для каждого столбца матрицы, найдем рейтинг совместимости требования Tj для тех случаев, когда оно не является основным. Рейтинг совместимости в этом случае опре- деляется следующим образом: для каждого столбца матрицы /= 1,2,..., п находим значение '*>• (4.4) Общее значение рейтинга совместимости R- некоторого требования Tj можно определить, складывая соответствующие ему значения 7?; и 7?“, т.е. 7?;' - R 4- R, , / - i. (4.5) 186
у п А с Б Д Ст- Г Пр. Н Рейт. Универсальность 0,25 1 0.25 0,3 0,3 0,3 0,25 0,25 0 2,8 5,25 Псих, факторы 0 0 1 1 1 0,8 0,75 1 1 6,3 11,75 .Адаптируемость 1 0.25 0,25 0,5 0,3 0,3 0,25 0,25 0,5 2 * 5,5 Сопровождаемость 0,3 1 1 ” 1 1 0,3 0,25 0,25 1 5,8 9,75 Безопасность 0,3 1 0,3 0.25 1 0,3 0,5 0.5 1 4,8 10,25 Документация 0,3 1 0,8 1 1 0,5 0,5 0,5 1 6,3 11 Стоимость 0,3 0,25 0,3 0,25 0,3 0.3 0,25 0,25 0,3 * 4,75 График 0,3 0,25 0,3 0,25 0,3 0,3 о.з^ 0,25 0,3 4,75 Производительное!! 0,3 0,5 0,3 0,5 0,5 0,5 0,3 0,25 0,5 3,3 6,5 Надежность 0 1 0,3 0,5 1 0,5 0,3 0,25 0,25 4 9,5 Рис. 4.9. Матрица уровня совместимости Проведенные вычисления для матрицы, изображенной на рис. 4.9, показывают, что наилучшую совместимость с другими рассмотренными выше требованиями имеют требования по психологическим факторам, документации, безопасности, сопровождаемости и надежности. 4.9, Постановка целей для программной системы При правильной постановки целей для программной системы не де- лается никаких предположений о конкретной реализации, но указывает- ся, каким образом на последующих этапах проектирования следует при- нимать компромиссные решения. Как отмечалось выше, должны быть поставлены цели двух типов: цели программного продукта и цели про- екта. 4.9.1. Цели продукта Это цели с точки зрения пользователя системы. Они должны вклю- чать следующую информацию. 1. Резюме. Здесь кратко описываются общее назначение разрабаты- ваемого программного продукта и его функции. 2. Определение пользователя. Описывается круг возможных пользо- вателей системы. При множестве типов пользователей требуется опре- делить специфические особенности каждой группы пользователей. 3. Подробное описание функции. Подробное описание функций, ко- торые должны обеспечиваться системой. Это необходимо для того, что- бы гарантировать однозначное восприятие требований пользователей проектировщиками. 4. Публикации. Должны быть определены цели для документации, поставляемой пользователям, в том числе типы документации и предпо- лагаемый круг читателей для каждого типа. 5. Эффективность. Данная категория объединяет все цели эффек- тивности (производительности), такие как время реакции на действия 187
пользователей, пропускная способность (число обрабатываемых тран- закций в единицу времени), использование ресурсов (загрузка процессо- ров, памяти, каналов связи и т.п.), а также необходимые средства изме- рения производительности и средства настройки и оптимизации систе- мы. 6. Совместимость. Если конкретный программный продукт должен быть совместимым с другими, эти цели указываются в этом разделе. Следует также указать относящиеся к делу международные и государст- венные стандарты и внутренние стандарты компании. 7. Конфигурации. Здесь указываются различные конфигурации аппа- ратуры и программного обеспечения, в которых система может работать (компьютеры, операционная система, аппаратура связи др.), а также другие программные продукты (например, СУБД), от которых система зависит. Должны быть указаны дополнительные возможности выбора отдельных частей системы, если это предусматривается. 8. Безопасность. Сюда относится описание целей в отношении безо- пасности. Если система связана с финансовой деятельностью (напри- мер, банковская система, игорная система, система управления запаса- ми, учет материальных ценностей и т.п.), должны быть указаны средст- ва надзора. Если предусматривается обработка информации ограничен- ного использования (в системах государственного управления, военного назначения и т.п.), то требования к защите повышаются. 9. Обслуживание. Данная категория целей содержит сведения по за- тратам и времени на исправление ошибок, а также функции и средства для достижения этих целей, например диагностические программы. 10. Установка. Сюда относятся методы и средства настройки систе- мы на конкретные условия эксплуатации. 11. Надежность. Цели в области надежности, как и другие цели, су- щественно зависят от конкретного типа системы. Следующие вопросы должны рассматриваться обязательно для каждого типа системы: а) для каждого типа отказов должно быть определено (задано) сред- нее время между отказами (отказ системы, ошибка пользователя, отказы конкретных функций) с учетом степени серьезности такого отказа; б) среднее время восстановления системы после отказа каждого ти- па; в) ориентиры в отношении числа ошибок в программной системе, расклассифицированные по степени серьезности и времени обнаруже- ния; г) последствия отказов системы или отказа наиболее важных функ- ций; д) допустимый объем данных, утрачиваемых в случае отказа; е) жизненно важная информация, которая должна быть защищена от разрушения; ж) функции, необходимые для обнаружения и исправления ошибок; з) функции, необходимые для обеспечения устойчивости к ошибкам; и) возможности обнаруживать ошибки пользователя и сбои аппара- туры, а также восстанавливать работоспособность. 188
4.9.2. Цели проекта Множество целей проекта — это цели, которые должны быть достиг- нуты в процессе проектирования. Для набора этих целей характерно то, что они непосредственно не проявляются в конечном продукте. Цели проекта должны быть формально установлены, так как противоречивые и непредусмотренные (неожиданные) результаты получаются тогда, ко- гда проектировщики не работают над общей системой целей проекта. Цели проекта должны содержать следующую основную информацию [Н]: 1) ориентировочную стоимость каждого процесса и стоимостные ограничения; 2) календарный план работы над проектом; 3) цели каждого процесса тестированиям 4) цели в области адаптируемости^ указывающие степень адапти- руемости, или расширяемости, которая должна быть достигнута; 5) вопросы сопровождения создаваемой системы, которые необходи- мо учитывать при разработке; 6) уровни надежности, достигаемые после каждого этапа разработки ПС для достижения заданной надежности продукта; 7) внутренняя документация при работе над проектом; 8) критерии для оценки готовности продукта к использованию. Крайне важно, чтобы цели были четкими, ясными, разумными и из- меряемыми. Цель, которую нельзя понять, бесполезна. Ни в коем случае нельзя скрывать цели от разработчиков программной системы; они должны быть известны всем участникам проекта. Цели должны быть достижимы — исследования классических проектов показали, что раз- вертывание работ с недостижимыми целями часто является главной причиной неудачи. Все цели должны быть сформулированы по возмож- ности в количественных терминах, чтобы можно было оценить, в какой мере в окончательном продукте эти цели достигнуты. Каждая цель должна быть сформулирована достаточно детально, чтобы могли осуществляться процессы проектирования, но не должна предполагать конкретного проектного решения. Должны быть опреде- лены зависимости между целями, чтобы, в случае когда изменяется или не может быть достигнута конкретная цель, проектировщик мог опреде- лить, как это скажется на других целях. Для каждой цели должен быть установлен приоритет (например, по шкале от «абсолютно необходимо» до «хорошо, но не обязательно»), чтобы иметь основу для принятия компромиссных решений. Очень полезный прием - перечислить в документации конкретные «не цели» вместе с целями. Это предотвращает многие случаи ошибоч- ных «предположений» или «чтения между строк», явно показывая, что именно рассматриваемая система не предполагает делать. Разработанные цели проекта должны пройти оценку. Правило <cV — плюс минус - один» предполагает привлечь к оценке целей автора тре- бований и проектировщика исходных внешних спецификаций [11]. По- 189
скольку, однако, цели - самый важный аспект проекта, необходимо уча- стие в их оценке дополнительных сил. К оценке должны быть привлече- ны полномочные представители пользователей, а также те лица, кото- рые будут заниматься проектированием, тестированием, сопровождени- ем системы и подготовкой публикаций. Первый шаг — сопоставление целей с требованиями, чтобы убедить- ся в том, что все требования правильно переведены на язык целей. Каж- дая цель должна оцениваться с учетом правил, изложенных выше. Так как этот вопрос очень важен, каждая из целей должна быть лично оце- нена представителями нескольких уровней руководства - как в органи- зации-пользователе, так и организации-разработчике. Литература к главе 4 1. IBM Rational RequisitePro [Электронный ресурс] // URL: http://www. interface.ru/home. asp?artld=311 2. IEEE Standard Glossary of Software Engineering Terminology [Элек- тронный ресурс] I I URL: http://www.andaim.ru/computer_engineering/ 10363-1132.html 3. RuGost — Разработка документации по ГОСТ [Электронный ре- сурс] // URL: http://www.rugost.com/ 4. Байкин А., Новичков А. Пять уровней зрелости требований [Элек- тронный ресурс] // URL: http://www.ibm.com/developerworks/ru/library/r- requircments/index.html#autor 1 5. Боэм Б. У. Инженерное проектирование программного обеспече- ния: пер. с англ. — М.: Радио и связь, 1985. - 512 с. 6. Брукс Ф.П. Как проектируются и создаются программные ком- плексы. Мифический человеко-месяц: пер. с англ. - М.: Наука, 1970. 7. Буч Г., Рамбо Д., Якобсон И. Язык UML: Руководство пользовате- ля: пер. с англ. - 2-е изд. — М.: ДМК Пресс, 2007. - 496 с. 8. Вали У., Лейбович Л., Превост Э. и др. Управление бизнес-про- цессами: от моделирования до мониторинга с использованием продук- тов WebSphere V6.: пер. с англ. [Электронный ресурс] // URL: http:// www.ibm.com/redbooks 2007. - 423 с. 9. ГОСТ Р ИСО 9000 ГОСТ Р ИСО 9000 [Электронный ресурс] // URL: http://urvista.ru/certification/sert-info/sert-stand/iso-9000-standard- series/iso-9000-standard/?_openstat=ZGlyZWN0LnlhbmRle- C5ydTs0MzAwODg2OzUwNjAzNTU2O31hbmRleC5ydTpwcmVtaXVt 10. Кватрани T.f Палистрает Д. Визуальное моделирование с помо- щью IBM Rational Software Architect и UML: пер. с англ. - М.: КУДИЦ- ПРЕСС.-2007.- 192 с. 11. Майерс Г, Надежность программного обеспечения: пер. с англ. — М.: Мир, 1980.-360 с. 12. Назаров С.В. Операционные системы специализированных вы- числительных комплексов: теория построения и системного проектиро- вания. — М.: Машиностроение, 1989. - 400 с. 190
13. Назаров С.В. Проектирование архитектуры программных систем. Разработка, анализ и управление требованиями: сборник научных тру- дов по материалам международной научно-практической конференции «Перспективные инновации в науке, образовании, производстве и транспорте ’2010». Т. 3: Технические науки. — Одесса: Черноморье, 2010. С. 27—42. 14. Назаров С.В. Сложность программной системы: сборник науч- ных трудов по материалам международной научно-практической конфе- ренции «Современные направления теоретических и прикладных иссле- дований ‘2011», Т. 2: Технические науки. — Одесса: Черноморье, 2011. С. 58-65. 15. Новичков А. Роль процесса управления требованиями при разра- ботке сложных программных систем. Практика применения методоло- гии IBM RUP и инструмента IBM Rational RequisitePro [Электронный ресурс] // URL: http://www.cmcons.com/articles/upravlenic_trebovanijami- _instrument_ibm_rational_r/ 16. Новичков А., Карабанова Б. Моделирование бизнес-процессов ав- томатизируемой предметной области при помощи диаграмм деятельно- сти (activity diagram) с использованием RSA [Электронный ресурс] // URL: http://www.ibm.com/developerworks/ru/library/r-rsa/index.html 17. Полис Г., Огастин Л., Лоу К, Мадхар Д. Разработка программ- ных проектов на основе Rational Unnified Process (RUP). — M.: Бином- Пресс, 2009. - 256 с. 18. Тавассоли Д. Управление требованиями. Десять шагов на пути к совершенству [Электронный ресурс] // URL: http://www.swd.ru/files/ pdf/IBM_uspch_edit l.pdf 19. Терехов А.Н Технология программирования: учебное пособие. — М.: Университет информационных технологий; БИНОМ; Лаборатория знаний, 2006. - 148 с. 20. Уровни требований к программному обеспечению [Электронный ресурс] // URL: http://www.atis.ru/Docltem.aspx7groupld_ 10= 8&itemld_ 10=15 21. Фасбиндер М. Новые возможности версии WebSphere Business Modeler V6.2 [Электронный ресурс] И URL: http://www.ibm.com/ developerworks/ru/library/wes-0812_fasbinder4/#author 1 22. Шураков В.В. Надежность программного обеспечения систем об- работки данных: учебник для вузов. — М.: Статистика, 1981. - 216 с.
Глава 5 РАЗРАБОТКА ПРЕДВАРИТЕЛЬНОГО ВНЕШНЕГО ПРОЕКТА 5.1. Представление и анализ требовании 5.1.1. Требования в V-модели Халла Жизненно важная часть проектирования любых систем — это разра- ботка требований, которые в первую очередь определяют саму предмет- ную область, а затем последовательно соотносят все последующие тех- нические решения с конкретными проблемами из этой предметной об- ласти [29]. Требования являются основой для любого проекта. Они оп- ределяют те потребности заинтересованных сторон (пользователей, по- требителей, поставщиков, разработчиков и самого бизнеса), которые яв- ляются для них необходимыми, а также тот функционал, которым сис- тема должна впоследствии обладать, чтобы удовлетворить эти потреб- ности. Как отмечалось выше, чтобы стать всем понятными, требования в большинстве случаев пишутся на обычном языке, что привносит про- блемы другого рода: необходимость полностью и однозначно обозна- чить проблемы и зафиксировать потребности без использования про- фессионального жаргона или предварительных договоренностей. Это весьма сложная задача. Только согласованные требования могут быть основой для проекта, однако с течением времени у заинтересованных сторон может становиться все больше и больше требований, причем ин- тересы сторон могут вступать в конфликт между собой. Кроме того, по- требности могут быть нечетко выражены в начале проекта, их удовле- творение может быть ограничено факторами, лежащими в неконтроли- руемой области, на удовлетворение потребностей могут влиять другие цели проекта, которые, в свою очередь, тоже могут меняться с течением времени. Согласованные требования обеспечивают базу для планирова- ния разработки системы и ее приемки по завершении работ. Можно ли дать оценку влияния предполагаемого изменения без де- тальной проработки модели системы? С другой стороны, что будет, ес- ли отменить ранее внесенные изменения? Даже если проблема и ее ре- шение определены, необходимо оценить риски, которые могут привести к провалу проекта. Организация работы с требованиями позволяет управлять рисками на самых ранних стадиях разработки. Например, ес- ли риск, вытекающий из определенного требования, может быть отсле- жен, может быть проведена оценка его влияния, вероятность появления, и, как следствие, может быть разработан предварительный план по пре- дотвращению и устранению последствий этого риска. Таким образом, требования являются базой для планирования проек- та, управления рисками, приемочного тестирования, установления ком- промиссов (согласований) и управления изменениями. Анализ требований включает [2]: • обнаружение и разрешение конфликтов между требованиями; 192
• определение границ задачи, решаемой создаваемым программным обеспечением; в общем случае - определение границ и содержания программного проекта; • детализацию системных требований для установления программ- ных требований. В S WEB ОК [2] отмечается, что традиционный взгляд на анализ тре- бований часто сфокусирован или уменьшен до вопросов концептуально- го моделирования с использованием соответствующих аналитических методов, одним из которых является SADT - Structured Analysis and Design Technique (методология структурного анализа и техники проек- тирования), известный по нотациям 1DEF0 (функциональное моделиро- вание— стандарт IEEE 1320.1), 1DEF1X (информационное моделирова- ние— стандарт IEEE 1320.2, известный также как IDEFObject), часто применяемым для моделирования как бизнес-процессов, так и структур данных, в частности реляционных баз данных. Не следует считать, что разработка требований — это единичная фаза, которая выполняется в начале разработки продукта. Очевидно, что тре- бования, разработанные в начале проекта, так или иначе используются на его самом последнем этапе (рис. 5.1). Классическая К-модель [29], описывающая различные стадии проекта, в основном базируется на свя- зи между требованиями и их тестированием. Пользовательские требования определение результата для заинтересованных сторон, приемка продукта Приемочные тесты определение того что система должна делать, проверка системы Системные требования Требования к подсистемам зптимиззция затрат/лользы, проверка требований Интеграционные тесты \ определение реализации требований. \ проверка компонентов Требования для компонентов Системные тесты Модульные тесты Рис. 5.1. Классическая И-модель Халла К-модель отображает также системную разработку в терминах уров- ней, где каждый уровень соотносится с определенным этапом разработ- ки. Несмотря на то что на каждом уровне может использоваться не- сколько различных процессов, основной принцип работы с требования- ми не меняется. Требования могут выступать также в роли связи между 193
проектами. Это хорошая идея, по мнению Халла [29], потому что орга- низации-разработчики желают: • максимально увеличить использование наработок в разных проек- тах; • управлять семействами сходных продуктов; • использовать программное управление для согласования дейст- вий; • оптимизировать разработку, используя опыт предыдущих проек- тов. Хороший набор пользовательских требований может обеспечить краткое и не техническое описание того, что будет разработано, на уровне, который доступен для понимания руководством. Аналогичным образом системные требования могут представлять собой прекрасное техническое описание проекта. Эти два шага служат основой для всех последующих действий в рамках проекта. Поскольку требования играют такую важную роль в разработке сис- тем, с ними нужно постоянно работать. При внесении изменения необ- ходимо выполнить следующие действия: • принять или отклонить изменение; • согласовать стоимость изменения с заказчиками (поставщиками); • организовать работы по переделке. Основной концепцией, позволяющей проводить анализ влияний та- кого рода, является возможность установления и последующего контро- ля связей между требованиями. Можно утверждать, что управление из- менениями является важной частью работы с требованиями. Способ- ность руководителя управлять проектом в значительной степени зави- сит от качества поставленного процесса работы с требованиями. 5.1.2. Моделирование в определении требований и спецификаций Важно понимать взаимосвязь между разработкой требований и сис- темным моделированием. Употребление термина «моделирование тре- бований» неверно по сути [5]. Моделируется реализация системы, а не требования к ней. Моделирование поддерживает наиболее творческий процесс - разработку реализации системы, выработку конкретных сис- темных решений. Системное моделирование вносит дополнительную формальность в процессы анализа и проектирования. При разработке любой системы часто используются схемы и рисунки, которые помогают наглядно ото- бразить некоторые аспекты разработки. Системное моделирование не только формализует это наглядное представление с помощью диаграмм, выполненных с использованием стандартных нотаций (синтаксиса), но и обеспечивает среду (средства) для понимания и обсуждения идей, свя- занных с процессом разработки [29]. Искусство моделирования, по мнению многих известных ученых и инженеров, является самой творческой частью работы системного раз- работчика. В большинстве случаев модели представляют собой некий 194
визуальный ряд, в котором для отображения информации используются взаимосвязанные диаграммы. Новые методы, такие как объектно ориен- тированные методы моделирования, расширяют концепцию моделиро- вания, однако большинство используемых в них подходов тем не менее базируются на известных и проверенных временем принципах. Хорошая модель — это модель, помогающая общению. Модели нуж- ны для того, чтобы было удобно обсуждать идеи и внутри команды раз- работчиков, и во всей организации в целом, подключая к этому процес- су и все заинтересованные стороны. Модели могут применяться для раз- личных целей и покрывать самые разные аспекты разработки системы. Так, например, одна модель может описывать общую структуру взаимо- действия внутри всей организации, а другая — отображать всего лишь одно конкретное функциональное требование к этой системе. Моделирование дает следующие преимущества: • поощряет использование точно определенной терминологии, одно- значность которой поддерживается в рамках разработки всей системы; • позволяет с помощью диаграмм получить наглядное представле- ние системных спецификаций и архитектуры системы; • позволяет рассматривать различные аспекты взаимодействия сис- темы с различных точек зрения; • поддерживает системный анализ; • обеспечивает возможность подтверждения достоверности некото- рых аспектов поведения системы с помощью динамических моделей; • позволяет постоянно совершенствовать систему посредством уточ- нения архитектуры, поддерживая генерацию тестов и исходного кода; • дает возможность свободного общения различным организациям между собой, используя стандартные нотации. Фиксация требований с помощью инструментов IBM Rational RequisitePro и IBM Rational Software Modeler [1] помогает найти ответы на вопросы «кто?», «что?», «почему?» и «как?» применительно к биз- нес-требованиям [4]. Своевременное создание эффективных бизнес-ре- шений начинается с понимания требований. Хороший анализ требова- ний способен существенно повысить шансы на разработку решений, ус- пешно преодолевающих имеющуюся проблему. Управление требова- ниями требует наличия адекватных связей со средствами, которые осу- ществляют их выполнение. Инструмент Rational RequisitePro предоставляет возможности для фиксации требований, определения требований и управления требова- ниями. Инструмент IBM® Rational® Software Architect поддерживает моделирование на языке UML, что позволяет показать возможности для реализации этих требований. Инструмент RequisitePro интегрирован с инструментом Rational Software Modeler, что позволяет визуализиро- вать требования и соединять элементы модели с требованиями, реали- зуемыми посредством этих элементов. Продукт RequisitePro предоставляет несколько шаблонов требова- ний, которые можно использовать для конфигурирования баз данных 195
требований с необходимыми типами требований в соответствии с по- требностями конкретного проекта. Хотя эти шаблоны весьма полезны, для некоторых пользователей они могут оказаться недостаточно ориентированными на бизнес или не- достаточно общими, чтобы быть применимыми на протяжении всего жизненного цикла разработки программных систем. В результате требо- вания могут оказаться сложны для их применения бизнес-аналитиками вследствие неподходящих типов требований и отношений между ними. Кроме того, это может привести к усложнению связей между базами данных RequisitePro, которые содержат разные типы требований для поддержки приложений на протяжении всего жизненного цикла. Инструмент Rational Software Modeler способен создавать модели на основе применяемых профилей. Эти профили можно использовать в ка- честве UML-расширений для поддержки визуального моделирования требований. Этот инструмент также интегрирован с RequisitePro, что позволяет создавать и связывать требования с помощью различных ин- струментов. В состав Rational Software Modeler включены следующие профили [4]: • профиль Analysis (анализ); • профиль Business Modeling (бизнес-моделирование); • профиль Software Services (бизнес-сервис). Профиль Business Modeling имеет стереотипы Business Goal (бизнес- цель) и Business Service (бизнес-сервис), которые соответствуют типам требований в RUP-шаблонах RequisitePro. Кроме того, существуют ти- пы требований для моделирования разных стилей сценариев примене- ния (use case), в том числе бизнес-сценариев и системных сценариев. Однако эти профили поддерживают моделирование ограниченного чис- ла требований, особенно применительно к бизнес-требованиям. Многие клиенты IBM для моделирования требований создают свои собственные шаблоны RequisitePro и профили UML. Однако это может привести к появлению нестандартных требований и несогласованных отношений между требованиями и средствами их реализации. Более стандартизованный подход к управлению требованиями упрощает инте- грацию бизнес-сервисов. Таким образом, можно ограничиться стандарт- ным шаблоном RequisitePro и соответствующим профилем UML, кото- рый поддерживает возможности управления требованиями. Наличие та- кого стандарта не только расширяет возможности моделирования требо- ваний, но и позволяет разрабатывать инструменты, способные управ- лять требованиями более эффективно за счет способности интерпрети- ровать смысл требований и их отношения с другими артефактами в про- цессе проектирования и создания решения. 5.1.3. Разработка программных систем, управляемая моделями Развитие идеи моделирования для решения отдельных задач привело к новому подходу разработки программного обеспечения — разработке, управляемой моделями (MDD) [22]. Разработка, управляемая мо дел fl- 196
ми, - это такой стиль разработки программ, когда главными артефакта- ми являются модели, а по ним генерируются код и другие прикладные артефакты. В MDD вводится дополнительный критерий, состоящий в том, что модель должна читаться машиной. Возможность чтения ма- шиной — необходимая предпосылка для генерации артефактов. Диаграм- ма, нарисованная на доске, может удовлетворять другим критериям мо- дели, однако до тех пор, пока она не переведена в форму, доступную для машины, ее невозможно использовать в цепочке инструментов раз- работки, управляемой моделями. Программные модели разрабатываются на унифицированном языке моделирования UML. Этот язык предоставляет нотацию и соответст- вующую семантику для моделей программных систем. В UML также есть стандартный метод сериализации в понимаемый машиной формат, что предоставляет возможности для автоматизации. Новая парадигма разработки программного обеспечения поддерживается методами архи- тектуры Model-driven Architecture (MDA) [26]. Этот подход к разработке ПО создан командой Object Management Group (OMG). MDA предоставляет ряд рекомендаций для структурирования специ- фикаций, представленных в качестве моделей, начиная с не зависящей от платформы модели (Р1М), затем при выборе языка программирова- ния, исходя из специфики разработки, и, наконец, при трансформации модели в одну или несколько моделей, определяемых платформой, в рамках которой они реализуются (PSM). Количество платформ может быть любым, например Java™2Platform, Enterprise Edition (J2EE)™, CORBA или .Net, реализованные на одном из общепринятых языков программирования, таких как Java ™, C# или Python. MDA обычно вы- полняется при помощи автоматизированного инструментария, например IBM® Rational® Software Architect. MDD-разработка, основанная на MDA, в основном занимается трансформацией моделей и генерацией кода (рис. 5.2). Основанный на создании кода подход, используемый MDD, имеет свои недостатки, связанные с ограничениями в сгенерированном коде, низкой компетентностью разработчиков и жесткой привязкой к кон- кретной модели. Если компания полностью полагается на автоматиче- скую генерацию кода, она оказывается ограниченной в возможностях тонкой настройки кода, особенно когда разработчикам приходится все изменения вносить только через изменение модели. Разработка на основе шаблонов помогает решить эту проблему. Шаблон - это решение периодически возникающей проблемы в рамках определенного контекста. Шаблоны инкапсулируют время, труд и зна- ния разработчика, однажды уже понадобившиеся для решения пробле- мы. После многократного использования в различных проектах шаблон может приобрести статус эталона. 197
Create the domain model Transformation Create the use-case model Traceability Create the design model Рис. 5.2. Трансформация моделей в технологии MDA Complete the implementation using UML visualization Используя шаблон как отправную точку в проектировании, разра- ботчики могут более гибко контролировать генерируемый код, который не ограничен, так как основан на абстрактной модели. Кроме того, MDD может автоматизировать реализацию шаблонов на основе трансформа- ций, что позволит исключить повторение операций низкоуровневой раз- работки и отразить в коде опыт разработчиков, что должно повысить со- вместимость и удобство обслуживания. Для применения рассмотренного подхода требуется интегрирован- ная среда разработки (IDE) с поддержкой следующих возможностей [26]: • моделирования на основе UML; • инфраструктуры шаблонов; • функций трансформации модели и генерации кода; • инструмента проектирования и разработки для конкретной плат- формы, а также среды для тестирования элементов. Выше уже не раз упоминался инструмент Rational Software Architect, который обеспечивает все эти возможности. Это интегрированное сред- ство проектирования и разработки, которое использует преимущества UML-разработки на основе моделей, позволяя создавать приложения и сервисы с практичной архитектурой. Использование UML-моделей в разработке ПО широко применялось и до появления MDD. Отличие заключается в том, что в MDD модели используются не как проекты или наброски, а как исходные артефакты, из которых путем трансфор- 198
маций генерируются эффективные реализации. В MDD подробно опи- санные предметно-ориентированные модели приложения играют глав- ную роль при разработке новых компонентов ПС. Код и другие арте- факты области назначения генерируются спомощью трансформаций, в разработке которых участвуют как специалисты по моделированию, так и специалисты в конкретной предметной области. В [26] приведен пример (рис. 5.3), как с помощью MDD коммерче- ская проблема преобразовывается в решение ПО. Бизнес-проблема ана- лизируется, а затем применяются общие бизнес-шаблоны. Таким обра- зом, модель разработки частично заполняется, в ней появляются детали конкретной разрабатываемой бизнес-функции. Далее внедряются неза- висящие от платформы шаблоны разработки для трансформации модели разработки в ряд не зависящих от рабочего цикла компонентов. После этого выбирается исполняемая платформа, и специфические для ее ра- бочего цикла шаблоны применяются для генерации артефактов. 5.2. Анализ требований и определение спецификаций. Структурный подход 5.2.1. Спецификации Слово «спецификация» буквально означает «описание» или «получе- ние описания», а «специфицировать» значит «описывать» [27]. Отме- тим, что в Единой системе программной документации (ЕСПД) специ- фикацией называется совсем другая вещь — перечень документов, отно- сящихся к программе (ГОСТ 19.202-78). Вместо термина «специфика- ция» и наряду с ним часто употребляют термин «требования» (re- quirements). Иногда эти термины различают. В [9] «соглашением о тре- бованиях» называется документ, содержащий «описание программного изделия, цели, стратегию и тактику его создания» (в него входят две спецификации — внешняя и внутренняя). Полный набор спецификаций будем обозначать SPs, причем в соот- ветствии с выражением (4.1) он может быть получен в результате преоб- разований требований ТТ?5, предъявляемых к ПС, по некоторой процеду- ре 77?з в соответствии с выражением TR, SP или SP = PR. (TR,). (5.1) Внешние спецификации будем обозначать SP*9 внутренние обозна- чим как Таким образом, SPs =8Р^8Р™. (5.2) Внешние спецификации обращены к внешнему пользователю, заказ- чику, потребителю программы, внутренние спецификации обращены к внутреннему пользователю, т.е. к разработчику программы. В методе формализованных технических заданий предусматривается ряд проме- 199
жуточных спецификаций. Благодаря такому положению спецификаций в разработке программ так называемые языки проектирования программ и языки разработки программ обычно либо оказываются языками спе- цификаций, либо включают в себя такие языки. Рис. 5.3. Трансформация моделей на основе шаблонов Вопрос в том, какая информация о задаче и в какой форме должна содержаться в спецификации. Многие авторы полагают, что специфика- ция, в отличие от программы, должна говорить, что надо сделать, а не как это делать, т.е. подчеркивают непроцедурный характер специфика- ций. При этом часто отмечают, что спецификация не должна содержать деталей реализации, навязывать программисту ту или иную реализацию. Однако обязана ли спецификация быть всегда непроцедурной — вопрос дискуссионный, так как задача может быть процедурной по своей при- роде. Кроме того, непроцедурность и свобода от деталей реализации - вещи разные, так как процедурность может быть высокого уровня, дале- кой от реализации. Другое часто упоминаемое желательное или обязательное свойство спецификации называют формальностью, однозначностью или точно- стью, причем диапазон требований здесь весьма широк - от полностью формализованного описания до слегка формализованного. Описание 200
«на естественном языке» обычно считается неудовлетворительным, но какие формальные средства или средства достижения точности следует использовать в спецификациях — вопрос тоже дискуссионный. Еще одно отличительное свойство спецификаций называют понят- ностью^ ясностью или читабельностью. Точность и понятность являют- ся двумя основными свойствами спецификации, причем второе должно отличать типичную спецификацию от типичной программы. В общем случае спецификация должна быть более понятным описанием задачи, чем программа, ее должно быть легче написать и легче прочитать. Од- нако важно отметить относительность понятия спецификации: имея де- ло с одной и той же задачей, одни люди в данной обстановке (включаю- щей их знания, языки и т.д.) будут склонны считать спецификацией од- но, а другие люди в другой обстановке - совсем другое. Понятность спецификации, легкость ее написания и чтения зависят от того, какими знаниями и средствами описания располагает данный человек. Третье, основное и часто неявно подразумеваемое, свойство специ- фикации - это полнота описания задачи: ничто существенное не долж- но быть упущено. Следует отличать спецификацию от менее полного и точного, более эскизного и предварительного описания, которое бу- дем называть требованиями к программе. Они упоминаются в ЕСПД (ГОСТ 19.201—78) в качестве раздела технического задания. То, что на- зывается формализованным техническим заданием, соответствует поня- тию спецификации. Между требованиями и внешней спецификацией помещаются еще цели программы и проекта. Таким образом, спецификацией является достаточно точное и достаточно полное описание задачи, которое чело- веку, участвующему в решении, легче написать, понять и прочитать, чем программу решения этой задачи на доступном ему языке програм- мирования. Средства спецификаций — это любые средства получения или построения таких описаний, а язык спецификаций - это рациональ- но организованный и синтаксически оформленный набор таких средств. Фактически языком спецификаций часто называют язык более высо- кого уровня, чем традиционный язык программирования. Если язык спецификаций реализован так, что спецификации можно выполнять на машине, то он одновременно является и языком программирования. Од- нако эффективность реализации такого языка спецификации, скорее всего, гораздо ниже, чем обычного языка программирования, и ниже, чем обычно требуется в производственных условиях. В языке специфи- кации ценна не эффективность реализации, а эффективность выраже- ния, высокая выразительность. Первые языки спецификаций, или языки проектирования программ, появились, по-видимому, в конце 60-х — начале 70-х гг. XX в. Языки де- лятся на два основных класса: графические и текстовые. В первом глав- ную роль играет представление в виде графов и диаграмм (например, Н1РО, LOGOS и SA). Во втором классе спецификации представляют со- бой тексты, последовательности символов (например, псевдокод, PSL и PDL) и делятся на три подкласса в зависимости от того, как в них ис- 201
пользуется естественный язык: неограниченно, с ограниченным синтак- сисом и семантикой, с дополнительной символикой. Выделяются такие классы языков [27]: • языки описания подсистем обработки данных (например, AXES и SA); • языки для детального описания (например, алгебраические специ- фикации абстрактных типов данных) и языки, ориентированные на опи- сание архитектуры; • языки, ориентированные на данные (например, HIP О и PSL); • языки, ориентированные на управление (например, LOGOS и RSL); • языки для общего описания системы в целом (называемые языка- ми описания заданий); • языки для описания конкретного функционирования на уровне подсистем (называемые языками описания проектов). Ранее были выделены три класса спецификаций и, соответственно, средства их построения: алгебраические, аксиоматические, модельные. Представляет интерес рассмотрение в основном модельных специфика- ций, т.е. средств явного построения математических моделей. Их можно упорядочить в виде следующих классов: таблицы; равенства и системы подстановок; логические средства; графы, сети и диаграммы; математи- ческие структуры и модули, типы, схемы и фреймы; операции, проце- дурные средства; средства именования; средства описания исключи- тельных и ошибочных ситуаций. Языки спецификаций делятся на два класса: универсальные языки, или языки общего назначения, и специализированные языки. Язык мо- жет быть специализированным в двух разных смыслах: • ориентирован на определенную предметную или проблемную об- ласть и может использовать разные и разнородные средства (например, язык описания АСУ в конкретной отрасли); • основан на определенном средстве, методе описания и может ис- пользоваться в разных областях (например, язык, основанный на табли- цах, или язык, основанный на равенствах). Для каждого из классов средств можно указать специализированный во втором смысле язык, основанный на средствах этого класса. Хотя не- редко определенная область связана с узкоспецифическими средствами, и тогда специализация по области совпадает со специализацией по сред- ствам, представляется уместным различать эти два вида специализации. Классов предметно- или проблемно-ориентированных языков, в прин- ципе, может быть так же много, как предметных или проблемных облас- тей. Среди них прежде всего следует выделить языки, ориентированные на управление, языки, и ориентированные на структуры данных. К первым относятся языки описания систем реального времени (или оперативной обработки информации), асинхронных и параллельных систем, ко вторым — языки описания задач обработки данных относи- тельно сложной структуры. Во вторых, в отличие от первых, основную роль играет структура обрабатываемых данных, а не взаимодействие 202
процессов обработки, В принципе, можно представить себе задачи, объ- единяющие черты задач обоих этих классов, но реально языки специа- лизируется либо в ту, либо в другую сторону. Кроме этих двух классов, особый интерес для программирования представляют задачи проектиро- вания трансляторов и задачи проектирования баз данных (или систем управления базами данных). Языки последнего класса тоже ориентиро- ваны на данные, как и языки упомянутого выше класса, но существенно иначе. Все остальные предметно- и проблемно-ориентированные язы- ки - это, по существу, многочисленные языки разнообразных пакетов или систем, прикладных программ. Перейдем теперь к рассмотрению отдельных классов средств специ- фикации и языков, специализированных по средствам. Очень удобным средством спецификаций являются таблицы. Одной из черт хорошо ор- ганизованной программы является легкость ее модификации. Эта лег- кость тесно связана с применением систематического подхода при по- строении программы. Часто о наличии такого подхода говорит исполь- зование таблиц, потому что, как правило, элементы таблиц представля- ют собой меняющиеся значения параметров, которые могут быть ис- пользованы общим участком программы в различных ситуациях. Чтобы написать программу, которая должна быть легко видоизме- няемой, можно выделить те аспекты задачи, которые подвержены изме- нениям, и сделать их элементами легко изменяемых таблиц - объектов, состоящих из конечного числа элементов, образующих строки и столб- цы. Строки и (или) столбцы имеют имена или номера (индексы), и поло- жение элемента в таблице однозначно определяется указанием столбца и строки. Таблицы решений состоят из условий, данных и действий, т.е. из ос- новных элементов всех программ, и поэтому могут использоваться как гибкий инструмент для описания не только данных, но и значительной части логики любой программы. Таблицы используются обычно для описания конечных функций, конечных отношений, табличных струк- тур данных (например, матрицы в алгебре, двумерные массивы в языках программирования, документы в сфере административно-хозяйственно- го управления и др.). В табличном описании конечной функции F(Xj, Хп) строка (или столбец) таблицы состоит из значений аргументов и соответствую- щего значения функции. Имена столбцов (или, соответственно, строк) — это имена аргументов и самой функции. Язык таблиц решений, очевид- но, специализирован по средствам и может применяться в самых раз- личных областях. Кроме того, его можно считать языком программиро- вания, так как таблицы решений легко и эффективно реализуются. Средства описания конечных функций являются существенной частью ряда универсальных языков спецификаций, например CIP-L и VDM, где определены специальные операции над конечными функциями. В табличном описании конечного отношения R(Xh Хр .,.,Х„), где Л,, Хр Х„ — имена компонентов (аргументов или областей) отношения, строкой таблицы является кортеж элементов. Конечные отношения иг- 203
рают главную роль в языках описания и манипулирования данными ре- ляционных баз данных. В некоторых реляционных языках определен ряд операций над отношениями. Табличные структуры данных, не ин- терпретируемые явно как функции или отношения, используются в про- граммировании давно — в частности, в форме двумерных массивов мно- гих языков программирования. По существу, одним из первых языков спецификации является РПГ, в основе которого лежит табличное описа- ние задач генерации отчетов. Матрицы и операции над ними использу- ются, естественно, при спецификации пакетов программ для задач ли- нейной алгебры, а также в языке АПЛ. Разного рода таблицы — одна из основных форм представления данных в языках, ориентированных на задачи обработки административных и экономических документов. Это делает таблицы удобным средством специфицирования очень многих задач. 5.2.2. Структурный подход представления спецификаций В структурном подходе используются в основном две группы средств, описывающих функциональную структуру системы и отноше- ния между данными. Каждой группе соответствуют определенные виды моделей (диаграмм), наиболее распространенными среди которых явля- ются [7, 8]: • DFD (Data Flow Diagrams) - диаграммы потоков данных; • SADT (Structured Analysis and Design Technique - метод структур- ного анализа и проектирования) — модели и соответствующие функцио- нальные диаграммы; • ERD (Entry — Relation Diagrams) — диаграммы «сущность — связь». Методологии структурного анализа и проектирования, основанные на моделировании потоков данных, обычно используют комплексное представление программного обеспечения в виде совокупности этих мо- делей, дополненных спецификациями процессов и словарем терминов. Взаимосвязь элементов такой обобщенной модели показана на рис. 5.4, Конкретный вид диаграмм и интерпретация их конструкций зависят от стадии жизненного цикла программной системы. На стадии форми- рования требований к программной системе SADT и DFD используются для построения моделей AS IS и ТО BE, отражая, таким образом, суще- ствующую и предлагаемую структуры бизнес-процессов, которые необ- ходимо автоматизировать с помощью программной системы. Использо- вание SADT-моделей, как правило, ограничивается только данной ста- дией, поскольку они первоначально не предназначались для проектиро- вания программных систем. С помощью ERD-диаграмм выполняется описание используемых в разрабатываемой программной системе дан- ных на концептуальном уровне, не зависящем от средств управления базами данных (СУБД). На стадии проектирования программной системы используются DFD-диаграммы, описывающие взаимодействия источников и потреби- телей информации через процессы, которые должны быть реализованы 204
в системе. Таким образом, они позволяют представлять структуру про- ектируемой программной системы и при этом могут уточняться, расши- ряться и дополняться новыми конструкциями. Аналогично ЕRD-диа- граммы уточняются и дополняются новыми конструкциями, описываю- щими представление данных на логическом уровне, пригодном для по- следующей генерации схемы базы данных. Контекстная программа Процесс Детализирующая диаграмма потоков данных Управляющий процесс Процесс Хранилище Спецификация процесса Словарь терминов Диаграмма Сущность- Связь Диаграмма переходов состояний Рис. 5.4. Комплексное представление программного обеспечения в виде совокупности моделей DFD, SADT и ERD 5.2.3. Метод функционального моделирования Метод SADT разработан Д. Россом (SoftTech, Inc) в 1973 г. [19]. Дан- ный метод успешно использовался в военных, промышленных и ком- мерческих организациях США для решения широкого круга задач, та- ких как долгосрочное и стратегическое планирование, автоматизирован- ное производство и проектирование, разработка программного обеспе- чения для оборонных систем, управления финансами и материально- техническим снабжением и др. Метод SADT поддерживается Министерством обороны США, кото- рое было инициатором разработки стандарта 1DEF0 (Icam DEFination) — подмножества SADT, являющегося основной частью программы 1САМ (Integrated Computer Aided Manufacturing — интегрированная компьюте- ризация производства), проводимой по инициативе ВВС США. Методо- логия SADT представляет собой набор методов, правил и процедур, предназначенных для построения функциональной модели объекта ка- кой-либо предметной области. Функциональная модель SADT отображает функциональную струк- туру объекта, т.е. производимые им действия и связи между этими дей- 205
ствиями. Основные элементы этой методологии основываются на сле- дующих концепциях [8, 20]: • графическое представление блочного моделирования (на SADT- диаграмме функции представляются в виде блока, а интерфейсы входа- выхода - в виде дуг, входящих и выходящих из него); • отображение взаимодействия функций друг с другом посредством определенных интерфейсных дуг; • следование правилам, определяющим строгость и точность ото- бражения. Правила SADT включают: • уникальность меток и наименований; • ограничение количества блоков на каждом уровне детализации; • синтаксические правила для графики; • связность диаграмм; • отделение организации от функции; • разделение входов и управлений. Методология SADT может использоваться для моделирования и раз- работки широкого круга систем, удовлетворяющих определенным тре- бованиям и реализующих требуемые функции. В уже разработанных системах методология SADT может быть использована для анализа вы- полняемых ими функций, а также для указания механизмов, посредст- вом которых они осуществляются. Диаграммы — главные компоненты модели, все функции программ- ной системы и интерфейсы представлены на них как блоки и дуги. Ме- сто соединения дуги с блоком определяет тип интерфейса. Дуга, обозна- чающая управление, входит в блок сверху, в то время как информация, которая подвергается обработке, представляется дугой с левой стороны блока, а результат обработки — дугами с правой стороны. Механизм (че- ловек, автоматизированная система или другой объект), который осуще- ствляет операцию, представляется в виде дуги, входящей в блок снизу (рис. 5.5). Блоки на диаграмме размещают по «ступенчатой» схеме в соответст- вии с последовательностью их работы или доминированием, которое понимается как влияние, оказываемое одним блоком на другие. В функ- циональных диаграммах SADT различают пять типов влияния блоков друг на друга [8]: • вход-выход блока подается на вход блока с меньшим доминирова- нием, т.е. следующего блока (рис. 5.6 а)); • управление: выход блока используется как управление для блока с меньшим доминированием (рис. 5.6 б)); • обратная связь по входу. Выход блока подастся на вход блока с большим доминированием (рис. 5.6 в)); • обратная связь по управлению: выход блока используется как управляющая информация для блока с большим доминированием (рис. 5.6 г)); • выход-исполнитель: выход блока используется как механизм для другого блока (рис.5.6 д)). 206
Управление Вход Выход ------> Функция ---------> Механизм Рис. 5.5. Структура блока На рис. 5.7 приведены четыре диаграммы и их взаимосвязи, показы- вающие структуру SADT-модели. Каждый компонент может быть дета- лизирован на другой диаграмме. Детальная диаграмма иллюстрирует внутреннее строение блока родительской диаграммы. Рис. 5.6. Типы взаимосвязей блоков Построение SADT-модели начинается с представления всей системы в виде простейшего компонента — одного блока и дуг, изображающих интерфейсы с внешними по отношению к данной системе функциями. Имя блока является общим для всей системы. Затем блок, представляю- щий систему в качестве единого модуля, детализируется на другой диа- грамме с помощью нескольких блоков, соединенных интерфейсными дугами. Каждый блок детальной диаграммы представляет собой под- функцию, границы которой определены интерфейсными дугами. Каж- дый из блоков детальной диаграммы может быть также детализирован на следующей в иерархии диаграмме (рис. 5.7). На каждом шаге деком- 207
позиции более общая диаграмма называется родительской по отноше- нию к детальной диаграмме. Во всех случаях каждая подфункция может содержать только эле- менты, которые входят в исходную функцию. Модель не может опус- тить какие-либо элементы, т.е. родительский блок и его интерфейсы обеспечивают контекст. К нему нельзя ничего добавить, и из него ниче- го не может быть удалено. Дуги, входящие в блок и выходящие из него на диаграмме верхнего уровня, являются теми же, что и дуги, входящие в диаграмму нижнего уровня и выходящие из нее, поскольку блок и диаграмма изображают одну и ту же часть системы. Рис. 5.7. Детализация структуры в иерархии диаграмм На рис. 5.8 представлены различные варианты выполнения функций и соединения дуг с блоками. Некоторые дуги присоединены к блокам диаграммы обоими концами, у других же один конец остается не при- соединенным. Неприсоединенные дуги соответствуют входам, управле- 208
ниям и выходам родительского блока. Источник или получатель этих пограничных дуг может быть обнаружен только на родительской диа- грамме. Все граничные дуги должны продолжаться на родительской диаграмме, чтобы она была полной и непротиворечивой (рис. 5.9). Функцииблоков А2 и АЗ могут выполняться параллельно Рис. 5.8. Варианты выполнения функций На SADT-диаграммах не указывается явно ни последовательность операций, ни время их выполнения. Обратные связи, итерации, продол- жающиеся процессы и перекрывающиеся (по времени) функции могут быть изображены с помощью дуг. Обратные связи могут выступать в виде комментариев, замечаний, исправлений и т.д. (рис. 5.10). Соответствие — интерфейсных дуг родительской и детальной диаграмм Рис. 5.9. Соответствие дуг в иерархии диаграмм Рассмотрим пример разработки функциональной диаграммы с целью уточнения спецификаций программы сортировки одномерного массива с использованием нескольких методов. Диаграмма, представленная на рис. 5.11 а), является диаграммой верхнего уровня. Она иллюстрирует исходные данные программы и ожидаемые результаты. Диаграмма на рис. 5.11 б) детализирует функции программы. На ней показаны три блока: «Меню», «Сортировка», «Вывод результата». Для каждого блока определены исходные данные, управляющие воздейст- 209
вия и результаты. На детализирующей диаграмме определены следую- щие обозначения: II - размер массива, 12- массив, С1 - выбор метода, R1 - вывод описания метода, R2 — вывод отсортированного массива. Системные требования Улучшенный проект Рис. 5.10. Представление обратных связей Одним из важных моментов при моделировании программных сис- тем с помощью метода SADT является точная согласованность типов связей между функциями. В [7] предлагается различать семь типов свя- зей между блоками (функциями): случайную, логическую, временную, процедурную, коммуникационную, последовательную и функциональ- ную. При этом считается, что три последних вида связей являются пред- почтительными для диаграмм хорошего качества. Однако такой подход плохо согласуется с применением метода SADT к структурному проек- тированию программных систем. Дело в том, что блок SADT-диаграм- мы в этом случае отождествляется с программным модулем. Известно, что сцепление модулей в программной системе представ- ляет собой меру относительной независимости модулей, которая опре- деляет их читабельность и сохранность. Независимые модули могут мо- дифицироваться без переделки других модулей. Слабое сцепление мо- дулей более желательно, так как это означает высокий уровень их неза- висимости. В то же время в методологии структурного проектирования про- граммных систем используется понятие связности модуля (внутренней связности его частей). Чем выше связность модулей, тем лучше резуль- тат проектирования [12, 18, 28] . Именно для оценки связности модуля и применяется семь видов внутренней связи между частями модуля, ко- торые в [7] приведены для характеристики межблочных связей в SADT- диаграммах. То, что в [7] понимается под функциональной связью, на 210
самом деле является связью по управлению - в понимании, принятом в публикациях [12, 18, 28]. Рис. 5.11. Пример функциональной диаграммы, специфицирующей сортировку одномерного массива Учитывая все выше сказанное, в SADT-диаграммах в случае их при- менения для поддержки структурного проектирования программных систем целесообразно использовать для характеристики сцепления (свя- зи) блоков (модулей) следующие меры: независимое сцепление (сла- бое), сцепление по данным, по образцу, по общей области, по управле- нию, по внешним ссылкам, по функциям. Последний вид связи блоков возникает в том случае, когда один блок обращается к внутренним функциям другого блока без обращения к его точкам входа. В про- граммном смысле это сцепление модулей возникает тогда, когда для од- ного модуля доступны внутренние области другого модуля, т.е. два мо- дуля используют общий участок памяти с командами. Такое сцепление характерно для случая, когда модули проектируются как отдельные подпрограммы, путь через которые начинается в различных точках вхо- да, но приводит к общему сегменту кодов. 5.2.4. Диаграммы потоков данных Диаграммы потоков данных (DFD) являются основным средством моделирования функциональных требований к проектируемой системе. С их помощью требования представляются в виде иерархии функцио- 211
нальных компонентов (процессов), связанных потоками данных. Глав- ная цель такого представления — продемонстрировать, как каждый про- цесс преобразует свои входные данные в выходные, а также выявить от- ношения между этими процессами. Для построения DFD-диаграмм используются две различные нота- ции, соответствующие методам Иордана и Гейна-Сэрсона, которые не- значительно отличаются графическими изображениями символов. Далее при построении примеров будет использоваться нотация Гейна-Сэрсо- на. Построение DFD-диаграмм в основном ассоциируется с разработкой программных систем, и нотация DFD изначально была разработана для этих целей. В соответствии с данными методами модель системы определяется как иерархия потоков данных, описывающих асинхронный процесс пре- образования информации от ее входа в систему до выдачи пользовате- лю. Диаграммы верхних уровней иерархии (контекстные диаграммы) определяют основные процессы или подсистемы с внешними входами и выходами. Они детализируются при помощи диаграмм нижнего уров- ня. Такая декомпозиция продолжается, создавая многоуровневую иерар- хию диаграмм, до тех пор, пока не будет достигнут такой уровень де- композиции, на котором процессы становятся элементарными, так что детализировать их далее невозможно. Источники информации (внешние сущности) порождают информа- ционные потоки (потоки данных), переносящие информацию к подсис- темам или процессам. Те, в свою очередь, преобразуют информацию и порождают новые потоки, которые переносят информацию к другим процессам или подсистемам, накопителям данных или внешним сущно- стям — потребителям информации. Основными компонентами DFD-диаграмм являются: • внешние сущности; • системы и подсистемы; • накопители данных; • потоки данных. Внешние сущности представляют собой материальные объекты или физическое лицо, являющее собой источник или приемник информации, например заказчика, персонала, поставщика, клиента, склад. Определе- ние некоторого объекта или системы в качестве внешней сущности ука- зывает на то, что они находятся за пределами границ анализируемой системы. В процессе анализа некоторые внешние сущности могут быть перенесены внутрь диаграммы анализируемой системы, если это необ- ходимо, или, наоборот, часть процессов может быть вынесена за преде- лы диаграммы и представлена как внешняя сущность. Внешняя сущность обозначается квадратом (рис. 5.12), расположен- ным как бы над диаграммой и бросающим тень для того, чтобы можно было выделить символ среди других обозначений. При построении мо- дели сложной программной системы она может быть представлена в са- мом общем виде на так называемой контекстной диаграмме в виде од- 212
ной системы как единого целого либо может быть декомпозирована на несколько подсистем (рис. 5.13). Рис. 5.12. Обозначение внешней сущности I Тдентифнкатор -----Поле номера Департамент по работе с пл астико в ыми кар там и Персонал, оборудование Полеимени Поле физической реализации Рис. 5.13. Контекстная диаграмма Номер подсистемы служит для ее идентификации. В поле имени вво- дится наименование подсистемы в форме предложения с подлежащим и соответствующими определениями и дополнениями. Процесс представляет собой преобразование входных потоков дан- ных в выходные в соответствии с определенным алгоритмом. Физиче- ски процесс может быть реализован различными способами: это мо- жет быть подразделение (отдел) организации, выполняющее опреде- ленную обработку входных документов и выпуск отчетов, программа, аппаратно реализованное физическое устройство и т.д. Процесс на диаграмме изображается в виде прямоугольника с закругленными уг- лами (рис. 5.14). Поленомера Полеимени Поле физической реализации Рис. 5.14. Изображение процесса Номер процесса служит для ее идентификации. В поле имени вво- дится наименование процесса в виде предложения с активным недву- 213
с мысленным глаголом в неопределенной форме («вычислить», «прове- рить», «рассчитать», «создать» и т.п.), за которым следует существи- тельное в винительном падеже (рис. 5.14). Использование таких глаго- лов, как «обработать», «модернизировать» или «отредактировать», озна- чает недостаточно глубокое понимание данного процесса и требует дальнейшего анализа. Информация в поле физической реализации пока- зывает, какое подразделение, программа или устройство выполняет данный процесс. Накопитель данных — это абстрактное устройство для хранения ин- формации, которую можно в любой момент поместить в накопитель и через некоторое время извлечь, причем способы помещения и извле- чения могут быть любыми. Накопитель данных (хранилище) может быть реализован физически в форме таблицы в оперативной памяти, файла на магнитном диске, ящика в картотеке и т.д. Накопитель данных на диаграмме идентифицируется буквой D и произвольным числом. Имя накопителя выбирается из соображений наибольшей информатив- ности для проектировщика (рис. 5.15). D8 Реестр налогоплательщиков t Поле Поле имени идентификации Рис. 5.15. Изображение накопителя данных Накопитель данных в общем случае является прообразом будущей базы данных, описание хранящихся в нем данных должно быть увязано с информационной моделью (ERD). Поток данных изображается стрел- кой и описывает передвижение информации от источника к приемнику. Реально это может быть информация, передаваемая по кабелю между двумя устройствами, пересылаемые по почте письма, дискеты, пере- мещаемые с одного компьютера на другой и т.д. Каждый поток имеет имя, отражающее его содержание (рис. 5.16). Сформировать отчетность по подоходному налогу Отдел отчетно сти Отчетно сть по -----------> подоходном у налогу Рис. 5.16. Изображение потоков данных 214
Ссылки на DFD-диаграммах могут быть разбиты (разветвлены) на части, при этом каждый получившийся сегмент может быть переимено- ван таким образом, чтобы показать декомпозицию данных, переноси- мых некоторым потоком (рис. 5.17). Стрелки могут соединяться между собой (объединяться) для формирования комплексных объектов. Напри- мер, чтобы сформировать адрес налогоплательщика, необходимо иметь данные о его элементах (индексе, городе, улице, номере дома и номере квартиры). 2.9 Записать адрес налогоплательщика Отдел учета налогоплательщиков Рис. 5.17. Декомпозиция данных При использовании DFD-диаграмм с целью моделирования функ- циональных требований, предъявляемых к программной системе, для ясности их понимания и удобства работы проектировщика строят ие- рархию диаграмм. При этом целесообразно выполнять следующие реко- мендации [7]: • размещать на каждой диаграмме от 3 до 6—7 процессов; • не загромождать диаграммы несущественными на данном уровне деталями; • декомпозицию потоков данных осуществлять параллельно с де- композицией процессов; • выбирать ясные, отражающие суть дела имена процессов и пото- ков, стараясь не использовать аббревиатуры. Первым шагом при построении иерархии диаграмм потоков данных является построение контекстных диаграмм, показывающих, как проек- тируемая система будет взаимодействовать с пользователями и другими внешними системами (некоторый аналог вариантов использования в объектно ориентированном подходе). При проектировании относи- тельно простых систем достаточно одной контекстной диаграммы, имеющей звездную топологию, в центре которой располагается основ- ной процесс, соединенный с источниками и приемниками информации. Для сложных систем строится иерархия контекстных диаграмм, ко- торая определяет взаимодействие основных функциональных подсистем проектируемой системы как между собой, так и с внешними входными и выходными потоками данных и внешними объектами. При этом кон- 215
текстная диаграмма верхнего уровня содержит набор подсистем, соеди- ненных потоками данных. Контекстные диаграммы следующего уровня детализируют содержимое и структуру подсистем. После построения контекстных диаграмм полученную модель надо проверить на полноту исходных данных об объектах системы и изоли- рованность объектов (отсутствие информационных связей с другими объектами). Для каждой подсистемы, присутствующей на контекстных диаграммах, выполняется ее детализация при помощи DFD-диаграмм. Каждое событие представляется в виде процесса с соответствующими входными и выходными потоками, накопителями данных, внешними сущностями ссылками на другие процессы для описания связей между этим процессом и его окружением. Каждый процесс на DFD-диаграмме, в свою очередь, может быть де- тализирован при помощи DFD или (если процесс элементарный) специ- фикации. При детализации должны соблюдаться следующие правила: • правило балансировки, означающее, что при детализации подсис- темы можно использовать только те компоненты (подсистемы, процес- сы, внешние сущности, накопители данных), с которыми она имеет связь на родительской диаграмме; • правило нумерации, заключающееся в том, что при детализации процессов должна поддерживаться их иерархическая нумерация. Спецификация процесса должна формулировать его основные функ- ции таким образом, чтобы в дальнейшем специалист, выполняющий реализацию проекта, смог выполнить их или разработать соответствую- щую программу. Спецификация является конечной вершиной иерархии DFD. Решение о завершении детализации процесса и использование спецификации принимается аналитиком, исходя из следующих критери- ев [7, 8]: • наличие у процесса небольшого количества входных и выходных данных (2—3 потока); • возможности описания преобразования данных процессом в виде последовательного алгоритма; • выполнение процессом единственной логической функции преоб- разования входной информации в выходную; • возможности описания логики процесса при помощи специфика- ции небольшого объема (не более 20—30 строк). Спецификации должны удовлетворять следующим требованиям: • для каждого процесса нижнего уровня должна существовать одна (и только одна) спецификация; • спецификация должна определять способ преобразования входных потоков в выходные; • нет необходимости (по крайней мере, на стадии формирования требований) определять метод реализации этого преобразования; • спецификация должна стремиться к ограничению избыточности: не следует переопределять то, что уже было определено на диаграмме; • набор конструкций для построения спецификаций должен быть простым и понятным. 216
Фактически спецификация представляет собой описание алгоритмов задач, выполняемых процессами. Спецификации содержат номер и (или) имя процесса, списки входных и выходных данных и тело (описа- ние) процесса, являющееся спецификацией алгоритма или операции, трансформирующей входные потоки данных в выходные. Известно большое количество разнообразных методов, позволяющих описать те- ло процесса. Соответствующие этим методам языки могут варьировать- ся от структурированного естественного языка, или псевдокода, до язы- ков визуального проектирования. Структурированный естественный язык применяется для читабель- ного, достаточно строгого описания спецификаций процессов. Такой язык состоит из подмножества слов, организованных в определенные логические структуры, арифметических выражений и диаграмм. К управляющим структурам языка относятся последовательная конст- рукция, конструкция выбора и итерация (цикл). При использовании структурированного естественного языка приня- ты следующие соглашения: • логика процесса выражается в виде комбинации последователь- ных конструкций, конструкций выбора и итераций; • глаголы должны быть активными, недвусмысленными и ориенти- рованными на целевое действие (например, «заполнить», «вычислить», «извлечь», а не «модернизировать», «обработать»); • логика процесса должна быть выражена четко и недвусмысленно. При построении иерархии диаграмм потоков данных переходить к детализации процессов следует только после определения структур данных, которые описывают содержание всех потоков и накопителей данных. Структуры данных могут содержать альтернативы, условные вхождения и итерации. Альтернатива означает, что в структуру может входить один из перечисленных элементов. Условное вхождение пока- зывает, что данный компонент может отсутствовать в структуре. Итера- ция предусматривает вхождение любого числа элементов из указанного диапазона. Для каждого элемента может указываться его тип (непрерывный или дискретный). Для непрерывных данных могут указываться единица из- мерения, диапазон значений, точность представления и форма физиче- ского кодирования. Для дискретных данных может указываться таблица допустимых значений. После построения законченной модели системы ее необходимо вери- фицировать (проверить на полноту и согласованность). В полной моде- ли все ее объекты (подсистемы, процессы, потоки данных) должны быть подробно описаны и детализированы. В согласованной модели для всех потоков данных и накопителей данных должно выполняться прави- ло сохранения информации: все поступающие куда-либо данные долж- ны быть считаны, а все считываемые данные должны быть записаны. 217
5.2.5. Диаграммы переходов состояний Диаграммы STD (State Transition Diagram) демонстрируют поведение разрабатываемой программной системы при получении управляющих воздействий (извне). В диаграммах такого вида узлы соответствуют со- стояниям системы, а дуги - переходу системы из одного состояния в другое. Узел, из которого выходит дуга, является начальным (проме- жуточным) состоянием, а узел, в который входит, - следующим состоя- нием. Дуга помечается именем входного сигнала или события, вызы- вающего переход, а также сигналом или действием, сопровождающим переход. Условные обозначения, используемые в STD-диаграммах, по- казаны на рис. 5.18 (а) — терминальное состояние, б) - промежуточное состояние, в) - переход). Имя состояния Условие -------> Действие Рис. 5.18. Обозначения на STD-диаграммах На рис. 5.19 приведена диаграмма переходов состояний программы, активно не взаимодействующей с окружающей средой, которая имеет примитивный интерфейс, производит некоторые вычисления и выводит результат. Исходное состояние Состояние завершения Всегда Всегда Всегда инициализация вычисления завершение Рис. 5.19. Пример диаграммы переходов и состояний Другой пример, более сложный, показан на рис. 5.20. Это диаграмма переходов торгового автомата, активно взаимодействующего с покупа- телем. 218
Товар выдан Получить оплату от клиента Возврат монеты Выдать сдачу Рис. 5.20. Диаграммы переходов и состояний торгового автомата 5.3. Анализ требований и определение спецификации при объектном подходе 5.3.1. Общие сведения о языке UML как языке моделирования сложных систем В основе объектного подхода к разработке программных системы лежит объектная декомпозиция, т.е. представление разрабатываемого продукта в виде совокупности объектов, в процессе взаимодействия ко- торых через передачу сообщений происходит выполнение требуемых функций. Впервые объектно ориентированное направление в разработке и дизайне появилось благодаря возможностям компьютерной имитации. Главный принцип компьютерной имитации состоит в том, что програм- ма должна моделировать реальный мир. Самый естественный путь к этому заключается в том, чтобы компьютерная программа оперирова- ла с объектами, являющимися отражением сущностей реального мира, которые моделируют их действия и отражают свойства, которыми они обладают. Системное моделирование вносит дополнительную формальность в процессы анализа и проектирования. В процессе разработки системы очень часто используются схемы и рисунки, которые помогают нагляд- но отобразить некоторые аспекты разработки. Системное моделирова- ние формализует это наглядное представление не только с помощью диаграмм, выполненных с использованием стандартных нотаций (син- таксиса), но и обеспечивает среду (средства) для понимания и обсужде- ния идей, связанных с процессом разработки. На практике не существу- ет единственного правильного решения, поэтому модели меняются и развиваются на протяжении этапов разработки. В большинстве случа- 219
ев модели представляют собой некий визуальный ряд, в котором для отображения информации используются взаимосвязанные диаграммы. Моделирование в процессах разработки систем даст следующие пре- имущества [29]: • поощряет использование точно определенной терминологии, од- нозначность которой поддерживается в рамках разработки всей систе- мы; • позволяет с помощью диаграмм получить наглядное представле- ние системных спецификаций и архитектуры системы; • позволяет рассматривать различные аспекты взаимодействия сис- темы с различных точек зрения; • поддерживает системный анализ; • позволяет подтвердить достоверность некоторых аспектов поведе- ния системы с помощью динамических моделей; • дает возможность совершенствовать систему посредством уточне- ния архитектуры, поддерживая генерацию тестов и исходного кода; • позволяет свободно общаться различным организациям между со- бой, используя стандартные нотации. Объектно ориентированные подходы моделирования существенно отличаются от структурных подходов. Объекты представляют собой ус- тойчивые, повторно используемые компоненты. Объектно ориентиро- ванные подходы направлены на максимизацию повторного использова- ния инженерами объектов при разработке системных требований и спе- цификаций системы. Таким образом, целью объектно ориентированного подхода являются: • инкапсуляция, т.е. заключение внутрь объектов их поведения (со- стояния и событий), информации (данных) и операций; • создание устойчивых объектов, которые могут быть использованы как для разработки требований, так и для разработки спецификаций сис- темы; • добавление информации путем большей детализации уже сущест- вующих объектов; • создание новых объектов путем детализации (конкретизации) су- ществующих объектов, а не создание абсолютно новых. Объектно ориентированные подходы описывают поведение объек- тов и их взаимодействие между собой. Задачей аналитика является на- хождение объектов, которые существуют наибольшее время, и модели- рование поведения системы вокруг этих объектов. Такой подход позво- ляет получить ясное представление о поведении системы. Другая задача состоит в том, чтобы повторно использовать существующие системные элементы: таким образом достигается значительное совершенствование последних. При проектировании программных систем моделирование использу- ется для решения следующих задач [7, 21, 23]: 1) визуализации системы; 2) определения структуры и поведения системы; 3) получения шаблона, позволяющего сконструировать систему; 220
4) документирования принимаемых решений, используя полученные модели. Для решения этих задач при описании поведения проектируемого программного продукта в настоящее время получил широкое распро- странение унифицированный язык моделирования UML [6]. Этот язык создавался как попытка объединить вместе объектно ориентированные подходы, получившие наибольшую поддержку и признание, а именно подходы Буча, ОМТ (техника объектного моделирования) и Обжектори (Objectory), предложенный Якобсоном. В середине 90-х гг. XX в. Буч, Румбах и Якобсон стали вместе работать в компании Rational. Здесь они разработали единый, общий и ныне широко используемый язык мо- делирования. С момента своего появления UML неоднократно подвер- гался доработке и изменениям. В настоящее время в основном исполь- зуется версия UML 2.0, выпущенная в 2003 г. [17]. UML предполагает разработку нескольких моделей, совокупность которых описывает разрабатываемую систему [17, 25, 26]. Каждая мо- дель относится к соответствующему этапу разработки системы и имеет собственное предназначение. При этом каждая из моделей состоит из одной или нескольких UML-диаграмм, которые можно классифициро- вать следующим образом: • структурные диаграммы; • диаграммы поведения; • диаграммы взаимодействия. На рис. 5.21 представлены все типы диаграмм, существующие в но- тации UML 2.3 и доступные для использования в практике разработки программных систем. Часто бывает достаточно использовать из пере- численных лишь небольшой набор диаграмм для моделирования систе- мы. Диаграмма классов (Class Diagram) - статическая структурная диа- грамма, описывающая структуру системы. Она демонстрирует классы системы, их атрибуты, методы и зависимости между классами. Существуют разные точки зрения на построение диаграмм классов в зависимости от целей их применения: • концептуальная точка зрения — диаграмма классов описывает мо- дель предметной области, в ней присутствуют только классы приклад- ных объектов; • точка зрения спецификации - диаграмма классов применяется при проектировании информационных систем; • точка зрения реализации - диаграмма классов содержит классы, используемые непосредственно в программном коде (при использова- нии объектно ориентированных языков программирования). Диаграмма структуры (Composite Structure Diagram) — статическая структурная диаграмма, демонстрирует внутреннюю структуру классов и, по возможности, взаимодействие элементов (частей) внутренней структуры класса. Подвидом диаграмм композитной структуры являют- ся кооперации (Collaboration Diagram, введены в UML 2.0), которые по- 221
называют роли и взаимодействие классов в рамках кооперации. Коопе- рации удобны при моделировании проектирования. Рис. 5.21. Типы диаграмм в нотации UML 2.3 Диаграмма развертывания (Deployment Diagram) служит для модели- рования работающих узлов (аппаратных средств, англ. - node) и артефак- тов, развернутых на них. В UML 2 на узлах разворачиваются артефакты (англ. - artifact), в то время как в UML 1 на узлах разворачивались компо- ненты. Между артефактом и логическим элементом (компонентом), кото- рый он реализует, устанавливается зависимость манифестации. Диаграмма объектов (Object Diagram) демонстрирует полный или час- тичный снимок моделируемой системы в заданный момент времени. На диаграмме объектов отображаются экземпляры классов (объекты) системы с указанием текущих значений их атрибутов и связей между объектами. Диаграмма пакетов (Package Diagram) — структурная диаграмма, ос- новным содержанием которой являются пакеты и отношения между ни- ми. Жесткого разделения между разными структурными диаграммами не проводится, поэтому данное название предлагается исключительно для удобства и не имеет семантического значения (пакеты и диаграммы пакетов могут присутствовать на других структурных диаграммах). Диаграммы пакетов служат в первую очередь для организации элемен- тов в группы по какому-либо признаку с целью упрощения структуры и организации работы с моделью системы. Диаграмма деятельности (Activity Diagram) — диаграмма, на кото- рой показано разложение некоторой деятельности на ее составные час- 222
ти. Под деятельностью (англ. - activity) понимается спецификация испол- няемого поведения в виде координированного последовательного и па- раллельного выполнения подчиненных элементов- вложенных видов деятельности и отдельных действий (англ. - action), соединенных между собой потоками, которые идут от выходов одного узла ко входам другого. Диаграмма автомата (State Machine Diagram, диаграмма конечного ав- томата, диаграмма состояний) — диаграмма, на которой представлен авто- мат с простыми состояниями, переходами и композитными состояниями. Автомат (англ. - state machine) - спецификация последовательности состояний, через которые проходит объект или взаимодействие в ответ на события своей жизни, а также ответные действия объекта на эти со- бытия. Конечный автомат прикреплен к исходному элементу (классу, кооперации или методу) и служит для определения поведения его эк- земпляров. Диаграмма прецедентов (Use Case Diagram, диаграмма вариантов использования) — диаграмма, на которой отражены отношения, сущест- вующие между акторами и прецедентами. Основная задача — представлять собой единое средство, дающее воз- можность заказчику, конечному пользователю и разработчику совмест- но обсуждать функциональность и поведение системы. Диаграмма коммуникации (Communication Diagram, в UML 1.x —диа- грамма кооперации, Collaboration Diagram) — диаграмма, на которой изо- бражаются взаимодействия между частями композитной структуры или ролями кооперации. В отличие от диаграммы последовательности, на диаграмме коммуникации явно указываются отношения между элемен- тами (объектами), а время как отдельное измерение не используется (применяются порядковые номера вызовов). Диаграмма последовательности — диаграмма, на которой изображено упорядоченное во времени взаимодействие объектов. В частности, на ней изображаются участвующие во взаимодействии объекты и последова- тельность сообщений, которыми они обмениваются. Диаграмма сотрудничества (Collaboration Diagram) — этот тип диа- грамм позволяет описать взаимодействия объектов, абстрагируясь от последовательности передачи сообщений. В этом типе диаграмм в ком- пактном виде отражаются все принимаемые и передаваемые сообщения конкретного объекта и типы этих сообщений. По причине того, что диаграммы Sequence и Collaboration являются разными взглядами на одни и те же процессы, Rational Rose позволяет создавать из Sequence-диаграммы диаграмму Collaboration и наоборот, а также производит автоматическую синхронизацию этих диаграмм. Диаграмма взаимодействия (Interaction Overview Diagram) — разно- видность диаграммы деятельности, включающая фрагменты диаграммы последовательности и конструкции потока управления. Этот тип диаграмм включает в себя диаграммы Sequence Diagram (диаграммы последовательностей действий) и Collaboration Diagram (диаграммы сотрудничества). Эти диаграммы позволяют с разных точек зрения рассмотреть взаимодействие объектов в создаваемой системе. 223
Диаграмма синхронизации (Timing Diagram) — альтернативное пред- ставление диаграммы последовательности, явным образом показываю- щее изменения состояния на линии жизни с заданной шкалой времени. Может быть полезна в приложениях реального времени. Спецификация разрабатываемой системы при использовании UML объединяет несколько моделей: использования, логическую, реализации процессов, развертывания. Модель использования содержит описание функций программной системы с точки зрения пользователя. Логическая модель описывает ключевые понятия моделируемой про- граммной системы (классы, интерфейсы и т.п.), т.е. средства, обеспечи- вающие ее функциональность. Модель реализации определяет реальную организацию программных модулей в среде разработки. Модель процессов отображает организацию вычислений и позволяет оценить производительность, масштабируемость и надежность про- граммной системы. Модель развертывания показывает, каким образом программные компоненты размещаются на конкретном оборудовании. Все вместе перечисленные модели, каждая из которых характеризует определенную сторону проектируемой системы, составляют относи- тельно полную модель разрабатываемого продукта. На этапе постановки задачи и требований к системе используют диа- граммы прецедентов, диаграммы деятельностей для расшифровки со- держания прецедентов, диаграммы состояний для моделирования объ- ектов со сложными состояниями, диаграммы классов для выявления концептуальных сущностей предметной области задачи и диаграммы последовательностей действий. 5.3.2. Определение прецедентов использования Для описания функциональных требований к системе используется моделирование прецедентов. Разработку спецификаций программной системы начинают с анализа требований к функциональности, указан- ных в техническом задании. В процессе анализа выявляют внешних пользователей разрабатываемой программной системы и перечень от- дельных аспектов ее поведения в процессе взаимодействия с конкрет- ными пользователями (отдельные физические лица, взаимодействую- щие системы, источники и потребители информации различной физиче- ской природы). Функциональные требования к системе документируют- ся во время разработки с помощью модели прецедентов использования, которая иллюстрирует планируемые функции системы (прецеденты ис- пользования), их окружение (актеров) и связи между ними [14]. Прецеденты - это подробные процедурные описания вариантов ис- пользования системы всеми заинтересованными лицами, а также внеш- ними системами, т.е. всеми, кто (или что) может рассматриваться как актеры (actors), иначе — действующие лица. По сути, прецеденты — это 224
своего рода алгоритмы работы с системой с точки зрения внешнего ми- ра. Прецеденты являются основой функциональных требований к систе- ме. Они позволяют описывать границы проектируемой системы, ее ин- терфейс, а затем выступают в качестве основы для тестирования систе- мы заказчиком с помощью приемочных тестов. Актеры не являются частью системы, они представляют любые объ- екты, взаимодействующие с системой (пользователи, датчики, другие системы и др.). Актер может: • только вводить информацию в систему; • только получать информацию от системы; • вводить и получать информацию от системы. Актеры выявляются на основании изучения предметной области, для которой предназначена разрабатываемая система, а также по результа- там общения с заказчиком системы и экспертами. Для определения ак- теров в системе можно использовать следующие вопросы. • Кто заинтересован в данных требованиях? • Где будет применяться данная система? • Кто выигрывает от использования системы? • Кто обеспечивает систему информацией и применяет эту инфор- мацию, а также удаляет ее? • Кто занимается поддержкой системы? • Использует ли система внешние ресурсы? • Выполняет ли один человек несколько ролей? • Выполняют ли несколько человек одну и ту же роль? • Взаимодействует ли система с уже действующими системами? Модель прецедентов использования - это диалог между актерами и системой. Прецеденты использования представляют функциональ- ность системы, т.е. то, какие возможности система предоставляет акте- рам. Набор прецедентов использования системы охватывает все спосо- бы ее применения. Прецедент использования — это последовательность выполняемых системой транзакций, приводящих к измеримому результату, важному для конкретного актера. Для определения прецедентов использования в системе можно ис- пользовать следующие вопросы. • Какую задачу выполняет каждый актер? • Будет ли актер создавать, сохранять, изменять, удалять или считы- вать информацию из системы? • В каком из прецедентов использования будет создаваться, сохра- няться, изменяться, удаляться или считываться эта информация? • Потребуется ли актеру уведомлять систему о внезапных внешних изменениях? • Необходимо ли оповещать актеров о наступлении каких-либо со- бытий в системе? • Какой прецедент использования будет поддерживать систему? 225
• Могут ли все функциональные требования быть выполнены с по- мощью прецедентов использования? В зависимости от цели выполнения конкретной задачи различают следующие варианты диаграмм использования: • основные, обеспечивающие выполнение функций проектируемой системы; • вспомогательные, обеспечивающие выполнение настроек системы и ее обслуживание; • дополнительные, служащие для удобства пользователя (реализу- ются в том случае, если не требуют серьезных затрат каких-либо ресур- сов ни при разработке, ни при эксплуатации). В качестве примера рассмотрим построение диаграмм вариантов ис- пользования для проведения анализа функциональных требований и пользователей системы тестирования, которая является модулем обу- чающей системы. Система тестирования прежде всего требуется сле- дующим заинтересованным лицам: • обучаемому (студенту); • составителю тестов (преподавателю); • преподавателю, принимающему экзамен; • сотруднику деканата, осуществляющему контроль за успеваемостью; • администратору сети и баз данных учебного заведения. На начальном этапе создания системы можно ограничиться только двумя важными для разработчика системы ролями действующих лиц: • роль студента (тестируемого); • роль администратора (он же преподаватель, он же составитель тес- тов). Соответственно основные прецеденты (варианты использования) для системы тестирования следующие. Прецедент для студента: • П1 - пройти тестирование. Прецеденты для администратора: • П2 - создать (изменить) тест; • ПЗ - просмотреть результаты тестирования; • П4 — добавить (изменить) пользователей и др. Каждый вариант использования можно описать кратко или подроб- но. Краткая форма содержит название, действующие лица, тип варианта (основной, вспомогательный, дополнительный) и его краткое описание. Для примера составим краткое описание варианта использования для прецедента П1 в форме следующей таблицы (табл. 5.1). Таблица 5.1. Вариант использования для прецедента П1 Название варианта Прохождение теста Цель Получение оценки Действующие лица (актеры) Студент Краткое описание Регистрация студента, запуск теста, выбор ответа из нескольких предложенных или ввод ответа, за- вершение теста, получение оценки 226
Подробное описание варианта использования для прецедента П1 мо- жет быть выглядеть следующим образом (табл. 5.2). Таблица 5.2. Вариант использования Прохождение теста Действия исполнителя Отклик системы 1. Студент вводит свои данные (ФИО, группа), т.е. регистрируется в системе 2. Система создает на диске файл с резуль- татом тестирования и предлагает выбрать тест 3. Студент выбирает тест 4. Система запускает тест 5. Студент последовательно отвечает на во- просы 6. Система регистрирует правильные и не- правильные ответы 7. Студент завершает тестирование 8. Система подсчитывает процент правиль- ных ответов 9. Студент ожидает результата 10. Система демонстрирует результат и предлагает его сохранить 11. Студент решает, сохранить результат или нет 12. Если выбрано сохранение, система запи- сывает результат в файл 13. Студент завершает работу 14. Система завершает работу Для большей наглядности используют диаграммы вариантов ис- пользования. При этом используются обозначения, которые приведены на рис. 5.22. ((а) - актер, б) - вариант использования, в) — связь). Рис. 5.22. Диаграмма варианта использования Для приведенного описания прецедентов диаграмма может выгля- деть так, как показано на рис. 5.23. Естественно, все варианты использования сразу определить, как пра- вило, не удается: новые варианты постоянно фиксируются по ходу раз- работки системы и даже в процессе эксплуатации. Но чем больше вари- антов выявлено в процессе уточнения спецификаций, тем лучше, так как при этом получают более точную модель предметной области, что уменьшает вероятность ее пересмотра при добавлении функций. Концептуальная модель (англ. — Conceptual model) — это определен- ное множество понятий и связей между ними, являющихся структурой рассматриваемой области. На диаграммы такой модели будут смотреть, их будут обдумывать, но с самой моделью ничего делать не будут. Это не означает, что модель не нужна, это означает, что модель использует- ся только для управления мыслительным процессом, для понимания. Поэтому такие модели называются концептуальными [15]. 227
Рис. 5.23. Диаграмма использования для прецедента П1 5.3.3. Концептуальная модель предметной области Такой тип использования моделей- один из самых важных, напри- мер, потому что так используются модели, которые получаются в ре- зультате анализа предметной области. Концептуальные модели доволь- но стабильны: если не меняется предметная область, то нет нужды ме- нять и модель. Главное требования к модели предметной области - это концептуальная целостность (consistency). Первый шаг к построению концептуальной модели — составление глоссария. Каждый проект влечет за собой массу терминов и определе- ний. Как отмечается в [10], во многих проектах возникает соблазн опус- тить создание глоссария, поскольку он не кажется чем-то значительным и важным. Но глоссарий выполняет две функции, которые не столь оче- видны в начале работы над проектом. 1. Он помогает новым людям в проекте быстрее разобраться с клю- чевыми терминами, сокращениями и акронимами, используемыми в проекте, тем самым ускоряя их погружение в проект. 2. Если в проекте нет глоссария, то участникам проекта придется изучать значения терминов путем постепенного их осознания. Это не очень надежный и последовательный способ изучения языка, характер- ного для среды работы заказчика. Глоссарий представляет собой общий репозитарий правильных определений используемых терминов. Центральное место в объектно ориентированном подходе к проекти- рованию ПС занимает разработка логической модели системы в виде диаграммы классов. Диаграмма классов определяет типы объектов сис- темы и различного рода статические связи, которые существуют между ними. Класс в языке UML служит для обозначения множества объектов, которые имеют одинаковую структуру, поведение и отношения с объек- тами из других классов. На диаграмме класс изображается в виде пря- моугольника, который дополнительно может быть разделен горизон- тальными линиями на две или три секции (рис. 5.24). В этих разделах 228
могут указываться имя класса, атрибуты (переменные) и операции (ме- тоды). Иногда добавляется четвертая секция, которая содержит описа- ние исключительных ситуаций. Чтобы отличить класс от других эле- ментов языка UML, секции атрибутов и операций выделяют горизон- тальными линиями, даже если они пустые (см. рис. 5.24). Рис. 5.24. Варианты изображения классов Обязательный элемент - имя класса. Имя класса должно быть уни- кальным в пределах диаграммы или совокупности диаграмм классов па- кета. Оно указывается в первой верхней секции прямоугольника, запи- сывается по центру секции полужирным шрифтом и должно начинаться с заглавной буквы. В качестве имени рекомендуется использовать одно или несколько существительных без пробелов. Кроме того, в этой сек- ции могут находиться ссылки на стандартные шаблоны или абстракт- ные классы, от которых образован данный класс и от которых он насле- дует свойства и методы. Во второй секции прямоугольника записываются атрибуты класса или свойства. Стандартная запись атрибута в языке UML выглядит сле- дующим образом: < квантор видимости > < имя атрибута > [кратность]: < тип атрибута > = < исходное значение > {строка свойств}. Квантор видимости может быть опущен — это означает, что види- мость атрибута не указывается либо же должна принимать одно из трех возможных значений: • общедоступный (public) - обозначается «+»; • защищенный (protected) - обозначается «#»; • закрытый (private) — обозначается «—». Имя атрибута, единственный обязательный элемент, - строка тек- ста, которая используется в качестве идентификатора атрибута и являет- ся уникальной в пределах данного класса. Кратность атрибута показывает количество конкретных атрибутов данного типа, входящих в состав класса, и обозначается следующим об- разом: [нижняягр аница 1 ... верхняя_граница1, нижняя_граница2 ... верх- няя_граница2, ... нижняя_ границак ... верхняя_границак], где нижняя граница и верхняя_граница являются положительными це- лыми числами, каждая пара которых служит для обозначения отдельно- го замкнутого интервала целых чисел. В качестве верхней границы мо- 229
жет использоваться специальный символ «*», который означает произ- вольное положительное целое число, т.е. не ограниченное сверху значе- ние кратности соответствующего атрибута. Если указывается единст- венный знак «*», то это означает, что кратность атрибута может быть произвольным положительным целым числом или нулем. Если крат- ность атрибута не указана, то по умолчанию она равна нулю. Тип атрибута указывается строкой текста, имеющей осмысленное значение в пределах пакета или модели, к которым относится рассмат- риваемый класс. Можно определить тип атрибута в зависимости от язы- ка программирования, который будет использоваться при реализации данной модели. Исходное значение служит для задания некоторого начального значе- ния для соответствующего атрибута в момент создания отдельного эк- земпляра класса. В третьей секции класса прямоугольника, обозначающего некоторый класс, указывается операция (или операции) класса. Часто понятия «операция» и «метод класса» отождествляют. Однако эти понятия в UML различаются. Операция — это сервис, который может быть за- прошен у любого объекта класса для реализации поведения, а метод — реализация операции [6]. Каждой неабстрактной операции класса дол- жен быть сопоставлен метод, который содержит исполняемый алгоритм в виде тела класса. Для именования операции рекомендуется использовать глаголы, со- ответствующие ожидаемому поведению объектов данного класса. Опи- сание операции имеет следующий вид: < квантор видимости > < имя операции > (список параметров): < выражение типа возвращаемого значения >{строка свойств}. Квантор видимости принимает такие же значения, как и в случае ат- рибутов класса, и может быть опущен. Вместо условных графических обозначений можно записывать соответствующее ключевое слово (public, protected, private). Имя операции (обязательный элемент) представляет собой строку текста, которая используется как идентификатор операции и которая должна быть уникальной в пределах данного класса. Список параметров представляет собой перечень формальных пара- метров, разделенных запятыми. Каждый параметр представляется в сле- дующем виде: < направление > < имя параметра > : < выражение типа> = < значение параметра по умолчанию > . Направление может принимать одно из следующих значений: • in — входной параметр, который не может быть модифицирован; • out— выходной параметр, который может быть модифицирован для передачи информации вызывающему коду; • inout— входной параметр, который может быть модифицирован для передачи информации вызывающему коду. Выражение типа зависит от конкретного языка программирования и описывает тип возвращаемого значения для соответствующего фор- 230
мального параметра. Значение по умолчанию в общем случае представ- ляет собой выражение для значения формального параметра. Выражение типа возвращаемого значения также зависимо от языка реализации спецификаций типа или типов значений параметров, кото- рые возвращаются объектом после выполнения соответствующей опера- ции. Операция может не возвращать никакого значения. Строка свойств служит для определения значений свойств опера- ции. Она может отсутствовать, если никакие свойства не специфициро- ваны. Операция, которая не может изменять наблюдаемое состояние класса, обозначается строкой-свойством {запрос} (query). Наблюдае- мым состоянием объекта является состояние, которое можно опреде- лить посредством связанных с ним запросов. Ряд свойств операции предназначен для поддержки семантики параллелизма операций. К ним относятся sequential (последовательная), guarded (защищенная) и concurrent (параллельная). Эти свойства существенны только в при- сутствии активных объектов, процессов или потоков. Если какая-либо операция в некотором классе не выполняется, она может быть помечена как абстрактная {abstract}. Классы— наиболее важные строительные блоки любой объектно ориентированной системы. Однако они представляют лишь одну разно- видность более общих строительных блоков UML - классификаторов. Классификатор - это механизм, описывающий структурные и поведен- ческие свойства. UML предоставляет множество других немаловажных для моделирования классификаторов [6]: • интерфейс — набор операций, используемых для спецификации сервиса класса или компонента; • тип данных — тип, значение которого неизменно (примеры: прими- тивные встроенные типы (числа, строки и др.), типы перечислений и др.); • ассоциация - описание набора ссылок, каждая из которых соеди- няет ряд объектов; • сигнал - спецификация асинхронного сообщения, передаваемого между экземплярами объектов; • компонент — модульная часть системы, скрывающая свою реализа- цию за набором внешних интерфейсов; • узел — физический элемент, существующий во время исполнения и представляющий вычислительный ресурс, который обычно наделен некоторой памятью и — частично - вычислительными возможностями; • вариант использования - описание последовательности действий, осуществляемых системой и порождающих значимый результат для оп- ределенного действующего лица; • подсистема — компонент, представляющий одну из главных частей системы. Графически UML представляет эти виды классификаторов так, как показано на рис. 5.25. 231
класс Shape origin интерфейс «interface» Tasteful set(v: Object) get (): Object тип данных «type» Int {values range from) •2**31-to 2**31 сигнал «signal» OffHook Move (} Resize (} Display (} компонент Call Handing Рис. 5.25. Классификаторы UML Вариант использования подсистема Loan approval «subsystem» Customer Service Язык UML предлагает использовать три уровня диаграмм классов в зависимости от степени их детализации. 1. Концептуальный уровень, на котором диаграммы классов ото- бражают связи между основными понятиями предметной области. Эти понятия будут соответствовать реализующим их классам, однако такое прямое соответствие зачастую отсутствует. На самом деле концептуаль- ная модель может иметь весьма слабое отношение к реализующему ее программному обеспечению, поэтому ее можно рассматривать как не зависящую от средств реализации (языка программирования). 2. Уровень спецификаций, на котором диаграммы классов отобра- жают связи объектов этих классов, но рассматриваются только интер- фейсы, а не программная реализация классов (под интерфейсом здесь понимается набор операций класса, видимых извне). 3. Уровень реализации, на котором диаграммы классов непосредст- венно показывают поля и операции конкретных классов. Каждую из перечисленных моделей используют на конкретном этапе разработки программных систем: • концептуальную модель на - этапе анализа; • диаграммы классов уровня спецификации - на этапе проектирова- ния; • диаграммы классов уровня реализации — на этапе реализации. Диаграмма классов может отражать различные взаимосвязи между отдельными сущностями предметной области, такими как объекты и подсистемы, а также описывает их внутреннюю структуру и типы от- ношений. Диаграммы классов обычно содержат следующие сущности: • классы; • интерфейсы; 232
• кооперации; • отношения зависимости, обобщения и ассоциации. В UML способы соединения сущностей друг с другом, логические либо физические, моделируются связями (рис. 5.26). В объектно ориен- тированном моделировании существует три типа наиболее важных свя- зей: зависимости, обобщения и ассоциации [6]. 1. Зависимость представляет собой связь использования. Это связь, которая устанавливает, что одна сущность, например класс Window (Окно), использует информацию и сервис (операцию или услугу), пре- доставляемые другой сущностью, например классом Event («Событие»), но необязательно наоборот. Зависимость изображается пунктирной ли- нией со стрелкой, направленной на зависимую сущность. Чаще всего за- висимость используется для того, чтобы показать, что один класс ис- пользует операции другого класса. 2. Ассоциация — это структурная связь между экземплярами. Она по- казывает, что объекты одной сущности соединяются с объектами дру- гой. Ассоциация изображается сплошной линией. Допустимо, чтобы оба конца ассоциации соединяли один и тот же класс, иными словами, один объект класса может связываться с другим объектом того же класса. Обычно ассоциация бинарна, т.е. связывает два класса, но бывают и парные ассоциации. Класс, участвующий в ассоциации, выполняет конкретную роль. Роль, которую играет класс, находящийся на конце ассоциации, называется конечным именем (в первой версии UML она называлась именем роли). Ассоциация может иметь параметр множе- ственности. Он представляет собой диапазон целых чисел, указываю- щий количество связанных объектов (рис. 5.27). Множественность мо- жет быть определена как единица (1), ноль или один (0.1), много (0..* или *), один или несколько (1..*). Рис. 5.26. Способы соединения сущностей 233
------------ 1"* Человек -------------- работник * _______________ ----------- Компания работодатель Рис. 5.27, Отображение параметра множественности 3. Обобщение связывает обобщенные классы (родительские классы) с более специализированными (дочерними) и потому известны как свя- зи наследования (класс — подкласс, родитель - потомок). Дочерняя сущ- ность наследует свойства родителей, а именно его атрибуты и операции. Часто потомок имеет дополнительные атрибуты и операции помимо ро- дительских. Реализация операции в дочернем классе замещает реализа- цию той же операции родителя (это явление называется полиморфиз- мом). Графически обобщение представляется сплошной линией со стрелкой в форме пустого треугольника, указывающего на родителя. Перечисленные выше три вида связей описывают большинство ос- новных способов взаимодействия сущностей. Как показано на рис. 5.26, UML предлагает особое графическое представление для каждого вида связи. Эта нотация позволяет визуализировать связи независимо от кон- кретного языка программирования, причем способом, описывающим наиболее важные параметры связей: имя, соединяемые сущности и свойства. 5.3.4. Описание поведения системы Для моделирования динамических аспектов систем в UML использу- ются диаграммы взаимодействия (набор объектов и их связей, включая передаваемые между ними сообщения), к которым относятся диаграм- мы коммуникации и диаграммы последовательности. Первые показыва- ют структурную организацию объектов, отправляющих и принимающих сообщения, в виде наборов вершин и дуг. Вторые выделяют временной порядок сообщений. Для описания особенностей поведения программной системы ис- пользуются диаграммы последовательностей системы, системные собы- тия, системные операции, диаграммы деятельности и — при необходимо- сти - диаграммы состояний объектов. Диаграммы последовательностей системы представляют собой гра- фическую модель, которая для определенного варианта использования показывает динамику взаимодействия объектов во времени [6, 8]. Для построения диаграммы последовательностей необходимо: • идентифицировать каждое действующее лицо (объект) и изобра- зить для него линию жизни (вертикальная линия, направленная вниз от объекта, см. ниже): крайним слева на диаграмме изображается объект, который является инициатором взаимодействия; правее изображается объект, который непосредственно взаимодействует с первым, и т.д.; 234
• из описания варианта использования определить множество сис- темных событий и их последовательность; • изобразить системные события в виде линий со стрелками на кон- це между линиями жизни действующих лиц и системы, а также указать имена событий и списки передаваемых значений. На диаграмме последовательностей изображаются только те объек- ты, которые непосредственно участвуют во взаимодействии, и не пока- зываются возможные статические ассоциации с другими объектами. При этом диаграмма последовательностей имеет два измерения. Одно — слева направо в виде вертикальных линий, каждая из которых изобра- жает линию жизни отдельного объекта, участвующего во взаимодейст- вии. Графически каждый объект изображается прямоугольником и рас- полагается в верхней части своей линии жизни (рис. 5.28). Внутри пря- моугольника записывается имя объекта и имя класса, разделенные двое- точием. Вся запись подчеркивается, что является признаком объекта, который представляет собой экземпляр класса. Линия жизни объекта служит обозначением периода времени, в те- чение которого объект существует в системе и, следовательно, может потенциально участвовать во всех ее взаимодействиях. На диаграмме линия жизни изображается пунктирной вертикальной линией, ассоции- руемой с единственным объектом. Если объект существует в системе постоянно, то линия его жизни должна продолжаться по всей плоскости диаграммы — последовательно от самой верхней до самой нижней части (объекты 1 и 2 на рис. 5.28). Если объекты разрушаются в какой-то момент для освобождения ре- сурсов системы, то их линия жизни обрывается в момент уничтожения. Для обозначения такого момента в языке UML используется специаль- ный символ в форме латинской буквы х (объект 3 на рис. 5.28). Ниже этого символа пунктирная линия не изображается, поскольку соответст- вующего объекта в системе уже нет (и этот объект должен быть исклю- чен из всех последующих взаимодействий). Объекты на диаграмме последовательности могут находиться в двух состояниях: активном, непосредственно выполняя какие-либо действия, и пассивном, ожидая сообщения от других объектов. Чтобы явно выде- лить подобную активность, диаграммы последовательностей имеют фо- кусы управления (это высокие узкие прямоугольники, показывающие период времени, в течение которого объект выполняет действие — как непосредственно, так и с помощью зависимой процедуры). Верхняя грань прямоугольника выровнена по началу действия, ниж- няя - по его завершению. Они могут быть отмечены сообщением воз- врата. Можно показать вложенность фокуса управления, вызванную ре- курсией, вызовом собственной операции либо возвратом вызова из дру- гого объекта, наложив другой фокус управления чуть правее родитель- ского (таким образом можно изобразить сколько угодно уровней вложе- ния). 235
Рис. 5.28. Элементы диаграммы последовательностей Основное содержимое диаграммы последовательности - сообщения. Они изображаются стрелками, направленными от одной линии жизни к другой. Стрелка указывает на приемник сообщения. Если сообщение асинхронное, то стрелка рисуется уголком, а если синхронное (вызов), то закрашивается треугольником. Ответ на синхронное сообщение (воз- врат из вызова) показывается пунктирной стрелкой уголком. Сообщение вызова может быть опущено, поскольку каждый вызов неявно подразу- мевает возврат, но иногда удобно таким образом продемонстрировать возвращаемое значение. Следует иметь в виду, что линии жизни показывают лишь относи- тельные последовательности. Позиции сообщений на отдельных парах линий жизни, как правило, не влияют на хронологию передачи инфор- мации; сообщения могут поступать в любом порядке. Полные наборы сообщений на отдельных линиях жизни формируют частичное упорядо- чение. Серии сообщений, однако, устанавливают цепь причинных свя- зей, поэтому любая точка на другой линии жизни в конце цепи должна всегда следовать за точкой начала цепи на исходной линии. Для моделирования процесса выполнения операций в языке UML ис- пользуются диаграммы деятельности. Диаграммы деятельности демон- стрируют потоки управления и потоки данных внутри прецедентов ис- пользования. На диаграммах отображаются последовательности дейст- вий, потоки управления, точки объединения и принятия решений. На этапе анализа требований и уточнения спецификаций диаграммы дея- тельности позволяют конкретизировать основные функции разрабаты- ваемой программной системы. Под деятельностью в данном случае понимают задачу (операцию), которую необходимо выполнить вручную или при помощи средств ав- томатизации. Каждому варианту использования соответствует своя по- следовательность задач. В теоретическом плане диаграммы деятельно- сти являются обобщенным представлением алгоритма, реализующего анализируемый вариант использования. 236
Действия — это элементы, с помощью которых реализуется поведе- ние в рабочем потоке. Действия отображаются прямоугольниками с за- кругленными углами (рис. 5.29). Внутри этой фигуры записывается вы- ражение действия, которое должно быть уникальным в пределах одной диаграммы деятельности. Действия можно определить, изучив специфи- кацию прецедентов использования и определив, какое поведение необ- ходимо для пошагового выполнения прецедента использования. Дейст- вие может быть записано на естественном языке, некотором псевдокоде или языке программирования. Рекомендуется в качестве имени просто- го действия использовать глагол с пояснительными словами. Управляющие потоки, или потоки управления. После завершения действия управление передается следующему действию. Потоки управ- ления показывают передачу управления от одного действия другому. Изображаются потоки управления стрелками. Потоку можно дать имя. Точки принятия решений. Точки принятия решений используются для отображения разветвления управления. Потоки, исходящие из точек принятия решений, содержат условия, определяющие альтернативные пути потока управления после точки принятия решения. Условия пи- шутся в квадратных скобках, а точки принятия решений изображаются небольшими ромбами. Графически ветвление на диаграмме деятельно- сти обозначается небольшим ромбом, внутри которого текст отсутству- ет. В этот ромб может входить только одна стрелка от того состояния действия, после выполнения которого поток управления должен быть продолжен по одной из взаимоисключающих ветвей. Потоки объектов. Поток объектов символизирует поток от действия к данным или от данных к действию. Данные изображаются прямо- угольником. А потоки данных обозначаются стрелками (рис. 5.30). —-------------1 Разработать план проекта > lndex:= number+1 Поток управления Действие Рис. 5.29. Элементы диаграммы деятельности Данные Рис. 5.30. Изображение данных и потоков данных 237
Разделение и слияние потока управления, В рабочем потоке могут быть действия, которые могут выполняться параллельно. Узел разделе- ния показывает, какие действия можно выполнять одновременно. Узел слияния показывает объединение потоков выполнения, поэтому соот- ветствующие действия должны завершиться перед выполнением даль- нейшей работы. Узлы разделения и слияния обозначаются отрезками (рис. 5.31). Начальный и конечный узлы. Для обозначения начального и конечно- го узлов рабочего потока используются специальные символы. Началь- ный узел изображается целого залитого круга, а конечный - в виде чер- ного круга, окаймленного линией окружности несколько большего диа- метра (так называемый «бычий глаз»). Возможный пример использова- ния начального и конечного узлов показан на рис. 5.32. Рис. 5.31. Разделение и слияние потоков управления Action Рис. 5.32. Обозначение начального и конечного узлов Кроме рассмотренных диаграмм для описания поведения системы используются диаграммы состояний. Главное назначение этих диа- грамм — описать возможные последовательности состояний и перехо- дов, которые в совокупности характеризуют поведение элемента модели в течение его жизненного цикла. Диаграмма состояний используется для моделирования динамических аспектов системы. В большинстве случаев под этим подразумевается моделирование поведения реактив- ных объектов. Реактивным является такой объект, поведение которого лучше всего характеризуется его реакцией на события, произошедшие вне его собственного контекста. Если внешние действия, изменяющие состояние системы, инициируются в произвольные случайные моменты времени, то говорят об асинхронном поведении модели. 238
Диаграмма состояний по существу является графом специального вида, который представляет некоторый автомат. Вершинами этого гра- фа являются состояния, которые изображаются соответствующими гра- фическими символами. Дуги графа служат для обозначения переходов из одного состояния в другое. Диаграммы состояний могут быть вложе- ны друг в друга, образуя более детальное представление отдельных эле- ментов модели. Простейший пример автомата с двумя состояниями демонстрирует ситуация с исправностью компьютера. Здесь рассматриваются два са- мых общих состояния («исправен» и «неисправен») и два перехода («выход из строя» и «ремонт»). Графически эта информация может быть представлена в виде диаграммы состояний компьютера (рис. 5.33). Рис. 5.33. Диаграмма состояний компьютера Основными понятиями, описывающими автомат, являются состоя- ние и переход. Предполагается, что система находится в каком-либо со- стоянии в течение некоторого времени, тогда как переход объекта из со- стояния в состояние происходит мгновенно. Состояние — это ситуация в жизни объекта, на протяжении которой он удовлетворяет некоторому условию, осуществляет определенную деятельность или ожидает какого-то события. Состояние на диаграмме изображается прямоугольником со скругленными вершинами, который может быть разделен горизонтальной линией на две секции. В прямо- угольнике может располагаться «Имя состояния» (первая секция) и «Список внутренних действий в данном состоянии» (вторая секция). При этом под действием в языке UML понимают некоторую атомарную операцию, выполнение которой приводит к изменению состояния или возврату некоторого значения. Имя состояния — это строка текста, начинающаяся с заглавной бук- вы, которая раскрывает содержательный смысл данного состояния. Имя является обязательным элементом. Рекомендуется в качестве имени ис- пользовать глаголы в настоящем времени («печатает», «ожидает», «по- лучает» и т.п.) или соответствующие причастия («занят», «свободен», «получено» и т.п.). Список внутренних действий содержит перечень внутренних дейст- вий или деятельностей, которые выполняются в процессе нахождения 239
моделируемого элемента в данном состоянии. Каждое действие записы- вается в виде отдельной сороки и имеет следующий формат: < метка действия 7’ выражение действия >. Метка действия указывает на обстоятельства или условия, при кото- рых будет выполняться деятельность, определенная выражением дейст- вия. При этом выражение действия может использовать любые атрибу- ты и связи, которые принадлежат области имен или контексту модели- руемого объекта. Если список выражения действий пустой, то раздели- тель в виде наклонной черты 7’может не указываться. На рис. 5.34 пока- зан пример состояния «Считывает запись» после открытия файла, со- держащего несколько записей. Считывает запись символ/считать символ конец строки / проверить на конец строки Рис. 5.34. Пример изображения состояния с пояснением Простой переход представляет собой отношение между двумя по- следовательными состояниями, которое указывает на факт смены одно- го состояния другим. Пребывание моделируемого объекта в первом со- стоянии может сопровождаться выполнением некоторых действий, а пе- реход во второе состояние будет возможным после завершения этих действий, а также после удовлетворения некоторых дополнительных ус- ловий. В этом случае говорят, что переход срабатывает. До срабатывания перехода объект находится в предыдущем от него (перехода) состоя- нии, называемом исходным состоянием, или в источнике (не путать с начальным состоянием — это разные понятия), а после его срабатыва- ния объект находится в последующем от него (перехода) состоянии (це- левом состоянии). На диаграмме состояний переход изображается сплошной линией со стрелкой, которая направлена в целевое состояние. Рядом с линией мо- жет находиться строка текста, описывающая со бытие-триггер, вызы- вающее переход (в этом случае переход будет триггерным), и стороже- вое условие, по которому осуществляется переход. Событие представляет собой спецификацию существенного факта, который происходит во времени и пространстве. В контексте автоматов это стимул, способный вызвать срабатывание перехода. Сторожевое условие, если оно есть, всегда записывается в прямых скобках после со бытия-триггер а и представляет собой некоторое булев- ское выражение, результатом которого является «истина» или «ложь». 240
5.4. Разработка предварительного внешнего проекта 5.4.1. Процесс внешнего проектирования Внешний проект является процессом описания планируемого пове- дения продукта программной системы, как если бы оно воспринималось наблюдателем, посторонним по отношению к продукту. Целью внешне- го проекта является конструирование внешних (обычно пользователь- ских, но не всегда) интерфейсов без рассмотрения внутренних свойств продукта [18]. Внешний проект выражается в форме внешних спецификаций, пред- назначенных для широкой аудитории, включающей в себя пользователя (для проверки и одобрения), авторов документации для пользователя, всех участвующих в проекте программистов, а также тех, кто будет уча- ствовать в тестировании продукта. Следуя схеме 4.1, приведенной в п. 4.1, процесс разработки внешнего проекта, как элемент этой схемы, в свою очередь, можно представить следующей схемой: (5.3) где 77?) с: 77? f — часть функциональных требований, предъявляемых к системе и определяющих пользовательский интерфейс; 77?; с TRS — полный набор функциональных требований, предъявляемых к про- граммной системе; 77? т — полный набор требований, предъявляемых к программной системе; TRS ~TRt и TR)tp, TRnj— нефункциональные тре- бования, предъявляемые к системе; 5Р/ — внешние спецификации, оп- ределяющие пользовательский интерфейс; Lsp — язык описания внешних спецификаций, определяющих пользовательский интерфейс системы. Подготовка полных и правильных внешних спецификаций - самая ответственная и трудная задача в разработке ПС. Это связано с тем, что внешние спецификации участвуют в большем числе процессов перевода (в понятии, определенном в п. 4.1), чем любой другой проектный доку- мент. Несмотря на отсутствие сложившейся методологии по разработке внешнего проекта, полезным принципом является идея концептуальной целостности, представляющей собой согласованность (или стремление к ней) между внешними функциями системы. В соответствии с этой концепцией лучше иметь относительно небольшой набор хорошо согла- сованных функций, чем, возможно, большой набор независимых и не- скоординированных функций. Особенности, которые кажутся привлека- тельными, но не согласуются с остальными, вероятно, следует откло- нить, чтобы не усложнять взаимодействие с пользователем. 241
Концептуальная целостность представляет собой меру единообразия способа взаимодействия с пользователем (т.е. меру однородности ин- терфейса пользователя). Система, лишенная концептуальной целостно- сти, - это система, в основе которой нет единообразия. В результате та- кая система характеризуется сложным взаимодействием с пользовате- лем и излишне сложной структурой. В качестве примера рассмотрим распространенную программную систему MS Office. В ней пять основных компонентов: MS Word, MS Excel, MS PowerPoint, MS Access и MS Outlook. Освоив работу, напри- мер, с текстовым процессором, пользователь без особых затруднений работает и с другими программами. Это обеспечивается именно концеп- туальной целостностью системы MS Office, в которой интерфейсы поль- зователей различных компонентов практически одинаковы. Самый легкий путь исключения концептуальной целостности можно отождествить с попыткой создать внешний проект большой группой разработчиков. Опыт показывает, что при числе разработчиков больше двух вероятность успеха (т.е. получения концептуальной целостности проекта) резко снижается [18]. Это справедливо даже для крупных про- ектов, например для создания операционных систем. Однако это не оз- начает, что в процессе проектирования должны принимать участие только двое; требуется, чтобы ответственность за внешние специфика- ции несла лишь маленькая группа людей, принимающая все решения, подготавливая спецификации. В случае крупного проекта этим разра- ботчикам необходима помощь исследователей, ассистентов и вспомога- тельного персонала (чертежников, секретарей и др.). Помощники занимаются сбором и обработкой информации, но не проектированием и не написанием спецификаций. Кем же должны быть ответственные исполнители внешних спецификаций? Они не должны быть программистами, так как процесс внешнего проектирования не имеет ничего общего с разработкой программ. Этот процесс больше связан с пониманием окружающей среды пользователей, их проблем и нужд, психологии связи «человек - машина». Особенно важно то, что эта тенденция внешнего проектирования возрастает с применением но- вых средств вычислительной техники, касающихся большого круга ко- нечных пользователей, не имеющих знаний в программировании и в об- ласти вычислительной техники. Программисты и специалисты по теории программирования обычно не имеют подготовки и опыта в проектировании такого рода. Они могут быть специалистами по языкам программирования, алгоритмам, мето- дам программирования, теории компиляторов, тестированию и отладке, но у них мало или вовсе нет опыта в использовании компьютера, разра- ботке психологических факторов, психологии взаимоотношений чело- века и машины. Было бы разумно использовать программистов для внешнего проектирования продукта, предназначенного для программи- стов, например языков программирования или инструментов отладки, но неразумно ожидать, чтобы программисты выполняли внешнее проек- тирование операционной системы или системы диспетчеризации грузо- виков. 242
Из-за сложности внешнего проектирования и его возрастающей важ- ности для разработки ПС оно требует специалистов особого рода. Такой специалист должен разбираться в соответствующей предметной области пользователя, быть знакомым со всеми этапами проектирования и тес- тирования системы, чтобы понимать влияние на них внешнего проекти- рования. В качестве возможных кандидатов можно назвать системных аналитиков, психологов, занимающихся вопросами поведения, специа- листов по исследованию операций, а возможно, и опытных специали- стов по теории программирования (если их подготовка включает упомя- нутые области). Часто бывает полезно привлечь к этой работе техниче- ского писателя, так как его ориентированность на пользователя оказы- вается очень полезной. 5.4.2. Проектирование взаимодействия с пользователем При проектировании внешних сопряжений системы разработчик ин- тересуется тремя областями, имеющими отношение к надежности про- граммной системы: минимизацией ошибок пользователя, обнаружением ошибок пользователя, когда они все же возникают, и минимизацией сложности. Между ошибками пользователя и ошибками в ПС имеет место опре- деленная связь. Ошибки пользователя увеличивают вероятность перехо- да системы в непредвиденное состояние. Минимизация ошибок пользо- вателя не уменьшает числа ошибок в программной системе, но увеличи- вает ее надежность за счет снижения вероятности столкновения с остав- шимися ошибками. Основные правила минимизации ошибок пользова- теля в диалоговых (интерактивных) системах приводятся ниже. Боль- шинство из них имеет аналогию и для случая пакетной обработки. Г. Майерс предлагает следующее. 1. Согласовывать способ взаимодействия с подготовкой и уровнем пользователя, а также с ограничениями, в условиях которых пользова- тель работает. Интерфейс должен быть различен в зависимости от того, кто им пользуется. Например, можно ожидать, что взаимодействие с пользователем банковской системы должно существенно различаться в зависимости от того, является ли пользователь клиентом банка или опытным кассиром. 2. Проектировать таким образом, чтобы сообщения, вводимые поль- зователем, были как можно короче, но не настолько, чтобы исчезла их осмысленность. При этом следует учитывать частоту работы с системой для среднего пользователя, а также возможность стрессовой ситуации для пользователя в момент его работы с системой. 3. Обеспечивать концептуальную целостность для разных типов вводимых и выводимых сообщений. Например, все сообщения, выда- ваемые на экран дисплея, отчеты, графические документы и т.п. должны иметь одинаковые (по возможности) форматы, стиль, сокращения. 4. Обеспечивать пользователю средства помощи с достаточно широ- ким набором функций. При разработке листингов помощи важно плани- ровать короткие ответы на запросы пользователя. 243
5. Проектировать сообщения пользователю в короткой форме и при- ближать язык сообщений к языку пользователя. 6. Проектировать подтверждение ввода данных, с тем чтобы пользо- ватель или оператор знал о завершении процесса. Без этого пользова- тель может засомневаться, правильно ли введено сообщение, и попыта- ется повторить ввод, вследствие чего может возникнуть ошибочная си- туация. Кроме доведения до минимума ошибок пользователя (за счет пра- вильно спроектированного интерфейса), система должна правильно об- рабатывать ошибки пользователя, а они будут независимо от того, на- сколько хорошо спроектированы правила взаимодействия. Основные правила обнаружения ошибок пользователя перечислены ниже [18]. 1. Проектируйте систему так, чтобы она принимала любые данные. Если введенная информация не является тем, что система считает до- пустимым, она должна информировать пользователя об этом. 2. Если пользователь вводит сложное сообщение, особенно если для этого нужно несколько обращений к системе, позвольте ему проверить части сообщения, прежде чем оно будет обрабатываться. 3. Проектируйте систему так, чтобы ошибки пользователя обрабаты- вались немедленно. 4. Там, где особенно важна точность, проектируйте избыточность входных данных. Например, в банковской системе можно потребовать, чтобы фамилия клиента вводилась вместе с номером счета, чтобы мож- но было обнаружить ошибки при вводе номеров счетов. Другой возможностью повышения надежности системы является до- ведение до минимума сложности внешнего проекта с целью уменьше- ния сложности будущей системы и минимизации ошибок пользовате- лей. Распространено мнение, что интеллектуальный («очеловеченный») внешний проект будет сложным, что он предполагает фантастические по сложности методы, много дополнительных возможностей, автомати- ческое исправление ошибок (например, орфографических). По мнению Г. Майерса [18], это представление ошибочно. Например, современные ПС, такие как поисковые и справочные системы (возьмите тот же «Ян- декс»), достаточно интеллектуальны. Вторая проблема, связанная со сложностью, - предоставление поль- зователю слишком большого числа возможностей и вариантов. В опера- ционной системе OS/360 (как и в советской ОС ЕС ЭВМ) имеется про- цесс настройки, называемый генерацией системы, который позволяет «перекраивать» систему при ее установке. Это привело к тому, что поч- ти каждая установка OS/360 имеет уникальную версию, что усложнило работу компании IBM по сопровождению системы. Всякий раз, когда есть сомнения, давать пользователю некоторую возможность или нет, слишком часто принимается решение давать. В целом же большой набор режимов (вариантов) не нужен пользовате- лю, поскольку часто их обилие неблагоприятно сказывается на работе пользователя. К этому надо добавить, что всякая дополнительная воз- 244
можность или режим работы системы усложняет ее, что приводит к до- полнительным ошибкам. Проектировщик должен рассматривать каждый режим или функцио- нальность системы с учетом их полезности (в противовес внесению до- полнительной сложности из-за достижения удобств пользователя). Ко- гда возникают сомнения, безопаснее отказаться от рассматриваемого ва- рианта. Множество доступных мелких возможностей не повысит конку- рентоспособности продукта. Кроме того, эти возможности могут нега- тивно повлиять на потенциального покупателя, показывая, что разра- ботчик не имеет ясного представления о том, как именно система будет использоваться. Последний вопрос, связанный с надежностью, — на каких предполо- жениях основывается система, воспринимающая входные данные. На- пример, из каких соображений она пытается исправлять ошибки пользо- вателя. Методы такого рода настолько сложны, что часто становятся опасными (см. пример на стр. 68 в [ 18]). Решением при проектировании интерфейса пользователя для надежной программной системы является обеспечение однородного и простого интерфейса, ожидающего некото- рый набор входных данных и немедленно обнаруживающего как можно больше ошибок. 5.4.3. Подготовка внешних спецификаций Правильно составленные внешние спецификации - это объемный документ. Эффективным методом создания такого большого документа является применение к нему иерархической организации. В п. 4.1 внеш- нее проектирование представлялось в виде двух процессов: первона- чального (предварительного) и подробного внешнего проектирования. Допуская, что спецификации являются иерархически организованными, эти два процесса просто представляют контрольные точки в проектиро- вании системы согласно принятой иерархии. Сначала описываются ос- новные компоненты (то, что находится на первом уровне), затем просто компоненты, затем внешние функции (функции пользователя) и в конце концов - детали всех функций пользователя. Предварительное внешнее проектирование включает три первых шага. Система проектируется до уровня, когда уже выделены все функции пользователя, но точный их синтаксис, семантика и выходные результа- ты остаются еще не определенными. При этом преследуются две цели: во-первых, внутри продолжительного процесса внешнего проектирова- ния устанавливается контрольная точка для руководства проекта, и, во- вторых, становится возможной проверка правильности промежуточного уровня проекта и сопоставление его с поставленными целями. В качест- ве примера на рис. 5.35 показан основной управляющий компонент, в рамках этого основного компонента примером компонента может быть администратор секретности. А в рамках этого компонента приме- ром функции может быть приказ «Сообщить о нарушениях секретно- сти» (пример взят из [18]). 245
Публикация [18] относится к тому времени, когда CASE-средства, поддерживающие процессы проектирования программных систем, только начали развиваться. В настоящее время для целей моделирова- ния процессов проектирования ПС на рынке программных продуктов представлен широкий спектр CASE-средств. В нашей стране в начале 2000-х гг. наиболее популярными CASE-средствам и являлись Rational Rose, BPwin, Silverrun, Process Analyst [13]. Моделирование жизненного цикла программных систем в этих средствах имеет много общего, хотя есть и различия. Основные компоненты Компоненты Внешние функции Описание функций Рис. 5.35. Иерархическая организация спецификаций Немаловажными являются комплексность подхода и использование единой унифицированной нотации не только на этапе моделирования предметной области, но и на последующих этапах разработки про- граммной системы, как это имеет место в CASE Rational Rose. Развитием Rational Rose явилось новое CASE-средство — IBM Rational Software Architect, являющееся частью IBM Software Development Platform— набора инструментов, поддерживающих жиз- ненный цикл разработки программных систем [14, 16, 26]. IBM Rational Software Architect предназначен для построения моделей разрабатывае- мых программных систем с использованием унифицированного языка моделирования UML 2.0, прежде всего моделей архитектуры разрабаты- ваемого приложения. Кроме того, Rational Software Architect поддержи- вает технологию разработки, управляемой моделями (model-driven development, MDD), позволяющую моделировать программное обеспе- чение на различных уровнях абстракции с возможностью трассируемо- сти [21]. Следуя принципам RUP, Rational Software Architect позволяет созда- вать необходимые модели в рамках рабочих процессов таких дисцип- лин, как [23]: 246
• управление требованиями (requirements); • анализ и проектирование (analysis and design); • реализация (implementation). Для решения задач подготовки внешних спецификаций RSA предос- тавляет разработчику средства построения диаграмм вариантов исполь- зования, диаграмм деятельности и диаграмм состояний. Варианты ис- пользования специфицируют внешнее поведение, ничего не говоря о том, как его достичь. Это важно, потому что позволяет эксперту или конечному пользователю общаться с разработчиками, конструирующи- ми систему в соответствии с требованиями пользователя (заказчика), не углубляясь в детали реализации. Диаграммы деятельности показывают структуру процесса как пошаговый поток управления и данных. Они описывают динамическое представление системы и важны при модели- ровании функций системы. Диаграммы состояний также описывают ди- намическое представление объекта. Они важны для моделирования по- ведения интерфейсов, классов или коопераций (вместе с функциони- рующими объектами обеспечивая совместное поведение). Детальный внешний проект каждой функции пользователя должен освещать следующие вопросы. 1. Описание входных данных. Точное описание синтаксиса (напри- мер, формат, допустимые значения, области изменения) и семантики всех данных, вводимых пользователем. Этими данными могут быть приказ, входной документ (форма), ответ на подсказку или аналоговый (цифровой) сигнал от внешних устройств. 2. Описание выходных данных. Точное описание всех результатов (выводов) функции, например реакции терминала, сообщения об ошиб- ках, отчеты, аналоговые (цифровые) управляющие сигналы для внеш- них устройств и т.п., должна быть описана функциональная связь вход- ных сигналов с выходными. Это значит, что читатель спецификаций должен быть в состоянии представить себе выходные данные, порож- денные каждым конкретным вариантом входных данных. 3. Преобразования системы. Многие внешние функции не только по- рождают выходные данные, но и изменяют состояние системы. В этом случае должны быть описаны все такие преобразования системы с точ- ки зрения пользователя, так как спецификация является внешней. На- пример, складская система могла бы иметь команду «заказ», которая ис- пользуется продавцом, чтобы начать выполнение заказа. Эта функция имеет два выходных результата: накладная, которая пересылается в от- дел доставки, и подтверждение, выдаваемое на терминал продавца. Она вызывает также два преобразования системы: обновляется инвентарный файл, и заказ регистрируется в файле контроля. 4. Характеристики надежности. Это описание воздействия всех воз- можных отказов функций на саму систему, файлы (базы данных) и пользователя. Установлено, что включение этого раздела в проект внешних спецификаций оказывает небольшое, но положительное влия- ние на надежность [18]. В проекте разработки интерактивной системы необходимо, чтобы предложение «любая ошибка в этой команде не 247
должна вызывать сбоя системы» имело место в этом разделе для каждой команды. Хотя разработчики, проектирующие внутреннюю структуру системы, никогда сознательно не допустили бы, чтобы ошибка в коман- де пользователя вызывала общесистемный сбой, это предложение слу- жит постоянным напоминанием данного факта. 5. Эффективность. Это описание всех требований, которые предъяв- ляются к эффективности функции, таких как затрачиваемое время, и используемая память. Эффективность редко можно специфицировать как абсолют, так как она зависит от конфигураций аппаратуры (компью- тера), скорости передачи данных по линиям связи, эффективности всех других параллельно выполняющихся программ, числа активных пользо- вательских терминалов и др. Чтобы справиться с этой проблемой, в спе- цификациях можно описать несколько стандартных конфигураций и уровней нагрузки, а затем можно указать эффективность отдельных функций по отношению к ним. 6. Замечания по программированию. Внешние спецификации долж- ны описывать продукты с точки зрения пользователя и избегать ограни- чений на внутреннее устройство системы. Однако иногда бывает необ- ходимо подсказать или сообщить какие-то идеи относительно внутрен- него проектирования функции. Практиковать это следует как можно ре- же, но если это необходимо, соответствующую информацию нужно со- общать в этом разделе. Для всякой достаточно сложной функции задача описания функцио- нальных связей результатов и преобразований с входными данными не- тривиальна. Отличный способ справиться с этой проблемой — таблица решений [18]. На рис. 5.36 показана таблица решений для команды L (ОТГРУЗИТЬ_ДЕТАЛИ) из спецификации для складской системы. Команда L имеет три операнда: N„ - номер покупателя, Nd — номер дета- ли, Сд- количество деталей. Результатами выполнения команды явля- ются сообщения на дисплей менеджера (ОМ = L (N„, Аа, )) и преоб- разования системы (Р = L (Nn, Ndi Сд )). Эта таблица решений, сопро- вождаемая небольшим описанием, является прекрасной внешней специ- фикацией команды. Таблицы решений представляют собой наглядный и строгий способ отражения отношений между входными и выходными данными. Чтобы оценить достоинства такой таблицы, можно попробовать выразить ин- формацию, представленную в таблице обычным словесным описанием. Таблицы решений уникальны по своим достоинствам в качестве средст- ва общения между разработчиком внешнего проекта, пользователем и программистом. С одной стороны, в них легко может разобраться пользователь, про- сматривая внешние спецификации. С другой стороны, они служат иде- альными исходными данными для программиста, разрабатывающего логику программы. Кроме того, существуют методы проверки таблиц решений на неполноту, избыточность и неоднозначность. К сожалению, 248
таблицы решений по необъяснимым причинам редко используются на практике. Преобразования Р у Выходные сообщения > отдела ОМ Синтаксис команды правилен ? Y Y у Y Y Y у Y Y Y Y N Номер покупателя правилен? Y у Y Y Y Y Y X N \ х Номер детали правилен? Y Y у у Y \ \ Y Y х х Количество положит, и целое? Y у у Y х Y х Y х Y х Кредит покупателя в порядке0 у Y у X Достаточно деталей на складе? у у N Достаточно ли дет. останется? Y х Послать увел, в отд. доставки х х Послать на терм, сообщение 1 х х Послать на терм, сообщение 2 х Послать на терм, сообщение 3 х Послать на терм, сообщение 4 х х х х Послать на терм, сообщение 5 х х х х Послать на терм, сообщение 6 х х х х Послать на терм, сообщение 7 х Послать в отд. Закупок увед. 1 * * х Послать в отд. Закупок увед. 2 х Уменьшить кол. в инвент. файле х х Зарег, сделку в файле контроля х х Рис. 5.36. Пример таблицы решений Таблицы решений могут использоваться для описания функциониро- вания объектов, подлежащих автоматизации. Например, на рис. 5.37 изображена таблица, представляющая работу банкомата при выполне- нии функции снятия пользователем некоторой суммы средств с банков- ской карточки. Для любой сложной функции пользователя задача установлении со- отношений между выводами, системными преобразованиями и вводами способом причины и следствия - нетривиальная работа. В целом внеш- ние спецификации описывают каждый возможный ввод в систему (как правильный, так и неправильный) и реакцию системы на каждый из вво- дов. Внешние спецификации должны точно и полно описывать внешние интерфейсы (сопряжения) при минимальных предположениях о внут- реннем устройстве системы. 249
Валидна ли карта в карт-ридере? Y Y Y У Y У Y У Y У N Совпадает ли секретный ПИН-код на карте с введенным Y Y Y Y Y У Y У Y N Сумма наличных целое положительное число? Y Y Y У Y У Y У N Соединение с банком установлено? Y У Y У Y У Y N Данное количество наличных есть на банковском счете? Y Y У У Y У N Данное количество есть в банкомате? Y У Y У Y N Есть ли бумага для распечатки чека? Y Y Y У N Извлек ли абонент карту (в течение 1 минуты)? Y У N N Y Взял ли абонент наличные (в течение 1 минуты)? Y N Y N Y Выходные соощииш - ОМ Послать сообщение о загрузке карты в карт-ридер X X X X X X X X X X X Послать сообщение о нерабочей карте X Послать сообщение о вводе секретного ПИН-кода X X X X X X X X X X Послать сообщение о допустимых операциях X X X X X X X X X Послать сообщение о вводе суммы наличных для выдачи X X X X X X X X X Послать сообщение о некооректном секретном ПИН-коде X Послать сообщение о вводе некорректной суммы X Послать сообщение о проблеме соединения с банком X Послать сообщение о недостаточном количестве денег на счету клиента X Послать сообщение о недостаточном количестве денег в банкомате X Послать сообщение о невозможности распечатать чек X Послать сообщение о том, что клиент не забрал наличные X X Послать сообщение о том, что клиент на забрал карту X X Преобразования - Р Загрузить карту кпинета в банкомат X X X X X X X X X X X Зарегистрировать операцию во логе X X X X X X X X X X X Уменьшить сумму на счете абонента X X X X X Извлечь карту из карт-ридера X X X X X X X X X X X Выдать наличные X X X X X Распечать чек X X X X Загрузить наличные в банкомат X X Увеличить сумму на счете абонента X X Рис. 5.37. Описание функции снятия средств с банковской карточки 5.4.4. Проверка правильности внешних спецификаций Рассмотренные до сих пор вопросы касались интерфейса «пользова- тель— программная система». Дальнейшие действия по проектирова- нию должны быть сосредоточены на разработке архитектуры системы. По этой причине и вследствие трудностей, обсуждавшихся при разра- ботке качественной внешней спецификации, проверка полноты и точно- сти внешней спецификации проекта является крайне важной. На этом этапе должна быть занята активная позиция в отношении ошибок. Цель всякой проверки правильности или тестирования — найти как можно больше ошибок, а не показать, что спецификации не содер- жат ошибок. Очень важно понимать это тонкое различие [18]. Необхо- димо проверять спецификации, когда они находятся в черновой форме, так как может возникнуть психологический барьер при проверке доку- мента, имеющего «законченную» печатную форму. 250
В [18] предлагается шесть методов проверки правильности, приме- няемых к внешним спецификациям. Эти методы не исключают друг друга и рекомендуются для последовательного применения. 1. Контроль по правилу «N плюс — минус один» и групповой кон- троль специалистами. Специалисты уровня N— 1 ищут ошибки перево- да, а специалисты уровня N + 1 — ошибки, которые могут привести к ошибкам в последующих процессах. Спецификации должны сверять- ся с целями путем рассмотрения каждой из них и последующего опре- деления адекватности отражения цели в спецификациях. Подробную внешнюю спецификацию должны оценивать люди, отвечающие за раз- работку первоначального внешнего проекта, проектирование структуры программ и баз данных, и люди, отвечающие за подготовку документа- ции пользователя. 2. Контроль со стороны пользователя. Крайне важно получить об- зор и одобрение первоначальной и подробной спецификации со сторо- ны организации пользователя. Если разрабатывается проект, не завися- щий от пользователя, то контрольная группа создается из специалистов проектирующей организации с теми же целями. В ее задачу будет вхо- дить оценка спецификаций с точки зрения пользователя. 3. Контроль по таблицам решений. Если в спецификациях исполь- зуются таблицы решений, тогда для проверки на полноту (поиск всех пропущенных условий) и неоднозначность (идентичные условия приво- дят к разным выходным данным или преобразованиям) могут быть при- менены «механические» методы. 4. Ручная имитация (прокрутка спецификаций). Эффективный при- ем проверки детальных внешних спецификаций - подготовка тестов и последующее использование детальных спецификаций для имитации поведения системы. Этот процесс оценки проектного документа мето- дом его выполнения на тестовых данных часто называется сквозным контролем. Для проверки отдельных внешних функций должны быть выполне- ны следующие действия. Кто-то (не автор спецификации) должен снача- ла построить тесты на бумаге для этой функции, т.е. список конкретных входных данных (допустимых и недопустимых). Вместе с автором спе- цификаций он потом имитирует ввод данных в систему, используя спе- цификации как описание поведения системы. Всякий раз, когда оказы- вается, что спецификация описывает выходные данные или преобразо- вание недостаточно полно и правильно, это означает, что обнаружена ошибка. Эту последовательность проверки внешних функций можно представить следующей схемой (рис. 5.37). Прежде чем начать сквозной контроль, обычно бывает необходимо установить исходное состояние системы, например описать начальные состояния всех файлов, а затем обновлять это состояние по мере ис- пользования тестов. Подобным образом можно тестировать и комбина- ции функций, строя тесты, представляющие собой сложные сценарии. Важно отметить, что цель всякого такого сеанса сквозного контроля — обнаруживать ошибки, но не исправлять их с лету. Исправление ошибок нужно отложить до тех пор, пока не будут обнаружены по возможности все ошибки. Также важно, чтобы строил тесты и интерпретировал спе- 251
цификации кто-то другой, не автор, так как последний будет «читать между строк». Рис. 5.38. Последовательность проверки внешних функций 5. Имитация за терминалом. В этом случае вместо того чтобы про- сто читать список тестов, как при ручной имитации, один из проверяю- щих садится за терминал (персональный компьютер), вводит тестовые данные и получает результаты так, как если бы терминал был подклю- чен к настоящей программной системе (рис. 5.38). Для имитации еще один участник проверки, вооружившись специ- фикациями, садится за другой компьютер. Небольшая программа, спе- циально разработанная для такой проверки, связывает эти компьютеры, передавая входные данные с терминала «пользователя» на терминал «имитатора», а выходные данные — обратно по командам «имитатора» (рис. 5.38). 6. Функциональные диаграммы. Этот метод заимствован из проекти- рования переключательных схем и позволяет проектировщику предста- вить спецификации булевской логической диаграммой (функциональ- ной диаграммой), которая затем преобразуется в таблицу решений. 252
Пользователь с тестами Имитатор системы со спецификациями Рис. 5.39. Схема имитации Литература к главе 5 1. IBM Rational Software Modeler [Электронный ресурс]// URL: http://www.interface.ru/fset. asp ?Url=/rational/RSM.htm 2. SWEBOK [Электронный ресурс] // URL: http://ru.wikipedia.org/ wiki/SWEBOK 3. UML [Электронный ресурс] // URL: http://ru.wikipedia.org/ wiki/UML 4. Амсден Д. Фиксация требований с помощью инструментов Busi- ness Motivation Model, IBM Rational RequisitePro и IBM Rational Software Modeler [Электронный ресурс]: http://www.ibm.com/ developer- works/ru/library/r-0401 _amsden/index.html#author 1 5. Басс Л., Клементс П., Кацман P. Архитектура программного обес- печения на практике. — 2-е изд. — СПб.: Питер, 2006. — 575 с. 6. Буч Г., Рамбо Д., Якобсон И. Язык UML: руководство пользовате- ля: пер. с англ. — 2-е изд. — М.: ДМК Пресс, 2007. — 496 с. 7. Вендров А.М. Проектирование программного обеспечения эконо- мических информационных систем: учебник для вузов. — 2-е изд. — М.: Финансы и статистика, 2006. — 352 с. 8. Гагарина Л.Г., Кокорева Е.В., Виснадул БД. Технология разработ- ки программного обеспечения: учебное пособие / под ред. Л.Г. Гагари- ной. - М.: ФОРУМ; ИНФРА-М, 2008. - 400 с. 9. Гантер Р. Методы управления проектированием программного обеспечения: пер. с англ. - М.: Мир, 1981. - 392 с. 10. Гибе Д. Управление проектами с помощью IBM Rational Unified Process. Практические уроки: пер. с англ. - М.: КУДИЦ-ПРЕСС, 2007. — 304 с. 11. Диаграмма потоков данных (DFD). Графический язык диаграм- мы [Электронный ресурс] И URL: http://e-educ.ru/bdl4.html .2. Зиглер К. Методы проектирования программных систем: пер. с англ. - М.: Мир, 1985. - 328 с. 13. Каляное ГН. CASE: структурный системный анализ (автомати- зация и применение). — М.: ЛОРИ, 1996. 14. Кватрани Т., Палистрает Д. Визуальное моделирование с помо- щью IBM Rational Software Architect и UML: пер. с англ. - М.: КУДИЦ- ПРЕСС. - 2007. - 192 с. 15. Классификация видов моделей [Электронный ресурс] // URL: http://www.uml3.ru/index.php?option=com_content&view=article&id=36:ki nd-of-modcls&catid=3: software-design 253
\6. Колин Ю. Применение Rational Software Architect в разработке, управляемой моделями. Ч. 1: Обзор парадигмы разработок, управляе- мых моделями с шаблонами [Электронный ресурс] // URL: http://www.ibin.coin/developerworks/ru/library/1121_yu/index.html?S_TAC T=105AGX63&S_CMP=NWLTR&ca=dnn-rut2405 " \1.Ларман К. Применение UML 2.0 и шаблонов проектирования.— 3-е изд. Издательский дом «Вильямс», 2006. — 736 с. 18. Майерс Г. Надежность программного обеспечения: пер. с англ. — М.: Мир, 1980.-360 с. 19. Метод функционального моделирования SADT [Электронный ресурс] // URL: http://dic.acadcmic.ru/dic.nsf/politology/1766/%D0%9C %D0%B5%D 1 %82%D0%BE%D0%B4 20. Методологии функционального моделирования [Электронный ресурс] И URL: http://www.mstu.edu.ru/study/materials/zelenkov/ ch_5_3.html 21. Новичков А., Карабанова Г. Моделирование бизнес-процессе в автоматизируемой предметной области при помощи диаграмм деятель- ности с использованием RSA [Электронный ресурс] // URL: http://www. ibm.eom/developerworks/ru/library/r-rsa/index.htinl#authorl 22. Общий обзор и концепция разработки, управляемой моделями [Электронный ресурс]: http://www.ibm.com/developerworks/ru/library/ mdd/ch 1 /ch 1 .html#author 1 23. Описание подключаемого модуля RUP для управляемой моделя- ми разработки систем [Электронный ресурс] // URL: http:// www. interface. ru/ho me. asp ? artld=21714 24. Основы программной инженерии (no SWEBOK) [Электронный ресурс] // URL: http://swebok.sorlik.ru/l_software_requireinents.html 25. Полис Г., Огастин Л., Лоу К., Мадхар Д. Разработка программ- ных проектов на основе Rational Unnified Process (RUP). — M.: Бином- Пресс, 2009. - 256 с. 26. Применение Rational Software Architect в разработке, управляе- мой моделями. Ч. 1: Обзор парадигмы разработок, управляемых моде- лями с шаблонами [Электронный ресурс] // URL: http://www.cmcons.com/articles/drugie_stati/priincnenie_rational_software_ architect_v_razrabotkc_upravljaemojj_modeljami_chast_l_obzor_paradigm y_razrabotok_upravlj aemykh_modelj a misshablonami/ 27. Технология программирования и создание программных продук- тов [Электронный ресурс] // URL: http://www.cs.nuos.edu.ua/textbooks/ SWDevelop/SaprS W1 /index, htm 28. Турский В. Методология программирования: пер. с англ. - М.: Мир, 1981.-264 с. 29. Халл Э., Джексон К, Дик Д. Разработка и управление требова- ниями: практическое руководство пользователя. - Tclclogic, 2005. 30. Химонин Ю. Сбор и анализ требований к программному продук- ту [Электронный ресурс] // URL: http://www.pini.ru/profes/ SoftwareRequir http://ru.wikipcdia.org/wiki/UMLements_Khimonin.pdf
Глава 6 Проектирование архитектуры программных систем 6.1. Методология проектирования По мнению авторов фундаментальных монографий [19, 20, 24, 41], всякую методологию проектирования программных систем можно пред- ставить набором желаемых характеристик результата проектирования и руководящих принципов процесса проектирования. Выше уже отмеча- лось, что формирование желательных целей проектирования сводится к определению требований и целей проектирования. Что касается прин- ципов проектирования, то они определяют сам процесс проектирования и методы проектирования. Принципы проектирования - это обычно не зависящие от пред- метной области стратегии, положенные в основу проектирования. Одна- ко есть принципы, специально относящиеся к проектированию про- граммных (и других) систем для компьютеров. Методы проектирования - это обычно зависящие от области при- ложения (предметной области) тактические средства, определяющие те или иные этапы процесса проектирования. Сам процесс проектирования подразумевает принятие решения о форме и содержании предмета про- ектирования. Метод должен помогать разработчику в упорядочении принимаемых решений. Методы проектирования опираются в конечном счете на свойства мыслительного процесса разработчика и обеспечива- ют определенные алгоритмы принятия решений. Кроме того, метод проектирования должен помочь разработчику сконцентрировать свое внимание на определенных аспектах или частях проектируемой системы, взятых по отдельности. Это особенно важно при разработке больших программных систем (например, операцион- ных систем), где сложность системы заранее исключает всякую возмож- ность охватить сразу все детали. Тем не менее разработчик должен точ- но понимать общие характеристики всей системы, по мере того как они проясняются во время проектирования. Основные принципы (стратегии) процесса проектирования, дающие разработчику возможность справиться со сложностью системы, - это декомпозиция и абстракция. Базовая парадигма (давно известная) в подходе к решению большой задачи - «разделяй и властвуй». К сожалению, буквальное следование этому принципу по-прежнему предполагает долгий путь к решению за- дачи. Самым важным является то, каким образом осуществляется это разделение. Понятно, что по мере возрастания размера программной системы монолитная структура становится неудобной, поскольку ее текст труден для понимания (никто не может понять миллионы строк современных операционных систем). Поэтому программа должна быть разбита на ряд небольших программ (модулей), которые сообща выпол- няют требуемую функцию. 255
Корректность такого разбиения (декомпозиции) становится все бо- лее важной по мере роста программной системы. Это обусловлено сле- дующими причинами. 1. В процесс разработки программной системы вовлекается все боль- шее количество людей. Регулярные контакты между исполнителями снижают вероятность разногласий, касающихся того, кто и что должен делать, и уменьшают возможность серьезных последствий, возникаю- щих в подобных ситуациях. 2. Полезное время жизни программы (рабочая стадия) начинается с момента передачи потребителю. Однако работа над программой не за- канчивается. Работа по модификации и сопровождению программы мо- жет потребовать больше времени, чем время, потраченное на ее разра- ботку. При этом неразумно начинать работу с полной переделки про- граммы. Лучше внести изменения в уже существующую структуру про- граммы. Поэтому важно, чтобы структура программы допускала воз- можность такой модификации. Для этого необходимо, чтобы части про- граммы были независимы друг от друга, с тем чтобы изменение в одной части могло быть сделано без изменений других частей. 3. Со временем модификация программ, время жизни которых доста- точно велико, выполняется уже не разработчиками программ. Все эти факторы предполагают структурирование программ таким образом, что- бы они были легко понимаемыми. Таким образом, большие программные системы целесообразно раз- рабатывать путем декомпозиции задачи. Декомпозиция является полезным и экономящим время способом ре- шения задач в самых различных областях. Однако это не панацея и при неграмотном использовании может принести массу неприятностей. Час- то большие или плохо понимаемые задачи поддаются декомпозиции с большим трудом. К числу распространенных проблем относится си- туация, при которой создание отдельных компонентов, способных ре- шить соответствующие задачи, не приводит к тому, что объединение компонентов позволяет решить исходную задачу. Это является одной из причин, по которой интеграция системы оказывается затруднительной. К тому же нужно помнить и тот факт, что разбиение любой задачи на подзадачи (это наиболее ярко проявляется в математических задачах) и оптимальное решение этих подзадач не даст оптимального решения общей задачи. Абстракция — эффективный способ декомпозиции, осуществляемый посредством изменения списка детализации. Когда разработчик абстра- гируется от проблемы, он предполагает игнорирование ряда подробно- стей, с тем чтобы сделать задачу более простой. Задачи абстрагирования и последующей декомпозиции типичны для процесса создания про- граммной системы: декомпозиция используется для разбиения програм- мы на компоненты (модули), которые затем могут быть объединены, по- зволив решить основную задачу; абстрагирование предполагает проду- манный выбор компонентов. Разработчики выполняют последовательно то один, то другой из этих процессов до тех пор, пока не сведут исход- ную задачу к набору простых подзадач, которые несложно решить. 256
Процесс абстракции можно рассматривать как некоторое обобщение. Он позволяет забыть об особенностях информации и рассматривать раз- личные объекты как если бы они были эквивалентны. Делается это с це- лью упрощения анализа путем отделения существенных атрибутов от несущественных. Однако следует учитывать, что критерий такого отде- ления зависит от контекста. Примером абстракции при программировании может служить кон- цепция файла. Файлы абстрагированы от конкретного носителя инфор- мации, реализуя долговременное и постоянно доступное хранилище по- именованных единиц данных. (Файл — поименованная совокупность данных, хранящаяся на некотором носителе информации.) Другой при- мер абстракции — языки высокого уровня. За счет использования компи- лятора происходит абстрагирование от набора машинных команд, кото- рые различны в разных компьютерах, выполняющих одну и ту же зада- чу, написанную на языке высокого уровня. При использовании современных языков программирования про- граммист может создавать свои собственные абстракции по мере необ- ходимости. Наиболее распространенный механизм такого рода— ис- пользование процедур. Разделяя в программе тело процедуры и обраще- ния к ней, язык высокого уровня реализует два важных метода абстрак- ции: абстракцию через параметризацию и абстракцию через специализа- цию [19]. Абстракция через параметризацию позволяет, используя парамет- ры, представить практически неограниченный набор различных вычис- лений одной программы, которая есть абстракция всех этих наборов. Таким образом, абстракция через параметризацию - важное средство повышения универсальности программ. Абстракция через спецификацию позволяет абстрагироваться от про- цессов вычислений (от алгоритма), описанных в теле процедуры, и ог- раничиться знанием лишь того, что данная процедура должна в итоге реализовать. Это достигается созданием для процедуры спецификации, описывающей эффект ее работы, после чего смысл обращения к данной процедуре становится ясным через анализ этой спецификации, а не са- мого тела процедуры. Одним из способов задания спецификаций являются комментарии в виде пары утверждений. Первое утверждение (начальное условие) за- дает на входе процедуры истинность или ложность некоторого условия (или нескольких условий). Второе утверждение (конечное условие) за- даст некоторое условие, которое предполагается истинным по заверше- нии данной процедуры. Например, это может выглядеть так: Имяпроцедуры (<список параметров) //все параметры >0 //возвращает наибольшее значение тело процедуры 257
При анализе спецификации для уяснения смысла обращения к про- цедуре следует придерживаться двух четких правил: 1) после выполнения процедуры можно считать, что конечное ус- ловие выполнено; 2) можно ограничиваться только теми свойствами, которые подра- зумевают конечное условие. Абстракция через спецификацию по сравнению с абстракцией через параметризацию имеет следующие преимущества: 1) используя процедуру, программисты не обязаны знакомиться с ее телом, т.е. не обязаны знать алгоритм, который она реализует (это боль- шое преимущество при работе со сложными процедурами); 2) второе правило уточняет, что происходит абстрагирование от те- ла процедуры за счет отказа от несущественной информации. Поскольку декомпозиция программных систем на основе абстракции связана с модуляризацией программной системы, имеет смысл более подробно остановиться на понятии и характеристиках модулей. 6.2. Модульность 6.2.1. Модули, модульно-интерфейсный подход, модульное программирование Для уменьшения сложности программной системы она разбивается на множество небольших, в высокой степени независимых модулей. Модуль — это замкнутая программа, которую можно вызвать из любого другого модуля системы. Это фрагмент программного текста, являю- щийся строительным блоком для физической структуры системы. Как правило, модуль состоит из интерфейсной части и части-реализации. Модули можно разрабатывать на различных языках программирования и отдельно компилировать. Высокой степени независимости модулей программной системы можно достичь с помощью двух методов оптими- зации: усилением внутренних связей в каждом модуле и ослаблением взаимосвязи между ними. Модульное программирование возникло в на- чале 60-х гг. XX в. [12, 40]. При создании программных систем оно дает следующие преимущества: • упрощаются разработка и реализация программных систем; • появляется возможность одновременной (параллельной) работы исполнителей, что позволяет сократить сроки создания ПС; • упрощается настройка и модификация ПС; • возникает много естественных контрольных точек для наблюде- ния за продвижением проекта; • можно создавать библиотеки наиболее употребительных про- грамм; • облегчается чтение и понимание программы; • обеспечивается более полное тестирование; 258
• становится проще процедура загрузки в оперативную память боль- шой ПС (эффективность распределения программы по страницам при ра- боте в виртуальной памяти зависит от способа ее разбиения на модули). Наряду с этими преимуществами имеются и некоторые недостатки, которые могут привести к возрастанию стоимости программной систе- мы: • может увеличиться время исполнения программы; • может возрасти размер требуемой памяти; • может увеличиться время компиляции и загрузки; • проблемы организации межмодульного взаимодействия могут ока- заться довольно сложными. Перечислим основные свойства и требования к модулям [36, 40]. 1. Модуль возникает в результате сепаратной компиляции или явля- ется частью результата совместной компиляции. Он может активизиро- ваться операционной системой или быть подпрограммой, вызываемой другим модулем. 2. На содержимое модуля можно ссылаться с помощью его имени. 3. Модуль должен возвращать управление тому, кто его вызвал. 4. Модуль может обращаться к другим модулям. 5. Модуль должен иметь один вход и один выход. Иногда программа с несколькими входами может оказаться короче и занимать меньше мес- та в памяти. Однако опыт модульного программирования [40] показал, что разработчики предпочитают иметь несколько похожих модулей, но не использовать несколько входов и выходов в одном модуле. Это объ- ясняется тем, что единственность входа и выхода гарантирует замкну- тость модуля и упрощает сопровождение программной системы. 6. Модуль сравнительно невелик. Размеры модуля влияют на степень независимости элементов программы, легкость ее чтения и тестирова- ния. Обнаружено, что небольшие модули позволяют строить такие про- граммы, которые легче изменять. Такие модули чаще используются, они облегчают оценку и управление разработкой, их можно рекомендовать и достаточно опытным, и неопытным программистам. Можно было бы удовлетворить критериям высокой прочности и минимального сцепле- ния, спроектировав программу как несколько больших модулей, но вряд ли таким образом была бы достигнута высокая степень независимости. Как правило, модуль должен содержать от 10 до 100 операторов языка высокого уровня (в некоторых публикациях — до 200). С другой стороны, небольшие модули дольше проектируются, мед- леннее работают. Состоят все вместе из большего числа предложений исходного текста, требуют большей документации, их написание может быть менее приятным для программиста. 7. Модуль не должен сохранять историю своих вызовов для управле- ния своим функционированием. Такой модуль называют предсказуе- мым. Модуль, хранящий следы своих состояний при последовательных вызовах, не является предсказуемым. Все модули ПС должны быть предсказуемыми, т.е. не должны сохранять никакой информации о пре- дыдущем вызове. Хитрые, неуловимые, зависящие от времени ошибки 259
возникают в тех программах, которые пытаются многократно вызвать непредсказуемый модуль. 8. Структура принятия решения в модуле должна быть организова- на таким образом, чтобы те модули, на которые прямо влияет принятое решение, были подчиненными (вызываемыми) по отношению к прини- мающему решение модулю. Таким образом, обычно удается исключить передачу специальных параметров-индикаторов, представляющих ре- шения, которые должны быть приняты, а также принимать влияющие на управление программой решения на высоком уровне в иерархии про- граммы. 9. Минимизация доступа к данным. Объем данных, на которые мо- дуль может ссылаться, должен быть сведен к минимуму. Исключение сцепления по общей области, внешним данным и по формату — хороший шаг в этом направлении. Проектировщик должен попытаться изолиро- вать сведения о любой конкретной структуре данных или записи в базе данных в отдельном модуле (или небольшом подмножестве модулей) — возможно, за счет использования информационно прочных модулей. 10. Внутренние процедуры. Внутренняя процедура, или подпрограм- ма, - это замкнутая подпрограмма, физически расположенная в вызы- вающем ее модуле. Таких процедур следует избегать по нескольким причинам. Внутренние процедуры трудно изолировать для тестирова- ния, и они не могут быть вызваны из модулей, отличных от тех, которые их содержат. Это не соответствует идее повторного использования. Ко- нечно, имеется альтернативный вариант - включить копии внутренней процедуры во все модули, которым она нужна. Однако это часто приво- дит к ошибкам (копии часто становятся «не совсем точными») и услож- няет сопровождение программы, поскольку, когда процедура изменяет- ся, все модули нужно перекомпилировать. 6.2.2 Обоснование модульности Модульность — свойство системы, которая может подвергаться де- композиции на ряд внутренне связанных и слабо зависящих друг от дру- га модулей. По определению Г. Майерса, модульность — свойство про- граммного обеспечения, обеспечивающее интеллектуальную возмож- ность создания сколь угодно сложной программы [20]. Эту точку зрения можно проиллюстрировать следующим образом [30]. Пусть С(х) — функция сложности решения проблемы х, Т(х) - функ- ция затрат времени на решение проблемы х. Для двух проблем р 1 и р2 из соотношения С(р 1) > С(р2) следует: Т(р\)>Т(р2). (6.1) Этот вывод интуитивно ясен: решение сложной проблемы требует большего времени. Из многолетней практики решения проблем челове- ком следует, что сложность решения большой проблемы выше, чем сум- марная сложность решения ее частей: С (/Я + />2) > С(р1) + С(р2). (6.2) Отсюда с учетом соотношения (6.1) получаем: 260
Т(р\+р2)>Т(р\) + Т(р2). (6.3) Соотношение (6.3) является обоснованием модульности. Оно приво- дит к известному заключению «разделяй и властвуй»: сложную пробле- му легче решить, разделив ее на управляемые части. Результат, выра- женный неравенством (6.3), имеет важное значение для модульности программных систем. Фактически это аргумент в пользу модульности. Однако здесь отражена лишь часть реальности, поскольку в данном случае не учитываются затраты на межмодульный интерфейс. Как пока- зано на рис. 6.1, с увеличением количества модулей (и уменьшением их размера) эти затраты также растут. Стоимость стоимости Рис. 6.1. Оптимальный размер модуля Таким образом, существует оптимальное количество модулей Opt, которое приводит к минимальной стоимости разработки. К сожалению, необходимого опыта для гарантированного предсказания значения Opt до сих пор не имеется. Однако опыт модульного конструирования про- граммных систем показывает, что оптимальный модуль должен удовле- творять двум критериям: • снаружи он проще, чем внутри; • его проще использовать, чем построить. Принцип информационной закрытости, определенный Д. Парнасом еще в 1972 г., утверждает: содержание модулей должно быть скрыто друг от друга [4]. С развитием объектно ориентированного программи- рования этот принцип стал одним из основополагающих. Модуль дол- жен определяться и проектироваться так, чтобы его содержимое (проце- дуры и данные) было недоступно тем модулям, которые не нуждаются в такой информации (клиентам). Информационная закрытость означает следующее: 1) все модули независимы, обмениваются только информацией, не- обходимой для работы; 2) доступ к операциям и структурам данных модуля ограничен. 261
Достоинствами информационной закрытости являются: • возможность разработки модулей различными, независимыми коллективами; • легкая модификация системы (вероятность распространения оши- бок очень мала, так как большинство данных и процедур скрыто от дру- гих частей системы). Идеальный модуль играет роль «черного» ящика, содержимое кото- рого невидимо клиентам. Он прост в использовании: количество «ручек и органов управления» им невелико (аналогия с эксплуатацией телеви- зора). Его легко развивать и корректировать в процессе сопровождения программной системы [30]. Для обеспечения таких возможностей систе- ма внутренних и внешних связей модуля должна отвечать определен- ным требованиям. Рассмотрим характеристики внутренних и внешних связей модуля. 6.2.3. Внутренняя характеристика модуля — связность (прочность) Связность (прочность) модуля (cohesion) - это мера зависимости его частей [3, 5]. Связность - внутренняя характеристика модуля. Чем выше связность модуля, тем лучше результат проектирования, т.е. тем «чернее» его ящик (капсула, защитная оболочка модуля), тем меньше «ручек управления» на нем находится и тем проще эти «ручки». Для измерения связности используют понятие силы связности (СС). Существует 7 типов связности (в различных источниках [14, 20, 30] дан- ные несколько различаются). 1 . Связность по совпадению (СС = 0). В модуле отсутствуют явно выраженные внутренние связи. 2 . Логическая связность (СС=1). Части модуля объединены по принципу функционального подобия. Например, модуль состоит из раз- ных подпрограмм обработки ошибок. При использовании такого модуля клиент выбирает только одну из подпрограмм. Недостатками такой связности являются сложное сопряжение и большая вероятность внесе- ния ошибок при изменении сопряжения ради одной из функций. 3 . Временная связность (СС = 3). Части модуля не связаны, но необ- ходимы в один и тот же период работы системы. Недостаток: сильная взаимная связь с другими модулями, отсюда - сильная чувствитель- ность внесению изменений. 4 . Процедурная связность (СС = 5). Части модуля связаны порядком выполняемых ими действий, реализующих некоторый сценарий поведе- ния. 5 . Коммуникативная связность (СС - 7). Части модуля связаны по данным (работают с одной и той же структурой данных). 6 . Информационная (последовательная) связность (СС - 9). Выход- ные данные в одной части используются как входные данные в другой части модуля. 7 . Функциональная связность (СС = 10). Части модуля вместе реали- зуют одну функцию. 262
Отметим, что типы связности 1,2, 3 — результат неправильного пла- нирования архитектуры, а тип связности 4 — результат небрежного пла- нирования архитектуры приложения. Общая характеристика типов связ- ности представлена в табл. 6.1. Рассмотрим теперь каждый тип связности более подробно. Функциональная связность. Функционально связный модуль содер- жит элементы, участвующие в выполнении одной, и только одной, про- блемной задачи. Примеры функционально связных модулей [30]: • вычислять синус угла; • проверять орфографию; • читать запись файла; • вычислять координаты цели. Таблица 6.1. Характеристика типов связности Тип связности Сопровож- даемость Роль модуля Функциональная Лучшая со- провождае- мость «Черный» ящик Информационная (последовательная) Не совсем «черный» ящик Коммуникативная «Серый» ящик Процедурная Худшая сопровождае- мость «Белый», или «просвечивающийся», ящик Временная «Белый» ящик Логическая По совпадению Каждый из этих модулей имеет единичное назначение. Когда клиент вызывает модуль, выполняется только одна работа, без привлечения внешних обработчиков. Некоторые из функционально связных модулей очень просты (например, «Вычислять синус угла» или «Читать запись файла»), другие сложны (например, «Вычислять координаты цели»). Модуль «Вычислять синус угла», очевидно, реализует единичную функ- цию. Но может ли модуль «Вычислять зарплату сотрудника» выполнять только одно действие? Дело в том, что, несмотря на сложность модуля и на то, что его обязанность исполняют несколько подфункций, если его действия можно представить как единую проблемную функцию, счита- ют, что модуль функционально связан. Приложения, построенные из функционально связных модулей, лег- че сопровождать. Хотелось бы любой модуль рассматривать как одно- функциональный, но это не всегда возможно. Существует много разно- видностей модулей, которые выполняют для клиентов перечень различ- ных работ, и этот перечень нельзя рассматривать как единую проблем- 263
ную функцию. Критерий при определении уровня связности этих не- функциональных модулей - в установлении того, как связаны друг с другом различные действия, которые они исполняют. Информационная связность. При информационной (последователь- ной) связности элементы-обработчики модуля образуют конвейер для обработки данных: результаты одного обработчика используются как исходные данные для следующего обработчика. Например, модуль «Прием и проверка записи» может содержать следующие элементы-об- работчики: • прочитать запись из файла, • проверить контрольные данные в записи, • удалить контрольные поля в записи, • вернуть обработанную запись. В этом модуле 4 элемента. Результаты первого элемента (прочитать запись из файла) используются как входные данные для второго элемен- та (проверить контрольные данные в записи) и т.д. Сопровождать моду- ли с информационной связностью почти так же легко, как и функцио- нально связные модули. Правда, возможности повторного использова- ния здесь ниже, чем в случае функциональной связности. Причина: со- вместное применение действий модуля с информационной связностью полезно далеко не всегда. Коммуникативная связность. При коммуникативной связности эле- менты-обработчики модуля используют одни и те же данные, например внешние данные. Пример коммуникативно-связного модуля - модуль «Отчет и средняя зарплата». В данном случае используется «Таблица зарплаты служащих» со следующими элементами-обработчиками: • сгенерировать «Отчет по зарплате»; • вычислить параметр «Средняя зарплата»; • вернуть «Отчет по зарплате», «Средняя зарплата». Здесь все элементы модуля работают со структурой «Таблица зар- платы служащих». С точки зрения клиента, проблема применения коммуникативно связного модуля состоит в избыточности получаемых результатов. На- пример, клиенту требуется только отчет по зарплате, он не нуждается в значении средней зарплаты. Такой клиент будет вынужден выполнять избыточную работу - выделение в полученных данных материала отче- та. Почти всегда разбиение коммуникативно связного модуля на отдель- ные функционально связные модули улучшает сопровождаемость сис- темы. Попытаемся провести аналогию между информационной и коммуни- кативной связностью. Модули с коммуникативной и информационной связностью подобны в том, что содержат элементы, связанные по дан- ным. Их удобно использовать, потому что лишь немногие элементы в этих модулях связаны с внешней средой. Главное различие между ни- ми в том, что информационно связный модуль работает подобно сбо- рочной линии; его обработчики действуют в определенном порядке; в коммуникативно связном модуле порядок выполнения действий без- 264
различен. В нашем примере не имеет значения, когда генерируется от- чет (до, после или одновременно с вычислением средней зарплаты). Процедурная связность. При достижении процедурной связности мы попадаем в пограничную область между хорошей сопровождаемостью (для модулей с более высокими уровнями связности) и плохой сопрово- ждаемостью (для модулей с более низкими уровнями связности). Про- цедурно связный модуль состоит из элементов, реализующих независи- мые действия, для которых задан порядок работы, т.е. порядок передачи управления. Зависимости по данным между элементами нет. Например, модуль «Вычисление средних значений» использует «Таблица-А» и «Таблица-В» и содержит элементы (пример из [30]): • вычислить среднее по «Таблица-А»; • вычислить среднее по «Таблица-В»; • вернуть среднее «Табл-А» и «среднее Табл-В». Этот модуль вычисляет средние значения для двух полностью несвя- заных таблиц: «Таблица-А» и «Таблица-В», каждая из которых имеет по 300 элементов. Теперь представим себе программиста, которому поручили реализо- вать данный модуль. Соблазнившись возможностью минимизации кода (использовать один цикл в интересах двух обработчиков, ведь они нахо- дятся внутри единого модуля!), программист пишет: //Модуль «Вычисление средних значений» //используется «Таблица-А». «Таблица-В» сумма «Табл-А»: = 0 сумма «Табл-В»: = 0 для i: = 1 до 300 сумма «Табл-А»: — сумма «Табл-А» + «Таблица-A(i)» сумма «Табл-В»: = сумма «Табл-В» + «Таблица-В(i)» конец для среднее «Табл-А»: = сумма «Табл-А» /300 среднее «Табл-В»: - сумма «Таб)л-В» /300 вернуть среднее «Табл-А», среднее «Табл-В» //Конец модуля. Для процедурной связности этот случай типичен: независимый (на уровне проблемы) код стал зависимым (на уровне реализации). Прошли годы, продукт сдали заказчику. И вдруг возникла задача сопровожде- ния - модифицировать модуль под уменьшение размера «Таблицы-В». Оцените, удобно ли ее решать? Временная связность. При связности по времени элементы-обработ- чики модуля привязаны к конкретному периоду времени (из жизни про- граммной системы). Классическим примером временной связности является модуль ини- циализации [30]: //Модуль «Инициализировать систему» Перемотать магнитную ленту 1 Счетчик магнитной ленты 1 : — 0 Перемотать магнитную ленту 2 265
Счетчик магнитной ленты 2 : = О Таблица текущих записей : - пробел..пробел Таблица количества записей : = 0.. О Переключатель 1 : = выкл Переключатель 2 : = вкл //Конец модуля. Элементы данного модуля почти не связаны друг с другом (за ис- ключением того, что должны выполняться в определенное время). Они все — часть программы запуска системы. Зато элементы более тесно взаимодействуют с другими модулями, что приводит к сложным внеш- ним связям. Модуль со связностью по времени приводит к тем же труд- ностям, что и процедурно связный модуль. Программист соблазняется возможностью совместного использования кода (действиями, которые связаны только по времени), модуль становится трудно использовать повторно. Так, при желании инициализировать магнитную ленту 2 в другое время можно столкнуться с неудобствами. Чтобы не сбрасы- вать всю систему, придется или ввести флажки, указывающие инициа- лизируемую часть, или написать другой код для работы с лентой 2. Оба решения ухудшают сопровождаемость. Процедурно связные модули и модули с временной связностью очень похожи. Степень их непрозрачности изменяется от темного серо- го до светло-серого цвета, так как трудно объявить функцию такого мо- дуля без перечисления ее внутренних деталей. Различие между ними по- добно различию между информационной и коммуникативной связно- стью. Порядок выполнения действий более важен в процедурно связных модулях. Кроме того, процедурные модули имеют тенденцию к совме- стному использованию циклов и ветвлений, а модули с временной связ- ностью чаще содержат более линейный код. Логическая связность. Элементы логически связного модуля при- надлежат к действиям одной категории, и из этой категории клиент вы- бирает выполняемое действие. Рассмотрим следующий пример [30]: //Модуль «Пересылка сообщения» Переслать по электронной почте Переслать по факсу Послать в телеконференцию Переслать по ftp-протоколу //Конец модуля. Как видно, логически связный модуль - «мешок» доступных дейст- вий. Действия вынуждены совместно использовать один и тот же интер- фейс модуля. В строке вызова модуля значение каждого параметра зави- сит от используемого действия. При вызове отдельных действий неко- торые параметры должны иметь значение пробела, нулевые значения и т.д. (хотя клиент все же должен использовать их и знать их типы). Действия в логически связном модуле попадают в одну категорию, хотя имеют не только сходства, но и различия. К сожалению, это застав- ляет программиста «завязывать код действий в узел», ориентируясь на 266
то, что действия совместно используют общие строки кода. Поэтому ло- гически связный модуль имеет: • уродливый внешний вид с различными параметрами, обеспечи- вающими, например, четыре вида доступа; • запутанную внутреннюю структуру с множеством переходов, по- хожую на волшебный лабиринт. В итоге модуль становится сложным как для понимания, так и для сопровождения. Связность по совпадению. Элементы связного по совпадению моду- ля вообще не имеют никаких отношений друг с другом: //Модуль «Разные функции» (какие-то параметры) Поздравить с Новым годом (...) Проверить исправность аппаратуры (...) Заполнить анкету героя (...) Измерить температуру (...) Вывести собаку на прогулку (...) Запастись продуктами (...) Приобрести «ягуар» (...) //Конец модуля. Связный по совпадению модуль похож на логически связный мо- дуль. Его элементы-действия не связаны ни потоком данных, ни пото- ком управления. Но в логически связном модуле действия, по крайней мере, относятся к одной категории; в связном по совпадению модуле да- же это не так. Словом, связные по совпадению модули имеют все недос- татки логически связных модулей и даже усиливают их. Применение та- ких модулей вселяет ужас, поскольку один параметр используется для разных целей. Чтобы клиент мог воспользоваться модулем «Разные функции», этот модуль (подобно всем связным по совпадению модулям) должен быть «белым» ящиком, чья реализация полностью видима. Такие модули де- лают системы менее понятными и труднее сопровождаемыми, чем сис- темы без модульности вообще! Обычно связность по совпадению встречается редко. Среди ее при- чин можно назвать: • бездумный перевод существующего монолитного кода в модули; • необоснованные изменения модулей с плохой (обычно временной) связностью, приводящие к добавлению флажков. Для определения связности модуля можно использовать следующий алгоритм. 1. Если модуль - единичная проблемно-ориентированная функция, то уровень связности функциональный; конец алгоритма. В противном случае перейти к п. 2. 2. Если действия внутри модуля связаны, то перейти к п. 3. Если дей- ствия внутри модуля никак не связаны, то перейти к п. 6. 3. Если действия внутри модуля связаны данными, то перейти к п. 4. Если действия внутри модуля связаны потоком управления, перейти к п. 5. 267
4. Если порядок действий внутри модуля важен, то уровень связно- сти информационный. В противном случае уровень связности коммуни- кативный. Конец алгоритма. 5. Если порядок действий внутри модуля важен, то уровень связно- сти процедурный. В противном случае уровень связности временной. Конец алгоритма. 6. Если действия внутри модуля принадлежат к одной категории, то уровень связности логический. Если действия внутри модуля не принад- лежат к одной категории, то уровень связности — по совпадению. Конец алгоритма. Возможны более сложные случаи, когда с модулем ассоциируется несколько уровней связности. В этих случаях следует применять одно из двух правил: • правило параллельной цепи (если все действия модуля имеют не- сколько уровней связности, то модулю присваивают самый сильный уровень связности); • правило последовательной цепи (если действия в модуле имеют разные уровни связности, то модулю присваивают самый слабый уро- вень связности). Например, модуль может содержать некоторые действия, которые связаны процедурно, а также другие действия, связные по совпадению. В этом случае применяют правило последовательной цепи и в целом модуль считают связным по совпадению. 6.2.4. Сцепление люду лей — внешняя характеристика модуля Сцепление (coupling) — мера взаимозависимости модулей по дан- ным [3]. Сцепление — внешняя характеристика модуля, которую жела- тельно уменьшать. Независимые модули могут быть модифицированы без переделки каких-либо других модулей. Количественно сцепление измеряется степенью сцепления (СЦ). Выделяют 7 типов сцепления. Следует заметить, что у различных авторов [14, 20, 30] могут быть неко- торые различия в оценке сцепления. 1. Полностью независимые модули (СЦ = 0). Модули, не вызываю- щие друг друга и не использующие общих данных, не сцеплены и явля- ются полностью независимыми. Чем больше информации о других мо- дулях используется в них, тем менее они независимы и тем сильнее сце- плены. 2. Сцепление по данным (СЦ = 1). Модуль А вызывает модуль В. Все входные и выходные параметры вызываемого модуля — простые элемен- ты данных. 3. Сцепление по образцу (СЦ = 3). В этом случае модули ссылаются на одну и ту же глобальную структуру данных. Недостатком такого сце- пления является то, что оба модуля должны знать о внутренней структу- ре данных. Если программист, сопровождающий программу, модифици- рует структуру данных в одном из модулей, он вынужден будет изме- нить структуру данных также и в другом. 268
4. Сцепление по общей области (СЦ = 5). Модули разделяют одну и ту же глобальную структуру данных. В этом случае возможностей для появления ошибок при модификации структуры данных в одном модуле много больше. По изменениям, производимым в объявленных парамет- рах, сразу можно определить модули, на которые эти изменения повлия- ют. Если модифицируются неявно заданные параметры, определить мо- дули, нуждающиеся в корректировке, труднее, если только не существу- ет документации, отражающей использование модулями общих облас- тей. 5. Сцепление по управлению (СЦ = 7). Модуль А явно управляет функционированием модуля В с помощью передачи флагов, переключа- телей или кодов, посылая ему управляющие данные. Возвращение фла- га состояния как неявной переменной не означает сцепления по управ- лению. Если модуль передает информацию о самом себе или об обрабо- танных данных, то это не всегда служит проявлением сцепления по управлению. Передача флага конца файла позволяет решить вопрос о возможности обработки этого файла. Установка флага, указывающего, какой именно способ доступа ис- пользуется в операции обмена (последовательный или прямой), означа- ет, что осуществляется сцепление по управлению, когда это влияет на модуль обмена. Если модуль имеет логическую связность и при его вы- зове используется переключатель, указывающий на требуемую функ- цию, вызываемый и вызывающий модули сцеплены по управлению. 6. Сцепление по внешним ссылкам (СЦ - 9). Модуль имеет сцепление по внешним ссылкам, если у него есть доступ к данным в другом моду- ле через внешнюю точку входа. Таким путем осуществляется неявное управление функционированием другого модуля. Сцепление такого ти- па возникает, например, при использовании некоторых языков програм- мирования («Паскаль»), когда внутренние процедуры оперируют с гло- бальными переменными. 7. Сцепление по кодам (содержанию) (СЦ = 10). Один модуль прямо ссылается на содержание другого модуля (не через его точку входа). На- пример, коды их команд перемежаются друг с другом. 6.2. Сложность программной системы 6.3.1. Методы о цепки сложност и Оценка сложности программной системы имеет большое значение в реализации проекта этой системы. От сложности системы зависит про- изводительность труда разработчиков, а следовательно, трудоемкость, сроки разработки и стоимость проекта. Между сложностью и надежно- стью программной системы также существует тесная связь. Очевидно, что чем выше сложность программной системы, тем труднее обеспечить ее надежность. При оценке сложности программ, как правило, выделяют три основ- ные группы метрик: метрики размера программ, метрики сложности по- 269
тока управления программ и метрики сложности потока данных про- грамм [28, 29]. Оценки первой группы наиболее просты и поэтому полу- чили широкое распространение. Традиционной характеристикой разме- ра программ является количество строк исходного текста. Под строкой понимается любой оператор программы. К группе оценок размера про- грамм можно отнести метрику М. Холстеда. За базу принят подсчет ко- личества операторов и операндов, используемых в программе, т.е. опре- деление размера программы. Непосредственное измерение размера программы, несмотря на свою простоту, дает хорошие результаты. Оценка размера программы недос- таточна для принятия решения о ее сложности, но вполне применима для классификации программ, существенно различающихся объемами. При уменьшении различий в объеме программ на первый план выдвига- ются оценки других факторов, оказывающих влияние на сложность. Та- ким образом, оценка размера программы есть оценка по номинальной шкале, на основе которой определяются только категории программ без уточнения оценки для каждой категории. Одно из основных правил программирования — соблюдение просто- ты. Более простой считается та программа, которая легче для понима- ния отладки, сопровождения и модификации. Однако не существует об- щепринятого мнения, что делает программу простой. Проще та про- грамма, которая использует привычную систему обозначений. Програм- ма с небольшим числом уровней вложенности проще, чем программа, в которой таких уровней много. Существует еще ряд правил достиже- ния простоты программ. Вероятно, проще вся программная система, ко- торая тщательно спроектирована. Однако в любом случае желательно уметь определять меру сложности программной системы. В простейшем случае сложность системы определяется как сумма мер сложности ее модулей. Сложность модуля может вычисляться различными способа- ми. Например, М. Холстед предложил такую меру длины N модуля [39]: N ~ nl- log2(«l) + п2 -log2(«2), (6.4) где и 1 — число различных операторов, п2 — число различных операндов. В качестве второй метрики М. Холстед рассматривал объем V моду- ля (количество символов для записи всех операторов и операндов текста программы): Г = ?Mog2(Kl + K2). (6.5) Вместе с тем известно, что любая сложная система состоит из эле- ментов и системы связей между элементами и что игнорировать внутри- системные связи неразумно. Вторая наиболее представительная группа оценок сложности программ - метрики сложности потока управления программ. Как правило, с помощью этих оценок оперируют либо плот- ностью управляющих переходов внутри программ, либо взаимосвязями этих переходов. Одной из наиболее простых, но достаточно эффективных оценок сложности программ является метрика Т. Джилба [30], в которой логи- ческая сложность программы определяется как насыщенность програм- 270
мы выражениями IF_THEN_ELSE. При этом вводятся две характеристи- ки: 1) CL — абсолютная сложность программы, характеризующаяся ко- личеством операторов условия; 2) cl — относительная сложность программы, характеризующаяся на- сыщенностью программы операторами условия, т.е. cl определяется как отношение CL к общему числу операторов. Используя метрику Т. Джилба, ее дополнили еще одной составляю- щей, а именно характеристикой максимального уровня вложенности оператора CL1, что позволило применить метрику Т. Джилба к анализу циклических конструкций. Т. МакКейб при оценке сложности ПС предложил исходить из топо- логии внутренних связей [2]. Для этой цели он разработал метрику цик- ло магической сложности: F(G) = £-7V + 2, (6.6) где Е — количество дуг, a N — количество вершин в управляющем графе ПС. Это был шаг в нужном направлении. Дальнейшее уточнение оценок сложности потребовало, чтобы каждый модуль мог представляться как локальная структура, состоящая из элементов и связей между ними. Большой интерес представляет оценка сложности программ по мето- ду граничных значений [28]. В этом методе вводится несколько допол- нительных понятий, связанных с графом программы. Пусть имеется G = (И, Е) — ориентированный граф программы с единственной начальной и единственной конечной вершинами (рис. 6.2). В этом графе число входящих в вершину дуг называется отри- цательной степенью вершины, а число исходящих из вершины дуг — по- ложительной степенью вершины. Тогда набор вершин графа можно раз- бить на две группы: • вершины, у которых положительная степень < 1; • вершины, у которых положительная степень > 2. Вершины первой группы назовем принимающими вершинами, а вер- шины второй группы — вершинами отбора. Для получения оценки по методу граничных значений необходимо разбить граф G на максималь- ное число подграфов G’, удовлетворяющих следующим условиям: • вход в подграф осуществляется только через вершину отбора; • каждый подграф включает вершину (называемую нижней грани- цей подграфа), в которую можно попасть из любой другой вершины подграфа (например, вершина отбора, соединенная сама с собой дугой петлей, образует подграф). Число вершин, образующих такой подграф, равно скорректирован- ной сложности вершины отбора. Для примера графа по рис. 6.2 можно составить таблицу характеристик подграфов (табл. 6.2). Каждая принимающая вершина имеет скорректированную слож- ность, равную 1, кроме конечной вершины, скорректированная слож- ность которой равна 0. Скорректированные сложности всех вершин гра- фа G суммируются, образуя абсолютную граничную сложность про- 271
граммы. После этого определяется относительная граничная сложность программы: = 1 —(v —1)75^, где 5О— относительная граничная слож- ность программы; — абсолютная граничная сложность программы, v — общее число вершин графа программы. Рис. 6.2. Пример графа Таблица 6.2. Характеристики подграфов Характеристики подграфов программ Вершины отбора а b c d Вершины перехода Ъ, с b, d e, f g,h Скорректированная слож- ность вершины графа 10 2 3 3 Вершины подграфа Ь, с, d,e, f, g, h, i,j b e, f g,h Нижняя граница подграфа k d j j Относительная сложность программы равна: So = 1 -(11/25) = 0,56. (6.7) Таким образом, при комплексной оценке сложности ПС необходимо рассматривать меру сложности модулей, меру сложности внешних свя- зей (между модулями) и меру сложности внутренних связей (внутри мо- дулей). Традиционно с внешними связями сопоставляют характеристи- ку «сцепление», а с внутренними связями — характеристику «связность». Иерархическая структура программной системы — основной резуль- тат предварительного проектирования. Она определяет состав модулей ПС и управляющие отношения между модулями. В этой структуре мо- дуль более высокого уровня (начальник) управляет модулем нижнего уровня (подчиненным). Иерархическая структура не отражает проце- дурные особенности программной системы, т.е. последовательность 272
операций, их повторение, ветвления и т.д. Рассмотрим основные харак- теристики иерархической структуры, представленной на рис. 6.3 [30'. Рис. 6.3. Иерархическая структура программной системы Первичными характеристиками являются количество вершин (моду- лей) и количество ребер (связей между модулями). К ним добавляются две глобальные характеристики: высота — количество уровней управле- ния; и ширина - максимальное из количеств модулей, размещенных на уровнях управления. В примере по рис. 6.3 высота = 4, ширина = 6. Локальными характеристиками модулей структуры являются коэф- фициент объединения по входу и коэффициент разветвления по выходу. Коэффициент объединения по входу Fan_in(i) — это количество моду- лей, которые прямо управляют г-м модулем. В примере для модуля и: Fan_in(ri) = 4. Коэффициент разветвления по выходу Fanout(i) - это ко- личество модулей, которыми прямо управляет 7-й модуль. В примере для модуля т: Fanoutfm) = 3. Возникает вопрос: как оценить качество структуры? Из практики проектирования известно, что лучшее решение обеспечивается иерархи- ческой структурой в виде дерева. Степень отличия реальной проектной структуры от дерева характеризуется невязкой структурой. Как опреде- лить невязку? Известно, что полный граф (complete graph) с п вершинами имеет ко- личество ребер ес = п * (п —1)/2, а дерево с таким же количеством вер- шин — существенно меньшее количество ребер е, = п -1. Тогда формулу невязки можно построить, сравнивая количество ре- бер полного графа, реального графа и дерева. Для проектной структуры с п (вершинами) и е (ребрами) невязка определяется по выражению N = е~е‘ = (е-» + 1)2 = 2(е-и + 1) " е,-е, и-(п-1)-2(и-1) (л-1)(п-2)' V 7 Значение невязки лежит в диапазоне от 0 до 1. Если = 0, то про- ектная структура является деревом, если Nev = 1, то проектная структу- ра - полный граф. 273
Ясно, что невязка дает грубую оценку структуры. Для увеличения точности оценки следует применить характеристики связности и сцеп- ления. Хорошая структура должна иметь низкое сцепление и высокую связность. Л. Констентайн и Э. Иордан (1979) предложили оценивать структуру с помощью коэффициентов Fan_in(i) и Fan_out(i) модулей [5]. Большое значение Fan_in(i) — свидетельство высокого сцепления, так как являет- ся мерой зависимости модуля. Большое значение Fan_out(i) говорит о высокой сложности вызывающего модуля. Причиной является то, что для координации подчиненных модулей требуется сложная логика управления. Основной недостаток коэффициентов Fan_in(t) и Fan_out(i) состоит в игнорировании веса связи. Здесь рассматриваются только управляю- щие потоки (вызовы модулей). В то же время информационные потоки, нагружающие ребра структуры, могут существенно изменяться, поэто- му нужна мера, которая учитывает не только количество ребер, но и ко- личество информации, проходящей через них. С. Генри и Д. Кафура (1981) ввели информационные коэффициенты ifan_in(i) \^ifan_out(j) [1]. Они учитывают количество элементов и структур данных, из которых /-й модуль берет информацию и которые обновляются/-м модулем соответственно. Информационные коэффици- енты суммируются со структурными коэффициентами sfaninfi) и sfan_out(j), которые учитывают только вызовы модулей. В результате формируются полные значения коэффициентов: FanJ,n (г) = sfanjn (z) + ifan_in (z), Fan out (j) = sfanout (j) + ifanout (j). (6.9) На основе полных коэффициентов модулей вычисляется метрика об- щей сложности структуры: 5 = length(i) - (Fan_in(i) + Fan _ out(i))2, (6.10) где length(i) — оценка размера z-го модуля (в виде LOC- или FP-оценки). 6.3.2. Оценка сложности на основе связности и сцепления модулей Одна из возможных моделей сложности модульной программной системы основывается на основных характеристиках ее модулей - связ- ности каждого модуля и сцеплении каждой пары модулей. Выше была дана числовая оценка (от 0 до 10) связности и сцепления модулей. Кста- ти заметим, что различные авторы дают в этом диапазоне несколько иные значения для каждого типа характеристики модуля. Для оценки сложности 5 программной системы выберем числовой диапазон от 0 до 1. При этом будем считать, что высокая связность и слабое сцепле- ние характеризуются числом, близким к нулю. Таким образом, чем бли- же значение £ к единице, тем сложнее ПС. При таком подходе к значе- нию числовой оценки нужно изменить значения коэффициентов СС (си- ла связности) и СЦ (сила сцепления) таким образом, как это приведено в табл. 6.3 и 6.4 [25]. 274
Таблица 6.3. Уровни связности модулей Связность модуля Степень связности Функциональная 0 (сильная связность) Информационная (последовательная) 0.1 Комм у никативная 0.3 Процедурная 0,5 Временная 0,7 Логическая 0,9 По совпадению 1 (слабая связность) Таблица 6.4. Уровни сцепления модулей Сцепление модулей Степень сцепления Независимое 0 (слабое сцепление) По данным 0,1 По образцу 0.3 По общей области 0,5 По управлению 0,7 По внешним ссылкам 0,9 По кодам 1,0 (сильное сцепление) Далее предполагается, что зависимость первого порядка между каж- дой парой модулей задается по формуле [20] 0,15 (л, + 5у) + 0,7су, если 0, если Су = 0, 1, если / =j\ * 0, (6.11) Величина dtJ представляет вероятность того, что модуль j придется изменить, если будет изменен модуль /, если рассматривать модули i и j вне контекста всей программной системы (отношение считается сим- метричным). Величины $,-и .^обозначают связность этих модулей, aсу— сцепление. Если сцепления нет, то = 0. Матрица D может быть представлена неориентированным графом, вершинами которого служат модули, а ребрами — ненулевые значения зависимости первого порядка. Эта матрица не отображает полную мо- дель программы, так как в ней не отражены явно влияния зависимостей более высоких порядков (т.е. зависимости второго, третьего и т.д. по- рядков). На рис. 6.4 показана совокупность модулей программы А, В, С, D, Е. Если изменится модуль А, то могут потребовать изменения модули В и С (зависимости первого порядка). Однако может потребоваться из- менить модуль Вив том случае, если после изменения модуля А был изменен модуль С (зависимость второго порядка: ребра АС — СВ). Как видно из рис. 6.4, для модуля В могла бы быть и зависимость третьего порядка: ребра АС — CD — DE - ЕВ (ребро ЕВ показано пунктиром). 275
Рис. 6.4. Граф модулей программы Для полного изображения зависимостей между любой парой моду- лей вычисляется полная матрица зависимостей. Это делается с помо- щью следующей последовательностью шагов. 1. Найти все пути в графе (исключая циклы) между каждой парой модулей. 2. Вычислить вероятности для каждого найденного пути как произ- ведения вероятностей для соответствующих дуг. 3. Вычислить зависимости между модулями, используя вероятности для путей, но не считая пути взаимно исключающими. В результате будет получена вероятность того, что будет необходи- мо изменить один из модулей в случае изменения другого (снова пред- полагается, что отношение симметрично). Рассмотрим пример. Пусть для графа модулей, изображенного на рис. 6.4, известны следующие исходные данные, характеризующие связ- ность и сцепление модулей: = 0,5; = 0,7; — 0,3; = 0,9; s£ = 1,0; cab ~ 0,5; cAC = 0,7; cCB = 0,3; cCD = 0,1; cDE = 0.3. Вычисляем элементы матрицы первого порядка, которые соответст- вуют вероятностям дуг графа: dAB = 0,15 * (0,5 + 0,7) + 0,7 * 0,5 = 0,53; dAC = 0,15 * (0,5 + 0,3) + 0,7 * 0,7 = 0,61; dCB = 0,15 * (0,3 + 0,7) + 0,7 * 0,3 = 0,36; JCD = 0,15 * (0,3+ 0,9)+ 0,7 *0,1 =0,25; dDE = 0,15 * (0,9 + 1,0) + 0,7 * 0,3 = 0,495. По полученным данным можно построить матрицу первого порядка (рис. 6.5). Переходим к построению полной матрицы зависимостей. Для этого (в нашем простом примере визуально) определяем все пути для каждой пары модулей: 1. Пара А, В: путь А — В и путь А — С - В. 2. Пара А, С: путь А — С и путь А — В — С. 3. Пара A, D: путь А — С - D и путь А — В — С — D. 4. Пара А, Е: путь А - С - D - Е и путь А — В - С — D - Е. 5. Пара В, С: путь В — С и путь В - А - С. 6. Пара В, D: путь В — С — D и путь В — А - С — D. 7. Пара В, Е: путь В—С — D — Ей путь В-A — C-D-E. 8. Пара D, С: путь С - D. 276
9. Пара D, Е: путь D — Е. 10. Пара Е, С: путь Е — D — С. А в с D Е А 1,00 0,53 0,61 0 0 В 0,53 1,00 0,36 0 0 С 0,61 0,36 1,00 0,25 0 D 0 0 0,25 1,00 0,495 Е 0 0 0 0,495 1,00 Рис. 6.5. Матрица зависимостей модулей первого порядка Вычисляем значения вероятностей для найденных путей, используя значения вероятностей дуг графа. 1. 7^=^ = 0,53; РАСВ = dAC* dCB = 0,6\ * 0,36 = 0,22. 2- Рас = dAC = 0,61; РАВС = dAB * dBC = 0,53 * 0,36 = 0,19. 3. PACD=dCA*dCD = 0,61 * 0,25 = 0,15; P.SCD = ^5 * dBC * dCD = 0,53 * * 0,36 * 0,25 =0,05. 4. Pacde ~ dAC * dCD * dDE ~ 0,61 * 0,25 * 0,495 = 0,15; PABcde = dAB * * dBC * dCD * dDE = 0,53 * 0,36 * 0,25 * 0,495 = 0,02. 5- Psc=dBC= 0,36; PBAC= dBA *dAC = 0,53 * 0,61 = 0,32. 6- Pbcd ~ dBc * dCD = 0,36 * 0,25 = 0,09; PBAcd ~ dBA * dAC * dCD = 0,53 * * 0,61 * 0,25 =0,08. 7. Pbcde ~ dBc * dCD * dDE = 0,36 * 0,25 * 0,495 = 0,044; PBAcde ~ dBA * * dAC * dCD * dDE = 0,53 * 0,61 * 0,25 * 0, 495 = 0,04. 8. Pcd ~ dcD ~ 0,25. 9. PD£ = JD£= 0,495. \0. PEDC = dED *dDC = 0,495 * 0,25 =0,12. Теперь, используя найденные вероятности путей с учетом того, что пути между парой модулей не являются взаимоисключающими, опреде- лим зависимости между модулями. 1. Пара А, В. Здесь возможны два пути, поэтому d АВ = Рав + Расв — - Рав + Расв = 0,53 + 0,22 - 0,53 * 0,22 = 0,63. } 2. Пара А, С. Здесь также имеется два пути, поэтому d АС — Ра вс + + Расв -Равс + Рас = 0,19 + 0,61 - 0,19 * 0,61 = 0,68. 3. Пара A, D. Имеет два пути для возможных изменений. Зависи- мость между этими модулями определяется значением d АЕ) = Pacd + + Pabcd ~ Pacd * Pabcd = 0,15 + 0,05 - 0,15 * 0,05 =0,19. 4. Пара A, E. Также имеется два пути, следовательно, d аЕ = Pacde + + Pabcde ~ Pacde * Pabcde = 0,15 + 0,02 - 0,15 * 0,02 = 0,17. 5. Пара В, С. Для возможных изменений имеется два пути, поэтому d1 вс = Рве + Рвас - Рве * Рвас = 0,36 + 0,32 - 0,36 * 0,32 = 0,56. 277
6. Пара В, D. Также имеется два пути: d BD = PBcd + Pbacd~ Pbcd * * Pbacd = 0,09 + 0,08 - 0,09 * 0,08 = 0,16. 7. Пара В, E. Имеет два пути: d ВЕ- РBCde + Рbacde ~ ?bcde * ?bacde ~ = 0,044 + 0,04 - 0,044 * 0.04 = 0,08. 8. Пара С, D. Здесь только один путь, следовательно, d cd — Pcd ~ — dcD ~ 0,25. 9. Пара D, Е. Тоже только один путь: d DE = РDE = dDE — 0,495. 10. Пара Е, С. Здесь только один путь: d ЕС — PEdc ~ dED * dDC ~ = 0,495 * 0,25 =0,12. После всех этих расчетов можно построить полную матрицу зависи- мостей между модулями системы (рис. 6.6). А В С D Е А 1,00 0,63 0,68 0,19 0,17 В 0,63 1,00 0,56 0,16 0,08 С 0,68 0,56 1,00 0,25 0,12 D 0,19 0,16 0,25 1,00 0,49 Е 0,17 0,08 0,12 0,49 1,00 □ Рис. 6.6. Полная матрица зависимостей модулей По полученной матрице можно определить, например, что при изме- нении модуля А вероятность того, что изменится модуль Е, равна 0,17. Суммируя элементы любой строки (за исключением диагонального эле- мента), можно вычислить число модулей, которые должны быть измене- ны при изменении соответствующего модуля программы. Например, ес- ли изменится модуль С, ожидаемое число изменений равно 1,61. Исклю- чив диагональ, вычитая каждый элемент строки из 1,0 и перемножая результаты, получим вероятность того, что изменение модуля не повле- чет за собой изменения других модулей. Например, для модуля С эта вероятность равна Р = (1 -0,68)(1 -0,56)(1 -0,25)(1 -0,12) = 0,09. (6.12) Суммируя все элементы матрицы, и деля сумму на число модулей, можно получить грубую оценку сложности программной системы (диа- гональные и повторяющиеся элементы не учитываются). В нашем при- мере получим значение сложности, равное 0,49. Чем выше это значение, тем сложнее система. В заключение следует заметить, что, несмотря на привлекательность рассмотренной модели, с учетом того факта, что она основана на ключе- вых свойствах структуры программы, все же модель основывается на многих недостаточно выверенных предположениях. 278
6.4. Представление архитектуры программных систем 6.4.1. Модулъно-интерфейсный подход В общем случае модульная программная система представляет собой древовидную структуру, в узлах которой размещаются программные модули, а направленные дуги показывают статическую подчиненность модулей. Если в тексте модуля имеется ссылка на другой модуль, то их на структурной схеме соединяет дуга, которая исходит из первого моду- ля и входит во второй модуль. При этом модульная структура про- граммной системы кроме структурной схемы должна включать в себя совокупность спецификаций модулей, образующих эту систему. Функции верхнего уровня обеспечиваются главным модулем. Он управляет выполнением нижестоящих функций, которым соответству- ют подчиненные модули. При определении набора модулей, реализую- щих функции конкретного алгоритма, необходимо учитывать следую- щее: 1) модуль вызывается на выполнение вышестоящим по иерархии мо- дулем и, закончив работу, возвращает ему управление; 2) принятие основных решений в алгоритме выносится на макси- мально высокий по иерархии уровень; 3) если в разных местах алгоритма используется одна и та же функ- ция, то она оформляется в отдельный модуль, который будет вызывать- ся по мере необходимости. Структурное проектирование обычно подразумевает использование структур для разделения различных частей системы. Каждая часть при этом может проектироваться отдельно. Один из наиболее традиционных методов проектирования операционных систем в 70-е гг. XX в. состоял в том, что проект каждого основного модуля системы выполнялся от- дельно. Этот подход называется модулъно-интерфейсным. Анализируя существующие системы, можно заключить [35, 41], что операционная система состоит из системы ввода-вывода, планировщика времени процессора, менеджера памяти, системы управления файлами и т.д. Каждый модуль выделяется и описывается. Кроме того, определя- ется его интерфейс с другими модулями. Модули и их взаимные связи образуют абстракцию системы высокого уровня. После этого этапа для дальнейшего проектирования и реализации модули рассматриваются по отдельности. Такая же процедура может быть повторена в отношении какого-либо одного модуля системы, и последующую работу над модулем можно расчленить на индивидуальные задания по программированию. После того как все эти модули спроектированы и реализованы, они связывают- ся воедино в соответствии с заранее определенными интерфейсами. Соблюдение правильного интерфейса представляет собой серьезную проблему в модульно-интерфейсном подходе. Модули и их интерфейсы 279
описываются весьма неформально и неточно. Поэтому хотя отдельные модули и пишутся в соответствии с заданными спецификациями, часто оказывается, что в них заложено неверное представление об операцион- ном окружении. И когда модули связываются вместе, они не взаимодей- ствуют должным образом. После того как модули получены в коде ком- пьютера, разрешение возникших конфликтов может оказаться чрезвы- чайно трудным делом. В принципе, если интерфейс полностью специфицирован, то в этом отношении не должно возникать трудностей. К сожалению, однако, первоначальный общий проект и планирование редко доводят до тако- го уровня детализации, когда используется язык программирования. В результате важные решения, которые должны были быть приняты опытными разработчиками в начале работы над проектом, отодвига- ются внутрь модулей. В конечном счете программисты принимают ре- шения, имеющие глобальные последствия, базируясь на очень ограни- ченной и узкой точке зрения на систему. Решения могут приниматься и чрезвычайно квалифицированными людьми, но не в тот момент, ко- гда нужно. Правильное определение и выделение модулей представляет собой трудную задачу (заметим, что при объектно ориентированном проекти- ровании возникает не менее сложная задача определения и выделения классов). Тесно связанные между собой части системы должны входить в один и тот же модуль. Было предложено считать мерой связности двух частей системы число предположений, которое одна часть должна де- лать относительно другой части. Слишком много или слишком мало ин- формации об окружении, в котором работают соседние модули, может иметь плохие последствия при проектировании модуля. Рассмотрим, например, модуль А, работающий со структурой дан- ных. Соседний модуль В должен знать точный протокол для связи с мо- дулем А. Однако он не должен знать всех подробностей о локальной структуре данных модуля А. При проектировании одного модуля не- формальное использование информации о внутренних спецификациях других модулей может оказаться вредным. Так, пусть в предыдущем примере модуль В использует информацию о локальной структуре дан- ных модуля А. В этом случае, если модулю А потребуется изменить свою организацию, модификация модуля В может оказаться неприят- ным делом. Неверное представление модуля В о структуре данных мо- дуля А может не проявиться до тех пор, пока вся система не выйдет из строя. Модульно-интерфейсный подход— один из методов структурного проектирования. Спецификации модулей и их интерфейсов дают струк- турную основу для проектирования каждого модуля и системы в целом. Спецификация программного модуля состоит из функциональной специ- 280
фикации, описывающей семантику функций, выполняемых этим моду- лем по каждому из его входов, и синтаксической спецификации его вхо- дов, позволяющей построить на используемом языке программирования синтаксически правильное обращение к модулю [33]. Существуют различные методы разработки модульной структуры программной системы, в зависимости от которых определяется порядок разработки структуры системы, программирования и отладки модулей, указанных в этой структуре. Обычно в литературе выделяют два основ- ных метода [10, 17, 30, 37, 40]: метод нисходящей разработки (метод «сверху вниз», или top-down) и метод восходящей разработки (метод «снизу вверх», или bottoin-up). По мнению В. Турского [37], формулировка «аналитический и син- тетический подходы» к проектированию программ является более пра- вильной, поскольку действительная суть проблемы не столько в выборе направления при проектировании, сколько в определении того, что именно преобладает: факторизация (аналитические шаги) или компози- ция (синтетические шаги). Ни один из этих подходов в их чистом виде не является жизнеспособной методологией проектирования: чисто ана- литическое проектирование сравнимо с построением «пирамиды с пла- вающей в воздухе вершиной», а чисто синтетическое — с построением пирамиды, «стоящей на голове». Реальная стратегия проектирования почти всегда представляет собой разумное сочетание этих двух подхо- дов. 6.4.2. Объектно ориентированный поход Как отмечает Киммел П. [16], есть две методики разработки объект- но ориентированных программных систем, которые он условно называ- ет «потребление» и «производство». Команды разработчиков могут ра- ботать совместно, используя либо одну из методик, либо обе. Но если руководитель проекта не уверен и не знает, хватит ли квалификации его сотрудников для того, чтобы использовать чужие объекты, или способ- ны ли они разрабатывать собственные, то это может привести к серьез- ным проблемам. Первая методика заключается в том, что команда разработчиков ак- тивно использует библиотеки и классы, разработанные другими. При такой методике разработчики понимают, что их опыт и квалификация пока недостаточны для разработки серьезных объектов. Вторая методи- ка применяется тогда, когда команда хорошо осведомлена о процессе разработки шаблонов, рефакторинге и имеет удачный опыт разработки объектно ориентированных проектов, включая разработку собственных классов. Обе техники приемлемы для использования, но важно знать, какая из них даст большие шансы добиться успеха. Выявление классов, правильно описывающих предметную область проекта, является самой сложной задачей, которую предстоит решить разработчику. Выявление нужных классов намного сложнее процесса создания диаграмм. Если команда разработчиков не смогла определить 281
классы, точно описывающие задачу, то неважно, сколько средств будет потрачено на средства разработки: в конечном счете есть большая веро- ятность того, что созданные модели окажутся неработоспособными. Не каждый класс является классом предметной области. Массивы, наборы и классы графического интерфейса не являются классами пред- метной области. Классы предметной области - это сущности, описы- вающие задачу предметной области, например такие классы, как «сту- дент», «преподаватель», «регистрация», «экзамены», в приложении, хранящем информацию о поступающих в вуз, или такие классы, как «за- казы», «люди», «уголовные дела», «счета», «денежные вклады» и др. Каждому классу предметной области соответствует объект предметной области [8, 23, 31]. Объект - это сущность, которая используется при выполнении неко- торой функции или операции (преобразования, обработки, формирова- ния и т.д.). Объекты могут иметь динамическую или статическую при- роду. Динамические объекты используются в одном цикле воспроизвод- ства, например «заказы на продукцию», «счета на оплату», «платежи». Статические объекты используются во многих циклах воспроизводства, например «оборудование», «персонал», «запасы материалов». На кон- цептуальном уровне построения модели предметной области уточняется состав классов объектов, определяются их атрибуты и взаимосвязи. Та- ким образом строится обобщенное представление структуры предмет- ной области. Многие учебники по объектно ориентированному программирова- нию рекомендуют поиск в предметной области существительных, а за- тем привязку к ним глаголов. Существительные впоследствии перехо- дят в классы, а глаголы - в методы [10, 15]. Это самый легкий способ, но он дает только 20—30% всех классов, которые реально потребуется выявить. Если затем на этапе анализа обнаружились только классы и методы, выявленные сочетанием существительных и глаголов, то, ве- роятнее всего, в разработанных моделях будет наблюдаться нехватка классов и потребуется дополнительный, углубленный анализ. Тем не менее выявление существительных и глаголов предметной области яв- ляется хорошим началом. В дополнение к тем деталям, которые могут сообщить эксперты за- казчика по предметной области, разработчику придется решить, как в системе представить эти данные в доступной для самого заказчика форме, и в любом случае придется решать задачу того, как сохранять информацию, вводимую потенциальными пользователями программной системы. Для решения этих вопросов используются интерфейсные классы, классы-контроллеры и классы-сущности [15, 16]. Интерфейс- ный класс — это класс, предназначенный для связи элементов вне систе- мы с элементами, входящими в состав системы. Поэтому такой класс часто называют граничным. Классы-сущности представляют данные. Обычно сущности передают информацию, которая хранится в базе дан- ных. Классы-контроллеры управляют другими классами. 282
Как правило, пользователи (заказчик) могут дать много полезной ин- формации о классах-сущностях и помочь определить вид графического интерфейса пользователя, основываясь на том, как они выполняют зада- чи своей повседневной деятельности. Однако этого недостаточно, и раз- работчику нужно еще выявить классы-контроллеры и интерфейсные классы. Автор работы [16] рекомендует помнить тот факт, что эксперты по предметной области могут сообщить разработчику большое количе- ство информации. Они могут рассказать многое о данных, которые им необходимо хранить немногое о том, каким образом им хотелось бы вводить эти данные в компьютер, пояснить кое-какие детали о процес- сах, с помощью которых они получают эти данные. Вторым важным моментом является то, что эксперты предметной области в рамках своей деятельности могут выполнять большое количе- ство действий, которые будут просто непонятны постороннему наблю- дателю. Это означает, что проектировщик, возможно, никогда и не пы- тался исследовать в целом то, что делает фирма-заказчик и как она это делает, и не пытался найти способ, как оптимизировать ее деятельность. В результате проектировщик может получить много информации от клиентов об их деятельности, которая, с его точки зрения, будет иметь слабое отношение к разрабатываемой системе. Но при этом эксперты по предметной области будут считать, что эта информация очень сущест- венна. После того как разработчик выяснит у пользователей все о классах- сущностях, его задачей становится определение классов-контроллеров и интерфейсных классов. При разработке модели системы к сущност- ным классам добавляются стереотип entity и специальный символ клас- са-сущности, поддерживаемый во многих инструментах разработки, на- пример в Rational RSA (рис. 6.7). Класс-контроллер является связую- щим кодом, который управляет другими классами и обеспечивает пере- дачу данных между классами-сущностями и интерфейсными классами. Классы-контроллеры обозначаются при помощи стереотипа control, до- бавляемого к обычному классификатору, и специального символа (см. рис. 6.7). Интерфейсные, или граничные, классы моделируются так, как показано на этом же рисунке, и имеют стереотип boundary. Рис. 6.7. Изображение классов в Rational RSA 283
Заметим, что классы-сущности включают в себя логические сущно- сти. Обычно их появление является результатом какого-либо смешанно- го запроса в базу данных. Выявление логических и простых сущностей является относительно простой задачей, так как теория реляционных баз данных легка для понимания. Классам-сущностям, определенным для отдельных таблиц и различных смешанных запросов, включающих обращения к нескольким таблицам, можно присваивать стереотип table. Вообще, процесс выявления классов при работе над проектом систе- мы весьма нетривиален. Существует много рекомендаций по организа- ции такой работы. Одна из них— использование CRC (Class Responsibility and Collaborator) — карты функциональности и сотрудни- ков класса [16]. В заключение этого раздела отметим, что многие специалисты (про- граммисты и ученые) высказывают отрицательные суждения по вопросу перспективности объектно ориентированной технологии программиро- вания. Поводом к таким высказываниям послужила публикация извест- ной и классической в мире программирования статьи «Почему объектно ориентированное программирование провалилось?». Статья была напи- сана доктором компьютерных наук Стэнфорда, старшим архитектором по разработке ПО сначала Sun, а потом и IBM Р. Гэбриелом (R. Gabriel) [34]. Он никогда не скрывал своего скептического отношения к парадиг- ме ООП. В 2002 г., по прошествии 2 лет после первоначальной публика- ции критической статьи, автора пригласили выступить на ежегодной конференции OOPSLA (центральная конференция 1Т-специалистов по объектно ориентированным языкам и методологиям разработки ПО) и изложить свои критические взгляды. В качестве оппонента пригласили Г. Стила (G. Steele), разработчика языка Scheme, крупнейшего специалиста-теоретика по ООП, авторитет которого в американской академической среде непререкаем. Чтобы мак- симально отразить позиции выступающих, их решили усилить еще дву- мя выступающими. В качестве «антиобъектника» дополнительно при- гласили П. Грэма (Р. Graham), крупнейшего специалиста по Lisp, автора многочисленных книг и стандартизаций Lisp. В стан объектников при- гласили Дж. Ноубла (J. Noble), автора одних из первых книг и работ по теории ООП. Многие участники вспоминают, что конференция надолго запомнилась им по тому уровню обсуждения, которое завязалось тогда в этой публичной «интеллектуальной дуэли» фактически диаметрально противоположных школ программирования. Но факт остается фактом: сторона, представлявшая объектно ориентированное программирова- ние, во время открытой дискуссии с противниками под смех зала даже запуталась в своих же концепциях. П. Грэм утверждал, что половина всех концепций ООП являются скорее плохими, чем хорошими, в связи с чем он искренне сочувствует ООП-программистам, тогда как вторая половина от оставшихся концеп- ций и вовсе не имеет никакого отношения к ООП, с которыми их поче- му-то постоянно ассоциируют. Например, он говорит [34]: «В 80-х гг. метод повторного использования каким-то неясным мне образом связа- 284
ли с объектно ориентированным программированием, и сколь угодно многочисленные имеющиеся доказательства обратного, по-видимому, уже не избавят этот метод от клейма ООП. Хотя иногда объектно ориен- тированный код годится для повторного использования, таким его дела- ет вовсе не объектно ориентированность, а программирование в стиле «снизу вверх». Возьмем, например, библиотеки: их можно подгружать и повторно использовать сколько угодно, потому что, по сути, они пред- ставляют собой отдельный язык. И при этом совсем неважно, написаны ли они в объектно ориентированном стиле или нет». Читателям этой книги будет интересно познакомиться с полным тек- стом статьи [34]. Однако только время покажет, кто в итоге окажется правым, а кто искренне заблуждался. 6.4.3. Компонентный подход Компоненты представляют собой автономный код (подсистему), ко- торый может быть повторно использован за счет его независимого раз- вертывания. Компоненты не обязательно должны быть большими по объему кода, но в основном они больше по объему отдельного класса или группы слабо связанных классов. Компоненты имеют множество предоставляемых и требуемых интерфейсов и используются в больших сложных приложениях с десятками и сотнями классов предметной об- ласти. В сложных случаях разработки больших систем, например при разработке корпоративной информационной системы, потребуются диа- граммы компонентов [11, 18]. Как отмечает автор книги [16], существует два основных способа выявления компонентов. Первым является метод движения сверху вниз, а вторым - движение снизу вверх. По сути, это методы проектирования архитектуры программной системы, которые характерны и для струк- турного проектирования с модульно-интерфейсным подходом. Если смотреть глубже, это не что иное, как метод декомпозиция сложной сис- темы. В первом случае — методом «сверху вниз», а во втором случае — синтезом сложной системы на основе метода проектирования «снизу вверх». При этом образуется многослойная архитектура программной системы (см. п. 2.6). В ряде монографий и статей рекомендуется определять архитектуру системы на основе компонентов методом «сверху вниз». В этом случае в первую очередь определяются компоненты (крупные модули систе- мы), а потом интерфейсы этих компонентов. Как только компоненты и их интерфейсы определены, можно разделить задачи реализации сис- темы среди участников процесса разработки, поручив проектирование каждого компонента отдельным группам. С того момента, как каждый согласится с разработкой интерфейсов, проектировщики будут свобод- ны в реализации внутренних частей компонента и могут осуществлять ее любым способом, который они выберут. По мнению автора [16], такой поход может быть эффективным в том случае, если команда проектировщиков применяет хорошо продуман - 285
ные компоненты с понятными и известными им интерфейсами. Однако правильно выявить все новые компоненты путем проектирования «свер- ху вниз» достаточно сложно. Кроме того, это достаточно сложный стиль программирования, связанный с тем, что системы, основанные на ком- понентах, имеют от трех до пяти вспомогательных интерфейсов и про- межуточных классов для каждого класса предметной области. Поэтому системы, основанные на компонентах, могут быть достаточно трудоем- кими, дорогими и рискованными. При разработке компонентной системы методом «снизу вверх» в первую очередь определяются классы предметной области, т.е. те, ко- торые служат непосредственно для решения задач предметной области, а не вопросов архитектуры приложения. В результате основные усилия концентрируются прежде всего на решении поставленной задачи вместо описания сложной архитектуры. Применяя метод разработки «снизу вверх» и выявляя классы предметной области, проектировщики имеют больший потенциал для решения поставленной задачи. Кроме того, все- гда можно объединить классы предметной области в компоненты, если сложность проекта растет; некоторые группы классов можно развернуть и повторно использовать, включив их в состав компонентов. Оба метода проектирования компонентных систем имеют право на ис- пользование. Для небольших приложений и приложений среднего разме- ра, вероятно, не потребуются компоненты, и в этих случаях можно вос- пользоваться методом «снизу вверх». Для приложений корпоративного масштаба потребуется более мощная методология, и в этом случае метод проектировании «сверху вниз» может оказаться более подходящим. Способы декомпозиции могут быть различными. При модульно-ин- терфейсном подходе на верхних уровнях осуществляется структурная декомпозиция, на средних и нижних — функциональная. При объектно- ориентированном подходе декомпозиция связана с правильным подхо- дом к определению классов предметной области. Каждый класс должен согласоваться с принципами хорошего объектно ориентированного про- ектирования. К принципам хорошего объектно ориентированного про- ектирования, в частности, относятся принцип единственной ответствен- ности (ПЕО - Single Responsibility Principle (SRP)), принцип открыто- сти-закрытости (ПОЗ - OpcnClosed Principle (ОСР)) и принцип подста- новки Лискова (ППЛ - Liskov Substitution Principle (LSP)) [27]. Принцип единственной ответственности говорит о том, что у такого элемента, как класс, должна быть лишь одна определяемая им ответствен- ность. Если же класс отвечает и за представление данных, и за доступ к ним, это характерный пример нарушения принципа ПЕО. Принцип от- крытости-закрытости заключается в том, что класс должен быть закрыт для модификации, но открыт для расширения. При изменении класса все- гда существует риск что-то нарушить. Но если вместо модификации класс расширяется подклассом, такое изменение менее рискованно. Принцип подстановки Лискова лучше пояснить следующим приме- ром. Допустим, что существует иерархия наследования для классов Person («Лицо») и Student («Студент»). При использовании класса 286
Person должна быть возможность использовать класс Student, поскольку он является подклассом Person. На первый взгляд, это всегда происхо- дит автоматически, хотя и не совсем очевидно в отношении рефлексии — метода, позволяющего проверять программными средствами тип экзем- пляра объекта, считывать и устанавливать его свойства и поля, а также вызывать его методы, ничего не зная заранее об этом типе. Так, в мето- де, использующем рефлексию для обращения к классу Person, может и не предполагаться подкласс Student. Проблема рефлексии имеет отношение к синтаксису. Р. Мартин [22] приводит в большей степени семантический пример класса Square («Квадрат»), который относится к классу Rectangle («Прямоугольник»). Но когда используется метод задания ширины квадрата Set Width для класса Square, это не имеет смысла - по крайней мере, для этого нужно еще вызвать (внутренним образом) метод задания высоты квадрата SetHeight. Такое поведение отличается оттого, что требуется для класса Rectangle. Безусловно, все эти принципы оказываются спорными в определен- ных контекстах. Ими следует руководствоваться лишь как рекоменда- циями, а не истиной в последней инстанции. Например, применяя ПОЗ, нетрудно переусердствовать до такой степени, когда становится ясно, что для реализации метода лучше модифицировать класс, чем расши- рять его. И ввод метода в класс можно также рассматривать как расши- рение. Заключая рассмотрение различных методов представления архитек- туры сложных программных систем, можно констатировать факт пред- ставления архитектуры таких систем в виде многослойной иерархиче- ской системы, которую удобно представлять иерархической системой вложенных виртуальных машин. 6.5. Слои программного продукта Сложность и значительные размеры текста программы, предлагае- мой как решение реальной задачи, заставляют читателей этого текста искать возможности анализа программы на нескольких уровнях: снача- ла установить ее грубую функциональную структуру, затем выделить в ней функциональные единицы (компоненты, модули) и наконец про- анализировать каждый модуль отдельно. Сознательное систематиче- ское применение некоторых методологий программирования позволяет создавать программы, которые легче поддаются анализу. Большинство известных специалистов [9, 12, 20] еще в 70-80-е гг. прошлого века пришли к заключению, что разбиение на уровни являет- ся стандартным способом восприятия сложных текстов (не только про- грамм) независимо от того, помогает в этом автор текста или нет. На- сколько хорошо понимают это основное правило разработчики про- граммных систем, убедительно показывает пример выделения уровней в документации, сопровождающей большие программы: как правило, одна и та же программа описывается в нескольких документах, отра- 287
жающих конкретные уровни абстракции (руководство пользователя, ру- ководство оператора, руководство системного программиста и т.п.). Раз- нообразие руководств указывает на возможность создания различных абстрактных описаний одной и той же программы. Эти описания могут (но не обязательно должны) образовывать упорядоченное множество. Правда, на практике документация, предоставляемая разработчиками, во многих случаях вопреки декларируемым ими целям не является ни упорядоченной, ни полной. Имеются весомые опытные данные в защиту того утверждения, что разбиение на уровни абстракции является стандартным методом разбо- ра сложных текстов, таких как машинные программы. Синтаксический анализ (применяется ли он к предложению на русском языке или к опе- ратору программы), использование мнемонических имен, абзацев, от- ступов и других графических средств (таких, как блок-схемы, подчерки- вание строк, выделение фрагментов текста на распечатках и т.п.) - лишь немногие методы, используемые для этой цели. Почти все машинные программы могут рассматриваться как конст- рукции с расслоенной структурой, вытекающей из общей структуры программного обеспечения. Программы, о которых идет речь, пишутся на языке, интерпретируемом компилятором, содержат библиотечные подпрограммы, выполняемые под управлением операционной системы, использующей различные служебные программы. Если рассмотреть те- перь фактически выполняемую последовательность машинных команд, то все упомянутые промежуточные средства будут отражать те или иные уровни абстракции. В свою очередь, машинные команды можно интерпретировать как абстрактные описания микропрограмм, посредст- вом которых они реализуются. Таким образом, мы получаем другое понятие расслоения программы, связанное со степенью отдаленности от оборудования компьютера. Ес- ли рассматривать переносимые программы, то, видимо, нужно выделять другое множество слоев, определяемое разделением свойств программы на машинно-независимые и присущие данной машине. Наконец, можно обнаружить еще модульное расслоение программы'. слои, соответствующие множествам равнодоступных модулей. Отме- тим, что (пока еще неточное) понятие равнодоступности модулей осво- бождает от языковых ограничений: языковые конструкции могут облег- чать модульное расслоение или даже обеспечивать синтаксическую еди- ницу, соответствующую модульному слою, но и при отсутствии таких языковых средств можно различать модульные слои в программе. Даль- нейшее изложение материала этого раздела следует В. Турскому [37]. Изучение структуры программы или ее проектирование, поскольку оно почти неизбежно связано с разбиением программы на слои, должно начинаться с выбора принципа расслоения. Далее будем рассматривать программные системы одного определенного языкового уровня (с хоро- шо определенными синтаксическими единицами в соответствии с хоро- шо определенными синтаксическими правилами и хорошо определен- ной семантикой элементарных операторов и синтаксических конструк- 288
ций). Из рассмотрения исключим все вопросы, относящиеся к другим языковым уровням, таким, например, как интерпретация элементарных операторов в терминах более примитивных составляющих. Для полноты элементарные операторы данного языкового уровня бу- дем рассматривать как модули базового (или нулевого) уровня, состав- ляющие базовый слой. Это можно сделать потому, что все элементар- ные операторы повсеместно доступны (единственные возможные огра- ничения на использование элементарных операторов касаются входя- щих в них данных, т.е. фактических параметров активации модулей). Затем потому, что здесь не учитываются привилегированные операторы машинного языка, которые не могут использоваться в прикладных про- граммных системах. Модули, построенные из модулей нулевого уровня, могут рассмат- риваться только как модули первого уровня. При этом не требуется, что- бы все модули первого уровня были равнодоступны. Например, в про- граммах, написанных на языке, допускающем блочную структуру, неко- торые модули первого уровня могут быть локализованы в каком-либо блоке и, следовательно, доступны только внутри этого блока и его под- блоков. С другой стороны, при конструировании модулей высших уровней в некоторых языках можно использовать модули разных уровней и да- же того же самого уровня, что и конструируемый модуль (рекурсия, сопрограммы). Это заставляет отказаться от обманчиво простого опре- деления слоев как модулей одного и того же уровня, где под уровнем модуля должно пониматься нечто большее, чем наивысший уровень со- ставляющих его модулей. Поэтому уточним понятие равной доступно- сти. Определение (семантическая спецификация) модуля, отличного от базового, зависит от спецификации других модулей. Будем предпола- гать, что в каком бы месте программы ни определялся модуль, специфи- кации всех требуемых модулей в том месте, где дается определение, бу- дут синтаксически доступны (иначе в программе имеется ошибка). Бу- дем писать п > т, если спецификация модуля т зависит от специфика- ции модуля п, Предположим теперь, что в данной программе выделены слоиLq, L\, Ln. Реально можно распознать только слой базовых моду- лей £0. Процесс, описываемый ниже, введет более высокие уровни и от- ношение порядка. Рассмотрим множество модулей М, не входящих в выделенные слои, и таких, что для каждого модуля т е М имеем: 1) если спецификация модуля т зависит от модуля т\ а модуль т[ принадлежит слою то 0 < i < п\ 2) хотя бы один из модулей, используемых в спецификации т, при- надлежит слою Любое множество М, удовлетворяющее этим двум требованиям, формирует страту над слоем L,v Максимальной стратой S(L„) над сло- ем Ln будем называть страту, содержащую все модули рассматриваемой программы, удовлетворяющие обоим условиям для страты. 289
Рассмотрим теперь множество всех модулей K(Ln) таких, что для ка- ждого модуля А"(Д,) выполняются: 1) хотя бы один модуль, используемый в спецификации к, принадле- жит слою Д,; 2) все модули, используемые в спецификации к, не являющиеся чле- нами о SH (£0 ) и... и о SH(L„_,) и Ln, должны быть членами Ж,); 3) если т > к и те K(Ln), то существует хотя бы один модуль р е К (Ln ) , такой, что р> т. Множество SH(Ln) = K(LI3) о S(L„) называется покровом над слоем Ln, Таким образом, произвольная (или максимальна) страта над Ln явля- ется некоторым (или конкретным) множеством (всех) модулей, специ- фицируемых с использованием слоев £0, **♦, которые по определе- нию не могут быть реализованы при отсутствии доступа хотя бы к неко- торым модулям слоя Lf]. На практике реализация модуля из страты над Ln потребует, вероятно, также доступа к модулям из других слоев (£о, Т।, .._ j). Взаимозависимые модули исключаются из страты, однако включа- ются в покровы как члены множеств Х(Д,). Для некоторых языков про- граммирования понятия покровов совпадают с понятием максимальной страты. Дальнейший анализ расслоения программы обусловлен специфиче- ским множеством синтаксических правил, налагающих ограничения на правила локализации. Предположим, что имеются общедоступный базо- вый слой модулей и покров над ними. Если все модули покрова равно- доступны во всех частях программы, исключая базовые модули и моду- ли покрова, то весь покров можно рассматривать как первый слой моду- лей. Тем не менее в большинстве случаев некоторые части покрова, т.е. некоторые модули, принадлежащие покрову, будут резервироваться для собственных нужд компонентов более высокого уровня. Следователь- но, при анализе программы может возникать тенденция рассматривать в качестве первого слоя модулей только ту часть покрова, которая рав- нодоступна всем компонентам программы, не относящимся ни к базово- му слою, ни к модулям самого покрова. Отметим, что в дальнейшем анализе будем включать в покров над первым уровнем только такие модули, которые строятся из общедоступ- ных модулей, т.е. из базовых модулей и тех модулей покрова над базо- вым слоем, которые являются «общественными». Тем не менее не сле- дует опасаться, что такой анализ не учтет какой-нибудь модуль про- граммы: программа в целом может рассматриваться как модуль. С дру- гой стороны, программа в целом одновременно является и наивысшим слоем, и покровом над предыдущим слоем. В хорошо структурированной программе спецификация программы как модуля будет семантически зависеть только от спецификаций не- скольких модулей предыдущего слоя и для связок будет использовать некоторые модули базового слоя. Использование базовых модулей во 290
всех покровах и слоях, очевидно, необходимо: это «плата» за использо- вание в качестве базового слоя базовых операторов определенного язы- кового уровня. Разбиение программы на слои показано на рис. 6.8, где отношение п > т обозначается стрелкой, проведенной из п в т. Базовый слой обо- значен горизонтальной линией в нижней части рисунка. Модули А, В, F, G и Н принадлежат максимальной страте над базовым слоем; модули С, D, Е составляют множество Х(Л„). Следовательно, все модули самого нижнего ряда принадлежат покрову над базовым слоем. Модули А, В, С, D, Ей Н равнодоступны «сверху» и, таким образом, принадлежат первому слою. Модули F и G являются собственностью модуля М, что дает основание рассматривать эту группу модулей как единый модуль М, а модули F и G - как модуляризованные компоненты модуля М. Модули 1, J и L удовлетворяют условиям множества и, следо- вательно, принадлежат покрову над первым слоем; действительно, по- скольку они равнодоступны, они являются членами второго слоя. Как классифицировать модуль М? Поскольку кроме базовых моду- лей он зависит только от модулей F и G, которые являются членами страты над базовым слоем, не принадлежат никакому слою, модуль М принадлежит максимальной страте над нулевым слоем. Будучи также доступен, как и модули А, В, С, D, Е и Н, модуль М должен рассматри- ваться как модуль первого слоя. Рис. 6.8. Разбиение программы на слои При помощи аналогичных рассуждений можно показать, что модули 1, J, L и О принадлежат второму слою, а третий и четвертый слои состо- ят каждый из одного модуля (N и Р соответственно). Модуль Р - это, ко- нечно, программа в целом. Анализ представленного на рис. 6.8 примера выявляет несколько ин- тересных аспектов. Во-первых, несмотря на искусственно введенную иерархию модулей М, F и G, все-таки правильно относить модуль М к первому слою. 291
Во-вторых, обнаруживается, что модули, являющиеся чей-то «собст- венностью», не попадают в какой-либо слой программы. В-третьих, показана взаимосвязь семантической зависимости и пра- вил локализации: если модули F и G сделать столь же доступными, как другие модули покрова над базовым слоем, их можно будет рассматри- вать как модули первого слоя, а модуль М будет принадлежать второму слою. В-четвертых, показано, что большое количество чисто механических понятий, конструируемых большей частью методом «снизу вверх», мо- жет использоваться для модульного расслоения программы, отражаю- щего взгляд «сверху вниз» на структуру программы. Однако необходимо отметить, что во всех приведенных рассуждени- ях программа рассматривалась как готовый продукт, т.е. не рассматри- вались способы и средства получения данной структуры. Из многих воз- можных интерпретаций понятия «расслоение» использовано то, которое определено как структура из слоев равнодоступных модулей. 6.6. Методы структурного проектирования 6.6.1. Метод восходящей разработки («снизу вверх») Этот метод применялся в 60-х гг. XX в. при разработке операцион- ных систем, в частности операционной системы THE. Он получил на- звание метода восходящей разработки, или синтетического, или снизу вверх. Применяя этот метод, разработчик начинает с основного аппарат- ного оборудования. «Чистая» аппаратура представляет собой для поль- зователя довольно неудобную машину, чтобы решать на ней задачу. По- этому разработчик на первом шаге добавляет к компьютеру слой про- граммного обеспечения. Этот слой программ вместе с нижележащей ап- паратурой обеспечивает выполнение некоторого множества команд, оп- ределяющих новую виртуальную машину. На следующем шаге выделя- ется другое нужное свойство, добавляется новый слой программного обеспечения и получается очередная виртуальная машина. Процесс про- должается до тех пор, пока не будет получена виртуальная машина с требуемыми пользователю свойствами (рис. 6.9). Аналогично проводится процесс конструирования программных сис- тем, который назван в [10, 17] архитектурным подходом. Модульная структура системы формируется в процессе программирования модулей начиная с самого низшего уровня, затем следующего и т.д. При этом модули реализуются в таком порядке, чтобы для каждого программи- руемого модуля были уже запрограммированы все модули, к которым он может обращаться. Главной целью архитектурного подхода является повышение уровня используемого языка программирования, а не разработка конкретной программы. Это означает, что для заданной предметной области выде- ляют типичные функции, каждую из которых можно использовать при решении разных задач в этой области. 292
Начало i VM; : VMNс требуемыми свойствами <---- LN N=? Рис. 6.9. Последовательность проектирования «снизу вверх» Основные преимущества метода проектирования «снизу вверх» вы- текают из способа структурирования, ограничивающего построение системы. Между различными уровнями можно организовать четкий ин- терфейс. При этом, поскольку любой процесс может требовать обслу- живания только от процесса на более низком уровне, практически ис- ключаются непредвиденные ситуации. Тестовые процедуры, исполь- зующиеся в этом методе проектирования, могут быть исчерпывающими, так как каждый уровень можно проверить по отдельности и достаточно полным образом. Когда некоторый уровень проверен до конца, можно добавлять части следующего уровня, реализующего более сложные функции, и продолжать проверку. При восходящем программировании разработчик начинает с полного набора базовых средств, обеспечиваемых выбранным языковым уров- нем (например, машинным языком). Все, что можно запрограммировать на этом языковом уровне, можно выразить в терминах таких средств. Как правило, это достаточно утомительно, а значит, и ненадежно. Для облегчения процесса программирования разработчик создает более вы- сокие слои модулей таким образом, чтобы они облегчали использование доступных средств в форме, позволяющей абстрагироваться от обреме- нительных деталей. Но на каждом следующем уровне (слое) разработ- 293
чик должен быть уверен, что все средства, выразимые в терминах уста- новленных на этом слое понятий, доступны и что слой модулей пред- ставляет собой полное и логически стройное описание всей совокупно- сти средств, обеспечиваемых базовым слоем. Главная трудность в применении метода восходящей разработки за- ключается в выборе уровней и иерархическом упорядочении. Кроме то- го, головной модуль проектируется и реализуется в последнюю очередь, что не дает возможности продемонстрировать его работу заказчику и проверить его соответствие спецификациям. Разработчики, использующие синтетический подход к проектирова- нию программ, сталкиваются с серьезными трудностями: начальные этапы утомительны, затрагивают самые базовые понятия и связаны с написанием длинных текстов на языке программирования. С другой стороны, нельзя пропустить их в описании и сразу перейти к понятиям «среднего уровня». Описание представляет собой как бы двойную за- гадку: первое (что всегда имеет место в этом подходе в его чистом ви- де) — как догадаться, какие понятия являются полезными для решения конкретной задачи; второе — как получить эти промежуточные понятия из базовых. На первый взгляд порядок разработки «снизу вверх» кажется вполне естественным. Каждый модуль при программировании выражается че- рез уже запрограммированные, непосредственно подчиненные модули, а при тестировании используются уже отлаженные модули. Однако со- временная технология не рекомендует такой порядок разработки про- граммной системы [17]. Во-первых, для программирования какого-либо модуля можно обой- тись без текстов используемых им модулей, достаточно того, чтобы ис- пользуемый модуль был специфицирован, а для его тестирования мож- но заменить используемые модули имитаторами. Во-вторых, каждая программа в какой-то степени подчиняется неко- торой внутренней для нее, но глобальной для ее модулей информации (принципам реализации, предположениям, структурам данных и т.п.), что определяет ее концептуальную целостность. Такая информация формируется в процессе разработки программной системы. При восхо- дящей разработке для модулей нижних уровней такая информация еще не ясна в полном объеме, поэтому эти модули приходится перепрограм- мировать. В-третьих, при восходящем тестировании для каждого модуля (кроме головного) приходится создавать ведущую программу, которая должна подготовить для тестируемого модуля необходимое состояние информационной среды и произвести требуемое обращение к нему. Это приводит к большому объему отладочного программирования и не дает гарантии, что тестирование модулей производилось именно в тех условиях, в которых они будут выполняться в рабочей программ- ной системе. Заметим, что последовательные слои программной системы, спроек- тированной «снизу вверх», не обладают свойством быть решением, но 294
обеспечивают весь спектр возможностей, предоставляемых базовым слоем, хотя и выраженных через более общие понятия. Поэтому можно считать, что синтетическое проектирование программ ориентировано скорее на обслуживание (или использование), чем на решение задачи. Необходимо еще раз подчеркнуть, что слои возникают в синтетиче- ском проекте тогда, когда разработчик получает набор модулей, реали- зующих понятия, в терминах которых можно выразить любую програм- му, выразимую в базовых понятиях. Модули, в которых детали, если они невыразимы в данном слое, упрятываются, не меняя при этом смыс- ла программы (выраженного в понятиях, доступных в рассматриваемом слое). 6.6.2. Метод нисходя щей разработки («сверху вниз») Альтернативный метод проектирования операционных систем полу- чил название аналитического, или нисходящего, проектирования, или «сверху вниз». В этом случае разработчик исходит из желаемых свойств виртуальной машины пользователя и последовательно разрабатывает уточнения в направлении аппаратуры. Реализация этой виртуальной машины полностью не специфицируется. Неопределенные части проек- тируются в дальнейшем как компоненты (модули) системы. Эта работа продолжается до тех пор, пока система не определена настолько, что ее основные функции реализуются аппаратурой или модулями, непосред- ственно выполняемыми на аппаратуре. В результате проектирования получается множество вложенных друг в друга компонентов. На каждом шаге проектирования у разработ- чика имеется некоторая абстракция функционального описания некото- рого компонента, и он должен уточнить эту абстракцию, разбив ее на более мелкие и более подробно проработанные части. Аналогичный подход используется при проектировании структуры программной системы. На начальном шаге, используя внешний проект (спецификации системы), формируется перечень всех функций системы. Затем определяются их подфункции. Далее каждая подфункция может расчленяться до тех пор, пока ее составные части не будут окончательно уточнены. Метод нисходящего проектирования, иногда называемый функциональной декомпозицией, основан на двух стратегиях: пошаго- вом уточнении (детализации), разработанном Э. Дейкстрой, и анализе сообщений, базирующемся на работах Иордана, Константайна, Г. Май- ерса [14]. Эти стратегии отличаются способами определения начальных спецификаций, методами, используемыми при разбиении задачи на час- ти, и правилами записи. Для проверки проекта в процессе его разработки можно с самого на- чала применять моделирование. Этот подход побуждает разработчика думать о том, какие функции должен выполнять некоторый компо- нент, а не как данный компонент будет их реализовать. Компоненты системы можно моделировать более детально по мере того, как продви- гается работа по проекту. 295
Первоначальное представление какого-либо компонента может быть некоторым алгоритмом, который для данного входа вырабатывает с не- которой временной задержкой соответствующий выход и таким образом моделирует работу данного компонента. По мере продвижения работ по проектированию данный алгоритм замещается последовательным набо- ром обращений к следующему множеству проектируемых компонентов. На каждой стадии разработки проект можно оценить, чтобы проверить, продолжает ли он соответствовать поставленным целям. Каждая абстракция системы соответствует некоторой программе мо- делирования, которая построена из иерархии процедур. Эта программа определяет, как нужно действовать с теми переменными, которые влия- ют на состояние системы на этом уровне абстракции. Программа, назо- вем ее Р, соответствующая некоторому уровню абстракции, управляется программой более высокого уровня абстракции, которую назовем Q. Программа Q принимает более глобальные решения, основываясь на значениях ее собственных переменных, которые на самом деле являют- ся абстракциями переменных программы Р. Изменения в переменных программы Q соответствуют изменениям в переменных программы Р. Аналогично некоторые процедуры программы Р делают запросы на вы- полнение работ к программам более низких уровней абстракции. Когда система окончательно спроектирована, ее можно реализовать путем замены основных алгоритмов на самом низком уровне абстрак- ции и средств, обеспечиваемых программой моделирования теми эле- ментами, из которых должна быть построена система. Если эта замена сделана правильно, полученная система будет представлять в точности те средства, которые определены на самом высоком уровне. При реализации программы нисходящим методом первым кодирует- ся головной модуль — модуль верхнего уровня. При этом, поскольку он вызывает модули соседнего низшего уровня, они представляются за- глушками — фиктивными модулями (имитаторами). С помощью заглу- шек организуется непосредственный возврат в вызывающий модуль или осуществлются передача значений текстовых данных и печать результа- тов работы. Если заглушки имеют параметры, целесообразно организо- вать их печать. После того как отлажен головной модуль, заглушки по- следовательно заменяются функциональными модулями, которые, в свою очередь, будут вызывающими для следующих заглушек (рис. 6.10). В большинстве случаев аналитическое проектирование обеспечивает естественное расслоение программной системы: каждый следующий уровень понятий в расширяющемся дереве может рассматриваться как слой. Иногда выдвигаются возражения против методологии проектиро- вания «сверху вниз», заключающиеся в том, что она не определяет, ка- кое из двух направлений дерева программной структуры является более предпочтительным: от ветви к ветви или от уровня к уровню. Однако рекомендация выражать решение на каждом уровне исключительно в терминах этого уровня практически решает эту дилемму. 296
6.6.3. Заключительные замечания по структурному проектированию Обе рассмотренные методологии естественным образом приводят к явно выраженной расслоенной структуре программной системы, обес- печивая равную доступность модулей одного слоя, В обеих методологиях слой представляет собой полное непротиворечивое множество модулей, даже если понятие полноты в каждом случае имеет различные оттенки. Сферы применения этих двух методов дополняют друг друга. Кратко это можно охарактеризовать так: при программировании решения от- дельной задачи предпочтительнее аналитическое проектирование, а при создании программных систем для решения класса задач - синтетиче- ский метод. Поэтому синтетическое проектирование часто используется при написании операционных систем, а аналитическое — при написании специализированных программных систем. Возможен другой путь использования методов структурного проек- тирования. Система может быть спроектирована методом «сверху вниз», а реализована методом «снизу вверх». В этом случае общий про- ект структуры программной системы выполняется методом «сверху вниз», а модули фактически объединяются в систему по методу «снизу вверх». При таком подходе налицо все преимущества реализации мето- дом «снизу вверх» и в то же время не приносится в жертву возможность общего охвата системы, предоставляемая методом проектирования «сверху вниз». Существует другая интерпретация взаимной дополняемости этих двух принципов проектирования, объясняющая, почему в большинстве важных задач используются оба подхода. Для трудных и больших задач обстановка, обеспечиваемая базовым уровнем, как правило, слишком удалена, чтобы к ней можно было прийти аналитическими методами проектирования. Поскольку при выборе абстрактных свойств, полезных для решения данной широкой задачи, можно учитывать предыдущий опыт, на этапах приведения системы к уровню этих предположительно полезных абстрактных понятий можно с успехом применять синтетиче- ское проектирование, что значительно снижает объем разработок анали- тического характера. Между сторонниками методов проектирования «снизу вверх» и «сверху вниз» ведется много дискуссий. Многие полагают, что проек- тирование «сверху вниз» больше соответствует идее, когда следует при- нимать решения на ранних стадиях проектирования и оставлять реше- ния по детальной реализации на более поздние этапы. Однако проекти- рование «сверху вниз» требует лучшего осмысливания на ранней стадии процесса проектирования. Проектирование «снизу вверх» не требует вначале полного осмысливания системы, так как каждый слой про- граммной системы добавляется к уже полностью определенному преды- дущему слою. Вследствие этого, как указывается в [41], некоторые раз- работчики программ пропагандируют метод «сверху вниз», но на прак- тике проектируют системы методом «снизу вверх»; особенно это каса- ется операционных систем. 297
Рис. 6.10. Последовательность проектирования сверху вниз 6.7. Формальное описание методики разработки модульной архитектуры программной системы 6.7.1. Проектирование «сверху вниз» Как было рассмотрено выше, при разработке программных систем сформировались два основных метода: аналитический метод, или метод «сверху вниз» (top-down), и синтетический метод, или метод «снизу вверх» (bottom-up). В первом случае при разработке проекта идут от аб- страктного к конкретному, от общего к частному, от целого к деталям. Во втором методе порядок обратный. Оба метода можно иллюстриро- вать схемой, приведенной на рис. 6.11. Здесь Mj(i = 1,2, — вирту- альная машина 7-го уровня. Технология проектирования по методу «сверху вниз» сосредотачива- ется вокруг двух основных моментов [6, 14, 19, 41]: • задание директив, касающихся того, как осуществлять пошаго- вые уточнения и как объединять в модули выделенные функции про- 298
граммной системы, установленные на этапе внешнего проектирования системы; • задание методов представления проектируемого потока управле- ния и его взаимодействия с данными. Работу в соответствии с таким подходом можно прежде всего разде- лить на два главных этапа: 1) на первом этапе (шаг 1) выполняется начальный набросок систе- мы (на основании установленных требований, предъявляемых к систе- ме), причем модулей как таковых нет; 2) на втором этапе (шаги 2—и) определяются и реализуются модули с помощью пошагового уточнения. Целое Целое шаг МИШИНЫ машины шаг 1 VM1 VNn n 2 vm2 VNft.x n-1 « п-1 VNn4 VM2 2 п VNn VM1 1 Детали Детали Рис. 6.11. Обобщенная схема проектирования многослойного программного продукта Рассмотрим теперь более подробно эти шаги [26]. Шаг 1. Исследуется внешний проект системы (внешние специфика- ции, определяющие все функции пользователей и взаимодействующих систем) и предлагается общая структура программной системы в виде некой виртуальной машины VM\, снабженной соответствующими опе- рандами (структурами данных) и некоторым множеством операторов (структурами программ). Заметим, что на этом шаге данные представляются в том виде, с ко- торым имеет дело пользователь: приказ, счет, сообщение, отчет, вход- ной или выходной документ и т.п. Кроме того, данными могут быть спе- циально описанные сигналы, поступающие в систему от взаимодейст- вующих с ней других систем, устройств или линий связи. Для машины определяется программная система, которая удовлетворяет внешне- му проекту системы. Обозначим ее следующим образом: =<О|,Г>|>, (6.13) где О| — множество операторов виртуальной машины VM\', D\ — множе- ство данных виртуальной машины УМ\Ь причем (9| и D\ определяются внешними спецификациями системы SP™ (см. главу 5). Шаги 2-л. На последовательно выполняемых шагах предложенная структура уточняется посредством разложения на модули (структуры 299
данных и программные структуры). Это уточнение продолжается до тех пор, пока на шаге п не будут получены модули, которые могут быть сформулированы с помощью имеющихся в распоряжении разработчи- ков языков программирования и реализованы на имеющемся (или зара- нее заданном) компьютере. Уточнение, выполняемое на каждом шаге, можно рассматривать как два параллельно выполняемых процесса: • процесс анализа, т.е. процесс декомпозиции, состоящий из дис- кретной последовательности шагов, на каждом из которых система ис- следуется и представляется более детально; • процесс синтеза, т.е. процесс композиции, когда последовательно, шаг за шагом, происходит построение системы. Этот процесс уточнения может быть описан следующим образом. 1. Процесс состоит из дискретной последовательности шагов. II. На шаге i в результате принятия решений S, вводятся новые (более простые) модули (компоненты) е М, и соответствующие множества операторов о у и множества операндов Jy. Виртуальная машина z-го уровня представляется следующим обра- зом: VMi = <Mi, Oi, Di>, (6.14) где Mj = {nig \ j = 1,2,..., TV,} — множество модулей уровня i, N, — количе- ство модулей уровня i; Q = {og | j = 1, 2,..., A)} — множество операто- ров модулей виртуальной машины Л/„ в котором каждый оператор мож- но представить как отображение некоторой функциональной fg и син- таксической спецификации sg конкретного модуля тд\ тц :< Л, Зд >—> Од, где Л е SP™ и slf g SP™, где — внутренние спе- цификации, определяющие /-Й уровень виртуальной машины, заметим, что на уровне z = 1 используются внешние спецификации виртуальной машины пользователя ЗР? (см. гл. 5); Di = {dg- | j = 1, 2,..., jVJ - мно- жество операндов модулей Л/„ в котором каждый операнд можно пред- ставить как отображение некоторой синтаксической и семантической спецификации ss,7 данных конкретного модуля т^: ss„ —> dh где ssg g SP™, SP™ — внутренние спецификации, определяющие интерфейс соответствующей виртуальной машины. Заметим, что любой модуль nig g выполняет некоторый набор операторов Од и использует некоторый набор данных Dg, для которых выполняются следующие условия. 1. Возможно, что множества операторов различных модулей пере- секается (т.е. модули используют одни и те же операторы): Од r\Ojk ^0, j^k, при этом N, >| Од|> 1. Замечание: на первом уровне виртуальной машины имеется один модуль, представляющий виртуальный процессор с требуемыми свойствами. Каждый следующий уровень в результате декомпозиции содержит большее число модулей, 300
т.е. jV;+j > N.. Если предположить, что возможно соотношение jV;+j > N., то это бы значило, что модули /-го уровня идентичны модулям i + 1 уровня). 2. Возможно также, что множества операндов различных модулей пересекаются (модули используют одни и те же операнды): Dy n Dik 7е 0, / £ при этом Nf >| Dy n Dik |> 1 (справедливо замечание, указанное в п. 1). 3. Оу cz при этом возможно, что для некоторого модуля Оу - 0. Это означает, что модуль ту используют только операторы базового слоя. 4. Dy cz Д, при этом возможно, что для некоторого модуля Dy — 0. Это означает, что модуль ту не обрабатывает данные слоя /. 5. невозможно одновременное выполнение условий Оу=0 и Dy - 0 для одного и того же модуля. III. Шаги процесса уточнения зависят от определяющих декомпози- цию решений Sj. На основе этих решений определяются множества опе- раторов и операндов, реализуемых модулями следующего слоя, которые необходимы для выполнения модулей предыдущего слоя: Vm,-, е М,- (5, (О,) -> OMJ) & (5f (D„) -> DMJ), j = 1,2,..., N,. (6.15) IV. На основе полученных результатов решения S, формируется мно- жество модулей слоя (виртуальной машины) Л//+1: Ш/+1 =<Л/,.+1,О,.+1, Д+1 > (6.16) где Mi+l = {и!/+1 . | j = 1,2,Nl+1} — множество модулей уровня / + 1, Vf+1 — количество модулей уровня /+1, при этом V/+| > ЛЛ; Q+i = {°,+i j 17 = 1,2,Vz+l} — множество операторов модулей вирту- альной машины Л/-+1, в котором ту спецификации каждого оператора можно представить как отображение части функциональной и синтак- сической спецификации модулей виртуальной машины Л/,; D]+{ = {J/+17 | j = 1,2,..., jV-+1} — множество операндов модулей вирту- альной машины Л/;+1, в котором спецификации каждого операнда можно представить как отображение части синтаксической и семантической спецификации модулей ту виртуальной машины Мк V. Решение S, принимается на основе некоторой стратегии. Имеют- ся три основные стратегии разбиения при применении композиционно- го анализа [20]. Для разбиения модулей (подмодулей) используется од- на из следующих стратегий. Разбиение STS (исток - преобразование — сток) предполагает деление модуля на функции, занимающиеся получе- нием данных, изменением из формы (преобразованием), а затем достав- кой их в некоторую точку вне модуля. Операционное разбиение состоит в делении модуля на части, каждая из которых выполняет операции от- 301
дельного типа. Функциональное разбиение- это деление задачи на функции, выполняющие преобразования данных. Разбиение STS обычно применяют для выделения первого слоя мо- дулей, а затем к каждому подмодулю применяется одна из трех страте- гий, причем выбор зависит от специфики подзадачи. Кроме этих страте- гий, рекомендуется учитывать следующие правила: • осуществлять, возможно, более глубокое разложение решений на части; • по возможности дольше не принимать решений, относящихся к деталям представления данных; • выбор проектировочных решений осуществлять на основе таких критериев, как эффективность, экономия памяти, ясность и согласован- ность структуры; • при выборе решений учитывать и возможные альтернативы, т.е. вместо одного решения следует разрабатывать семейство решений; • сначала реализовать самые простые решения; • построение системы производить небольшими шагами, на каждом шаге принимать решения минимальными порциями. Теоретически процесс уточнения является однопроходным процес- сом, но практически это все же не так. На каждом шаге подходящие ре- шения выбираются из некоторого множества альтернатив. На каком-ли- бо из последующих шагов может выясниться, что это решение неприем- лемо. Поэтому в процессе работы приходится возвращаться назад и вы- бирать там другое решение, обеспечивающее возможность успешного продолжения. Каждый шаг в процессе уточнения соответствует некото- рой ступени абстракции или одному слою системы. В результате шага 1 получается слой 1, в результате шага 2 - слой 2 и т.д. Слой 0 представ- ляет систему в самой абстрактной (общей) форме. Все прочие слои слу- жат для определения модулей системы. В самом нижнем слое модули уточнены настолько, что их можно выразить с помощью имеющихся языков программирования. Как было уже сказано, каждый слой можно рассматривать как мно- жество компонентов, выполняемых с помощью некоторой виртуальной машины. Слой i реализуется машиной ИЛ/, с помощью слоев j (j > z). Слой i следует рассматривать как логически полный, когда в нем можно задать выполнимый на машине М, алгоритм Аь реализующий постанов- ку задачи системы в предположении существования машины М,. Интерфейс между слоем i - 1 и более конкретизированным слоем i находится в модулях, которые слой i предоставляет слою i - 1 и которые зафиксируются при выборе решений во время проектирования. Таким образом, задача слоя i состоит в моделировании машины, существова- ние которой предполагалось в слое / — 1. Очевидно, что применение этого метода даст следующее: • четко выделены отношения между модулями проекта; • вообще говоря, реализация каждого модуля возможна при знании специфицированных отношений между его подмодулями; 302
• в предположении правильности подмодулей правильность модуля может быть проверена независимо от остальной части системы и от спо- соба дальнейшего разложения подмодулей; • наконец, имеется возможность обеспечить в весьма большой сте- пени независимость программной системы от компьютера (машинного языка), перенося эту зависимость в нижние слои. Следует иметь в виду, что функциональное и операционное разбие- ние — это в основном интуитивные процессы, во многом зависящие от разработчика. Что касается разбиения STS, это более сложный процесс, для выполнения которого можно использовать следующие рекоменда- ции [20]. 1. Основываясь на потоке данных в системе (задаче), представить ее структуру в виде нескольких (3—10) процессов. 2. Определить главный поток данных системы и главный выходной поток. 3. Проследить входной поток данных по структуре системы (моду- ля). При этом можно обнаружить два явления: входной поток изменяет форму, становясь все абстрактнее по мере того, как идет продвижение по структуре процессов системы. В конце концов будет найдена точка, где поток как будто пропадает. Точка, где он появляется в последний раз, - точка наивысшей абстракции входного потока. 4. Подобный анализ нужно выполнить для выходного потока дан- ных, двигаясь в обратном направлении, и определить точку, где выход- ной поток появляется в самой абстрактной форме. Эти точки представ- ляют особый интерес, поскольку они делят систему (модуль) на наибо- лее независимые части. 5. Две найденные точки обычно делят систему на три части. Эти час- ти представляются модулями с соответствующими функциями. Опреде- ленные таким образом модули становятся подчиненными по отноше- нию к модулю, разбиение которого выполняется. 6. Определяются сопряжения этих модулей на уровне качественного описания входных и выходных аргументов. Детали этих сопряжений бу- дут определены при внешнем проектировании модулей. Рассмотрев технологию проектирования по методу «сверху вниз», следует задаться вопросом: когда прекратить разбиение системы, т.е. сколько уровней должно быть или чему равно п? Этот момент определя- ется по следующему правилу: логика модулей должна стать интуитивно понятной. Это означает, что модуль, вероятно, будет содержать не более 50-100 предложений [13, 32]. 6.7.2. Проектирование «снизу вверх» В этом случае разработка осуществляется в направлении от деталей к целому, от отдельного через частное к общему. Если принцип «сверху вниз» характеризовался разложением (декомпозицией) на модули, то те- перь, наоборот, проводится объединение (композиция) элементарных компонентов в более сложные и таким образом строится программная 303
система. Часто целью разработки в этом методе является повышение уровня языка программирования, а не разработка конкретной програм- мы, Это означает, что для данной предметной области выделяются ти- пичные функции, которые специфицируются, а затем программируются отдельные программные модули, выполняющие эти функции. Сначала в виде модулей реализуются более простые функции, затем создаются модули, использующие уже имеющиеся функции, и т.д. Действия разработчика в соответствии с таким подходом можно раз- делить на два этапа. На первом этапе (шаг Г) предлагается состав компонентов, которые должны войти в систему. В общем случае это могут быть команды ма- шинного языка, если идет разработка с уровня физической машины. Од- нако, как уже говорилось выше, мы будем рассматривать программные системы одного языкового уровня, а именно языка прикладного про- граммирования (не важно, какого конкретно). Будем считать, что на ис- ходном уровне (М| для технологии «снизу вверх») задано некоторое множество элементарных компонентов, реализующих функции (опера- торы) первого уровня. Виртуальная машина 1-го уровня представляется следующим обра- зом: (6.17) где . | j = 1, 2,..., Nj} - множество модулей уровня 1; JV| — коли- чество модулей уровня 1; = {oly- | j = 1, 2,..., NJ - множество опера- торов модулей виртуальной машины М\, в котором каждый оператор характеризуется определенной функциональной . и синтаксической спецификацией конкретного модуля тХ)—>< >; Z>] = | j = 1, 2,7VJ — множество операндов модулей M\, в кото- ром каждый операнд задан определенной синтаксической и семантиче- ской спецификацией данных конкретного модуля m{j :o{j —> На втором этапе (шаги 2-п) последовательно, шаг за шагом, разра- ботчик объединяет элементарные компоненты таким образом, чтобы реализовать более сложные функции и приблизиться к требуемой цели. На последнем шаге должна быть получена искомая система. Процесс объединения может быть получен следующим образом. 1. Процесс состоит из дискретной последовательности шагов. 2. На шаге i в результате принятия решений S, путем объединения компонентов уровня i — 1 строятся более сложные компоненты (модули) /и. е М. и соответствующие им множества операторов оу- и множества операндов dir Виртуальная машина i-ro уровня представляется следующим обра- зом: 304
где = {т- | j = 1, 2,..., Nt} - множество модулей уровня z, N, — коли- чество модулей уровня z; Os = {о- | j = 1, 2,..., } — множество операто- ров модулей виртуальной машины Л/,-, в котором каждый оператор мож- но представить как объединение некоторых операторов модулей ниже- лежащего уровня: (6.18) Функциональные (£,) и синтаксические (s5y) спецификации конкрет- ного модуля определяются соответствующими спецификациями мо- дулей нижележащего уровня: Угу — > $ij — > J 1» 2, ♦.., -^f-l (6.19) При этом D. = {dy | j = 1, 2,..7VJ — множество операндов модулей Mh в котором каждый операнд можно представить как некоторое обоб- щение синтаксической и семантической спецификаций ; данных тех модулей т._х е р которые используются для построения соответ- ствующих операторов модулей М,. 3. Как это типично для процесса синтеза, решения ^ принимаются на каждом уровне. Эти решения определяют указанные объединения: Vmy е ->-+ ДД J = 1.2,.... N, (6 19) В этом процессе объединения также возможны возвраты на преды- дущие уровни синтеза. Дело в том, что неподходящие решения иногда проявляются лишь на самом последнем шаге. Тогда необходимо на од- ном из предыдущих шагов принять другое решение и продолжать рабо- ту с этого шага. Как уже отмечалось, принцип проектирования «снизу вверх» при реализации и тестировании имеет то преимущество, что ра- бота может быть начата с реализуемых (проверяемых) компонентов. Однако это, конечно, не означает, что к реализации можно приступить, не имея полного проекта системы. 6.7.3. Еще раз о проектировании архитектуры ПС на основе объектно-ориентированной и компонентной методологии Автор статьи [11] Дж. Макгрегор отмечает, что современная архи- тектура предлагает развернутую и точную модель системы. Методики формирования программной архитектуры предполагают детальный ана- лиз системы перед ее реализацией. Такие методики, как Attribute Driven Design (ADD) [7], гарантируют, что программное обеспечение, реализо- ванное на основе предварительно сформированной архитектуры, будет точно отвечать своему предназначению. Есть немало примеров исполь- зования архитектуры в стратегических целях. Один из самых известных примеров — Common Object Request Broker Architecture (CORBA). Эта архитектура служит для связи унаследован- ных систем, для интеграции систем, написанных на разных языках, 305
и поддержки взаимодействия между компьютерами с различными аппа- ратными архитектурами. CORBA позволяет дать новую жизнь унасле- дованным системам и в то же время быстро интегрировать в них новые приложения, что обеспечивает предприятиям стратегические преимуще- ства перед конкурентами, модифицирующими унаследованные систе- мы. Еще один пример - свободно распространяемая интегрированная среда разработки Eclipse. В целом процесс проектирования архитектуры состоит из системати- ческой декомпозиции элементов верхнего уровня на совокупности бо- лее мелких элементов. На рис. 6.12 исходный элемент путем декомпози- ции разделяется на элементы «Модель», «Контроллеры» и «Представле- ния». Каждый из них влияет на общее поведение исходного элемента. Декомпозиция Рис. 6.12. Декомпозиция как основа проектирования архитектуры любой программной системы С помощью подхода ADD архитектор выбирает конкретную деком- позицию, стремясь улучшить определенные свойства конечного продук- та. Следует, однако, иметь в виду, что каждая декомпозиция, как прави- ло, какие-то свойства ухудшает. На рис. 6.12 декомпозиция «“Модель” — “Представление” - “Контроллер”» (Model — View — Controller, MVC) вы- брана для расширения возможностей модификации системы, в частно- сти, для более эффективного добавления новых представлений модели. Но такая декомпозиция приведет к снижению производительности, по- скольку при возникновении любых изменений «Модели» придется уве- домлять «Представление». С точки зрения архитектора, это приемле- мый компромисс, поскольку система работает не в реальном (компью- терном) времени, а в расчете на восприятие изменений человеком. Процесс создания архитектуры предполагает разработку системы с конкретными свойствами и функциональностью, причем каждое из та- ких свойств имеет заданный приоритет. Начиная с общей структуры, которая поддерживает всю требуемую функциональность (она пред- ставлена в квадрате в левой части рис. 6.12), архитектор методично про- водит декомпозицию функциональности и распределяет ее между ком- понентами. Процесс декомпозиции, показанный на рис. 6.13, продолжа- ется до тех пор, пока компоненты не будут детализированы должным образом. 306
Декомпозиция Рис. 6.13. Многоуровневая декомпозиция Затем на предприятии могут быть созданы рабочие группы, каждая из которых станет заниматься проектированием и реализацией своего подмножества элементов архитектуры. Как видим, суть процесса проектирования заключается в декомпози- ции системы независимо от способа ее представления: модульный, объ- ектный или компонентный. В любом случае результатом будет много- слойный программный продукт. Литература к главе 6 1. Henry, S. and Kafura, D. Software Structure Metrics Based on Infor- mation Flow. IEEE Transactions on Software Engineering. - Sept. 1981.- Vol. 7. - № 5. - P. 510-518. 2. McCabe, T. J. A Complexity Measure. IEEE Transactions on Software Engineering. - Apr. 1976. - Vol. 2. -№ 4. - P. 308-320. 3. Page-Jones, M. The Practical Guide to Structured Systems Design. Englewood Cliffs. - NY: Yourdon Press, 1988. 4. Parnas, D. On the Criteria to the Be Used in Decomposing Systems in- to Modules. Communications of the ACM. - Dec., 1972.-Vol. 15 (12).- P. 1053-1058. 5. Yourdon, E., Constantine, L. Structured Design: fundamentals of a discipline of computer program and systems design. - Englewood Cliffs, NJ: Prentice-Hall, 1979. 6. Баманн П., Френцелъ M., Ханцшман К. и др. Программные систе- мы: пер. с нем. / под ред. Бахманна. — М.: Мир, 1988. - 288 с. 7. Басс Л., Клементс П., Кацман Р. Архитектура программного обес- печения на практике. — 2-е изд. — СПб.: Питер, 2006. — 575 с. 8. Буч Г., Рамбо Д., Якобсон И. Язык UML: руководство пользовате- ля. — 2-е изд.: пер. с англ. — М.: ДМК Пресс, 2007. — 496 с. 9. Вирт Н. Алгоритмы и структуры данных: пер. с англ. — М.: Мир, 1989.-358 с. 10. Гагарина Л.Г., Кокорева Е.В., Виснадул БД. Технология разра- ботки программного обеспечения: учебное пособие / под ред. Л.Г. Гага- риной. - М.: ФОРУМ; ИНФРА-М, 2008. - 400 с. 11. Грегор Дж. Программная архитектура [Электронный ресурс] // URL: http://ooad.asf.ru/standarts/Library/Architecture/index.aspx 307
12. Дейкстра Э.В. Дисциплина программирования: пер. с англ. — М.: Мир, 1978. 13. Зелковец М., Шоу А., Гэннон Дж. Принципы разработки про- граммного обеспечения: пер. с англ. — М.: Мир, 1982. - 368 с. 14. Зиглер К. Методы проектирования программных систем: пер. с англ. - М.: Мир, 1985. — 328 с. 15. Кватрани Т., Палистрает Д. Визуальное моделирование с помо- щью IBM Rational Software Architect и UML: пер. с англ. — М.: КУДИЦ- ПРЕСС.-2007.- 192 с. 16. Киммел П. UML. Основы визуального анализа и проектирования = UML. Универсальный язык программирования: пер. с англ. — М.: НТ Пресс, 2008. - 272 с. 17. Крылов Е.В., Оспгрейковский В.А., Типикин Н.Г. Техника разра- ботки программ: в 2 кн. Кн. 2: Технология, надежность и качество про- граммного обеспечения. - М.: Высшая шк., 2008. - 469 с. 18. Кулямин В.В. Технологии программирования. Компонентный подход [Электронный ресурс] // URL: http://lib.mdpu.org.ua/e-book/vstup/ L/Jogolev.pdf 19. Дисков Б., Гатэг Дж. Использование абстракций и специфика- ций при разработке программ: пер. с англ. - М.: Мир, 1989. —424 с. 20. Майерс Г. Надежность программного обеспечения: пер. с англ. — М.: Мир, 1980.-360 с. 21. Макконнел С. Профессиональная разработка программного обес- печения: пер. с англ. - СПб.: Символ-Плюс, 2006. - 240 с. 22. Мартин Р., Ньюкирк Дж., Косс Р. Быстрая разработка про- грамм: принципы, примеры, практика. — Вильямс, 2004.-752с. 23. Модели реализации объектно-ориентированных программных систем. Связность объектов [Электронный ресурс] // URL: http:// 2programmcr. ru/te hnol og5 ? start= 16 24. Назаров C.B. Операционные системы специализированных вы- числительных комплексов: теория построения и системного проектиро- вания. — М.: Машиностроение, 1989. — 400 с. 25. Назаров С.В. Сложность программной системы. Сборник науч- ных трудов по материалам международной научно-практической конфе- ренции «Современные направления теоретических и прикладных иссле- дований 2011», Т. 2: Технические науки. — Одесса: Черноморье, 2011.— С. 58-65 26. Назаров С.В. Системы // Сборник научных трудов по материалам международной научно-практической конференции «Перспективные инновации в науке, образовании, производстве и транспорте 2011». — Одесса: Черноморье, 2011. - Т. 5. — С. 3-11. 27. Нильссон Дж. Применение DDD и шаблонов проектирования. Проблемно-ориентированное проектирование приложений с примерами на C# и .NET. — Издательский дом «Вильямс», 2008. — 560 с. 28. Новичков А. Метрики кода и их практическая реализация в IBM Rational ClearCase [Электронный ресурс] И URL: http://anovichkov. msk.ru/?p=13 308
29. Новичков А. Метрики кода [Электронный ресурс] // URL: http://anovichkov.msk.ru/7pM 196 30. Орлов С.А. Технологии разработки программного обеспечения: учебник. — СПб.: Питер, 2002 — 464 с. 31. Полис Г., Огастин Л., Лоу К., Мадхар Д. Разработка программ- ных проектов на основе Rational Unnified Process (RUP). - M.: Бином- Пресс, 2009. - 256 с. 32. Программные системы: пер. с нем. / под ред. П. Захманна. — М.: Мир, 1988.-288 с. 33. Разработка спецификаций. Средства специфицирования ПО [Электронный ресурс] И URL: http://www.cs.nuos.edu.ua/textbooks/ SWDevelop/SaprSW 1 /page5.htm 34. Савчук И. Почему объектно-ориентированное программирование провалилось? [Электронный ресурс] // URL: http://blogerator.ru/page/ oop_why-obj ects-have-failed 35. Таненбаум Э. Современные операционные системы. 4-е изд. — СПб.: Питер, 2011.- 1120 с. 36. Тассел В. Стиль, разработка, эффективность, отладка и испыта- ние программ: пер. с англ. — М.: Мир. 1985 - 332 с. 37. Турский В. Методология программирования: пер. с англ. — М.: Мир, 1981.-264 с. 38. Фанг Дж. Введение в IBM Rational Application Developer: учеб- ное руководство: пер. с англ. — М.: КУДИЦ-ПРЕСС. — 2006. - 592 с. 39. Холстед М. X. Начала науки о программах: пер. с англ. - М: Фи- нансы и статистика, 1981 — 128 с. 40. Хъюз Дж., Мичтом ДЖ. Структурный подход к программирова- нию: пер. с англ. — М.: Мир, 1980. - 280 с. 41. Цикритзис Д., Бернстайн Ф. Операционные системы: пер. с англ. — М.: Мир, 1977.
Глава 7 РЕФАКТОРИНГ ПРОГРАММНЫХ СИСТЕМ 7.1. Что такое рефакторинг Начиная с последнего десятилетия прошлого века получили широкое распространение эволюционные методологии разработки программного обеспечения, часто называемые адаптивными, такие как экстремальное программирование (Extreme Programming - ХР), метод Scrum, унифици- рованный процесс компании Rational (Rational Unified Process - RUP), адаптивный унифицированный процесс (Agile Unified Process - AUP) и др. [38]. Часть этих методологий рассмотрена в третьей главе книги, где рассматривалась сущность их методов, которые по своему характеру являются и итеративными, и инкрементными, а адаптивный подход яв- ляется эволюционным и вместе с тем характеризуется высокой степе- нью взаимодействия участников разработки проектов. В организациях, применяющих подобные технологии, внедряются такие адаптивные методики, как рефакторинг, программирование в па- ре, разработка на основе тестирования (Test-Driven Development - TDD) и адаптивное проектирование на основе модели (Agile Model Driven Development — AMDD). Концепция рефакторинга (refactoring) возникла в кругах, связанных со Smalltalk, но вскоре нашла себе дорогу и в лагеря приверженцев других языков программирования [3]. Несколько позже М. Фаулер в своей фундаментальной монографии, выдержавшей в на- шей стране несколько изданий [33], дал определение рефакторинга как небольшого изменения в исходном коде, которое способствует улучше- нию проекта кода без изменения его семантики. Иными словами, рефакторинг — это улучшение качества сделанной программистом работы без нарушения или добавления чего-либо. В своей книге М. Фаулер говорит также о том, что если возможно под- вергнуть рефакторингу прикладной исходный код, то, видимо, есть воз- можность подвергнуть рефакторингу схему базы данных. Однако он считает, что рефакторинг баз данных - достаточно сложная и отдельная проблема, и исключил эту тематику из своей книги. Однако это замеча- ние не осталось незамеченным, и несколько позже появилась еще одна фундаментальная книга, посвященная именно рефакторингу баз данных [38]. Эволюция сложных программных систем требует от разработчика повышенного внимания к выбору архитектуры [34]. Практически всегда во время разработки появляются новые требования со стороны заказчи- ка, и приходится пересматривать первоначальную архитектуру, в том числе и структуру базы данных. Возможна и другая ситуация. Часто в фазе поддержки и эволюции приложения появляется желание переде- лать все «с нуля». Что же такое рефакторинг? Рефакторинг представляет собой про- цесс такого изменения программной системы, при котором не меняет- ся внешнее поведение кода, но улучшается его внутренняя структура. 310
При проведении рефакторинга разработчик улучшает дизайн кода уже после того, как он написан [8, 16, 33]. Что дает рефакторинг? Вот некоторые преимущества. 1. Рефакторинг улучшает композицию программной системы (ПС). По мере развития программы часто приходится вносить изменения, ко- торые обусловлены текущей необходимостью. Изменения вносят и про- граммисты, которые не до конца понимают архитектуру ПС в целом. Поэтому постепенно код становится менее структурированным, и раз- бираться в нем все труднее. 2. Рефакторинг облегчает понимание ПС. Проведение рефакторинга обычно приводит к более глубокому пониманию того, как работает про- грамма. Такое понимание существенно ускоряет процесс программиро- вания. 3. Рефакторинг помогает найти ошибки. Поскольку рефакторинг уве- личивает понимание кода, то он и позволяет быстрее находить ошибки. 4. Рефакторинг позволяет быстрее писать программы. Все вышепе- речисленные пункты сводятся к тому, что рефакторинг позволяет быст- рее разрабатывать код. Рефакторинг является одной из основных практик экстремального программирования (ХР) и позволяет создавать хорошую архитектуру программы, но несколько непривычным путем. В традиционном подхо- де архитектура программы создается еще до того, как написана первая строчка кода. Архитекторы на основе требований к ПС создают все то, что необходимо кодировщикам для написания кода. Конечно, обычно архитекторы — опытные разработчики, тем не менее очень сложно сразу создать хорошую архитектуру. Если полагаться на рефакторинг, то вначале можно сделать лишь предварительный набросок будущей системы, а детали выяснять в про- цессе разработки. Чем больше разработчики знают о системе, тем боль- ше возникает мыслей по ее улучшению. Мелкие и средние изменения в коде сами по себе не оказывают огромного влияния на систему, но суммарный эффект часто превосходит все ожидания. Спустя несколько итераций архитектура системы становится стабильной и красивой. Ко- гда программист чувствует целостность и внутреннюю красоту систе- мы, он испытывает удовлетворение от работы. Может показаться, что такой подход ненадежен. Действительно, что- бы смело полагаться на рефакторинг, необходимо наличие следующих условий: 1. Автоматические тесты. Если они отсутствуют, то сложно контро- лировать изменения. Предположим, программист изменил название ме- тода. Теперь ему нужно узнать, где он вызывается. При наличии автома- тических тестов это делается элементарно. Если же изменения менее очевидные, то ценность автоматических тестов многократно возрастает. 2. Система контроля исходного кода. Управление исходным кодом является одной из главных практик разработки ПС. Если имеется рабо- тающая версия системы, то можно без опасений вносить изменения, по- 311
тому что всегда есть возможность вернуться к стабильной версии. Кон- троль кода дает свободу. 3. Итерационная разработка. Рефакторинг можно проводить и при обычном водопадном процессе, но его ценность в таком случае умень- шается. Итерации позволяют лучше организовать процесс рефакторинга и сделать его последовательным. 4. Регулярность осуществления рефакторинга. Только методичное применение может принести ощутимую пользу. Эпизодический рефак- торинг может быть даже опасным в случае отсутствия контроля исход- ного кода. Есть одна опасность, которая подстерегает тех, кто начинает исполь- зовать рефакторинг. Надо четко разделять процесс разработки нового кода и процесс изменения уже существующего. Например, разработчик нашел в программе метод, который можно изменить (улучшить). Он на- чинает его изменять, не доводит изменения до конца и вдруг вспомина- ет, что надо добавить в этот метод немного новой функциональности. Программист начинает ее добавлять, увлекается и забывает завершить рефакторинг. В результате придется тратить лишнее время на то, чтобы все заработало как надо. Поэтому, когда выполняется рефакторинг, надо обязательно доводить его до конца или вернуться к начальному состоя- нию и только после этого начинать писать новый код. Проблема выбора при добавлении в систему новой возможности воз- никает при разработке достаточно сложной ПС практически всегда. Са- мым сложным при выборе того или иного решения является доведение разработчиком своего выбора до непосредственного руководителя, что- бы он смог принять взвешенное решение. С точки зрения многих руко- водителей «взвешивание» заканчивается сразу же после того, как он ус- лышит предполагаемые сроки реализации. Неудивительно, что програм- мисты и архитекторы с таким подходом несогласны. С подобной ситуа- цией сталкивались известные специалисты, которые придумали типо- вые паттерны, описывающие такую ситуацию. Одним из таких паттер- нов является метафора технического долга, впервые описанная В. Кан- нингемом примерно 20 лет назад [32]. Под техническим долгом понимается осознанное компромиссное ре- шение, когда заказчик и ключевые разработчики четко понимают все преимущества от быстрого, пусть и не идеального технического реше- ния, за которое придется расплатиться позднее. И хотя, сточки зрения многих разработчиков, ситуация, когда плохое решение может быть хо- рошим, может показаться нереальной, на самом деле это вполне воз- можно. Если краткосрочное решение позволит компании получить ви- димые преимущества, выпустить продукт раньше конкурентов, удовле- творить ключевого заказчика или получить какие-то преимущества пе- ред конкурентами, тогда такое решение совершенно оправданно. Можно провести параллель между техническим и финансовым дол- гом. Финансовый долг означает, что вы получаете дополнительные средства сейчас, однако вам придется выплачивать фиксированную про- центную ставку, и в конце срока вернуть весь долг кредитору. Анало- 312
гичная ситуация происходит и в случае принятия неоптимального тех- нического решения. Как признается в ряде публикаций [28, 32, 39], именно такой подход к построению приложений является оптимальным с архитектурной точ- ки зрения. Если, принимая архитектурное решение, разработчик не чув- ствует компромисса между краткосрочными и долгосрочными выгода- ми, то в любом случае не стоит считать его окончательным. Вполне воз- можно, что неправильное решение принято неосознанно, из-за непони- мания предметной области или из-за будущего изменения требований заказчиком. Значительно проще снизить стоимость будущих изменений путем инкапсуляции важных решений в наименьшее число модулей, чем ста- раться продумать архитектуру до мельчайших подробностей в надежде учесть все возможные и невозможные сценарии. Для определенного круга задач идеальная продуманная архитектура возможна, а иногда просто необходима, но в большинстве случаев это не так: рано или поздно придет момент, когда видение системы разработчиком или за- казчиком изменится, что потребует внесения в нее существенных изме- нений. Однако даже при разумном накоплении технического долга сущест- вует вероятность того, что команда (или заказчики) пойдут по старому принципу (работает - не трогай), и никогда не вернутся к этому реше- нию снова, чтобы расплатиться за свой технический долг. Кроме того, существует множество случаев, когда технический долг накапливается постепенно и неосознанно. И если разработчики не будут об этом заду- мываться, то придут к ситуации, когда стоимость добавления новой воз- можности становится очень дорогой. В этом случае команда усиленно работает, исполнители устают, злятся друг на друга, не получив при этом никаких преимуществ перед конкурентами. Результатом является «грязный код» - второй источник технического долга [32]. Технический долг в классическом понимании является преднамерен- ным и в основном касается стратегических решений, поэтому и ответст- венность за него лежит на заказчиках и архитекторах, но все, что связа- но с «грязным кодом», касается по большей части простых разработчи- ков [11, 13]. Во время кодирования, как и во время принятия любых других решений, разработчик должен рассматривать краткосрочные и долгосрочные выгоды. Обычно в процессе развития приложения на- капливается как груз крупных стратегических ошибок или непониманий требований, так и масса мелких тактических ошибок наподобие длин- ных функций с неочевидными обязанностями и сложными побочными эффектами; расплывчатых классов с нечеткими границами и обязанно- стями; отсутствия идиом именования и документации и т.п. И если команда не отдает свои долги путем переосмысливания суще- ствующих решений и их последующего рефакторинга, если она не ста- рается держать качество кода на высоком уровне, то рано или поздно это повысит стоимость развития и сопровождения кода настолько, что 313
все существующие средства будут уходить на оплату процентов по тех- ническому долгу. Существует несколько способов выплаты технического долга. Наи- более простым из них является рефакторинг системы или некоторых ее частей или полное переписывание некоторых частей системы. Однако часто бывает, что вместо прагматичного подхода к улучшению некото- рых частей системы для получения разумной выгоды в будущем про- граммист начинает переписывать все подряд: что нужно, что не обяза- тельно и что вообще переписывать не стоит. Это приводит к еще одной метафоре, которая описывает подобное неустранимое желание к пере- писыванию старого кода - к синдрому рефакторинга [28]. Первый симптом такого синдрома — неуважительное отношение к чужому коду. Заметим, что, во-первых, это не красит разработчика, а во-вторых, в большинстве случаев не уменьшает технический долг, который должен уменьшаться благодаря этому рефакторингу. Система в целом не становится более сопровождаемой и понятной, просто она теперь ближе и понятнее одному конкретному человеку. Конечно, на некоторое время сопровождаемость системы улучшается, но все выго- ды заканчиваются, когда за этот кусок кода берется другой разработчик. Стремление к идеальному коду (симптом 2) является наиболее бла- гим намерением при изменении существующего кода. Слепое следова- ние непродуманным стандартам кодирования не многим лучше, чем от- сутствие стандартов кодирования вовсе. Красота кода- не самоцель; код может быть красиво оформленным, все функции могут быть не- большого размера, с достаточным количеством комментариев и даже проверены тестами. Но это не значит, что этот код будет справляться со своей основной задачей, поскольку никто не удосужился узнать эту за- дачу у пользователя. С течением времени с высоты своего собственного профессиональ- ного опыта, благодаря более четкому пониманию требований пользова- теля и пониманию своей собственной системы даже свой код начинает казаться ужасным (симптом 3). Поэтому улучшение своего собственно- го кода — процесс настолько же полезный, как и улучшение кода чужо- го. Бывают случаи, когда даже свой код подвергается значительным пе- ределкам не раз за год, но при этом в нем толком ничего и не меняется. Это нормально, если человек это делает для своего собственного проекта, когда при этом нарабатываются новые решения уже хорошо известных задач. Но это не совсем разумно для коммерческого продук- та. Получается, что код переписывается просто потому, что програм- мист узнал о новой возможности в языке программирования или о но- вой библиотеке, которая значительно лучше решает эту же задачу. Про- ходит время, появляются новые возможности, процесс опять повторяет- ся сначала, но при этом никакие долги не выплачиваются и ничего цен- ного в систему не добавляется. Практически в любом деле прагматизм и отсутствие крайностей яв- ляется лучшим выбором, и рефакторинг существующего кода — не ис- ключение. Не стоит забывать о законе Парето — принципе 80/20. В соот- 314
ветствии с ним 20% усилий на улучшение кода и поддержание его в аде- кватном состоянии достаточно, чтобы улучшить его на 80%. А если это не так, то инвестировать дополнительные средства на покрытие столь большого технического долга просто нет смысла, и стоит начать все с чистого листа. И тут начинает проявляться эффект второй системы [39]. Когда тех- нический долг команды начинает превышать все мыслимые и немысли- мые границы, у команды разработчиков появляется как минимум два способа его погашения: провести рефакторинг системы таким образом, чтобы стоимость будущих изменений была не столь высокой, или оста- вить текущую версию системы в покос и переписать все заново. В первом случае легко столкнуться с синдромом рефакторинга, ко- гда изменения делаются не с расчетом уменьшения стоимости будущих изменений, а вносятся просто ради изменений. Во втором случае может возникнуть «эффект второй системы», когда развиваются и совершенст- вуются уже никому не нужные функции системы, а мысль «А не пере- писать ли все?» является первой и единственной, которая приходит в го- лову команде, как только она сталкивается с чужим кодом. И хотя в классическом понимании «эффект второй системы» немного отлича- ется от патологической нелюбви к чужому коду и постоянному его пе- реписыванию, оба эти случая имеют и что-то общее, так что имеет смысл оба эти симптома рассмотреть совместно. Поскольку многие разработчики и архитекторы вкладывают свою душу при создании программных систем, совсем не удивительно, что, приступая к новой версии своего детища, они стараются исправить все ошибки и недочеты и пытаются создать идеальную систему. Кроме то- го, окрыленные успехом первой системы, разработчики начинают ду- мать, что у них достаточно знаний и опыта в данной предметной облас- ти, чтобы на основе существующих частных знаний делать общие выво- ды. Это зачастую приводит к преждевременному обобщению решения, что очень часто считается злом не меньшим, чем преждевременная оп- тимизация. Другой проблемой при создании второй системы является непра- вильная переоценка ценностей, когда доводятся до совершенства мо- рально устаревшие функции системы, а принципиально новые подходы и решения не развиваются. Это приводит к идеальной реализации нико- му не нужных функций и вряд ли полезно для успеха системы. Все эти проблемы описаны еще Ф. Бруксом в его «Мифическом человеко-меся- це», где помимо описания этой проблемы даются и рекомендации по ее решению. И хотя она вышла задолго до замечательной книги Э. Ханта и Д. Томаса [35], Ф. Брукс дает тот же самый совет, что постоянно зву- чит в книге прагматиков: не нужно крайностей, и больше прагматизма и самодисциплины. Этот прагматизм проявится в разумном балансе причин изменения программного кода. Код лучше подвергать рефакторингу сразу, как толь- ко выявлена необходимость. Не стоит забывать про баланс между жела- ниями команды разработчиков и требованиями от заказчика по срокам. Баланс заключается в том, чтобы разумно совмещать введение новых 315
функций и рефакторинга. Во многих случаях это экономит время. Разра- ботчику следует всегда отдавать себе отчет, тратить время на рефакто- ринг или же заняться новым функционалом. Заказчики легче расстаются с деньгами, чем с благодарностью за хорошо сделанную работу, тем бо- лее за качественный код, в котором они ничего не понимают. У. Апдайк, написавший главу в книге М. Фаулера [33], задается во- просом: почему разработчики не хотят применять рефакторинг к своим программам? Он считает, что если проект начинается с нуля и понятна задача, для решения которой предназначена система, и тот, кто финан- сирует проект, готов поддерживать его, пока разработчик не будет удов- летворен результатами, то разработчику крупно повезло. Такой сцена- рий идеален для применения объектно ориентированной технологии, но большинство может о нем только мечтать. Значительно чаще предлагается расширить возможности уже имею- щегося программного обеспечения. Если у разработчика далеко не пол- ное понимание того, что он делает, или он поставлен в жесткие времен- ные рамки, что можно предпринять? Можно переписать программу за- ново. Применить свой опыт проектировщика, исправить все прежние грехи, работать творчески и с удовольствием. Но кто оплатит расходы? И можно ли быть уверенным, что новая система сможет делать все то, что делала старая? Можно скопировать и модифицировать части суще- ствующей системы, чтобы расширить ее возможности. Это может пока- заться оправданным, и даже будет рассматриваться как демонстрация повторного использования кода. Не надо даже разбираться в том, что будет повторно использоваться. Однако с течением времени происходит умножение ошибок, про- граммы разбухают, их архитектура разрушается, и нарастают дополни- тельные издержки на модификацию. Рефакторинг лежит посередине ме- жду этими двумя крайностями. Он дает возможность изменить сущест- вующее программное обеспечение, сделав понимание конструкции бо- лее явным, развить строение и извлечь повторно используемые компо- ненты, сделать архитектуру программы яснее, а также создать условия, облегчающие ввод дополнительных функций. Рефакторинг может спо- собствовать извлечению выгоды из сделанных ранее инвестиций, уменьшить дублирование и рационализировать программу. Допустим, что разработчика привлекают эти преимущества. Он со- гласен с Ф. Бруксом в том, что модернизация представляет собой «внут- ренне присущую сложность» разработки программного обеспечения. Он согласен с тем, что теоретически рефакторинг дает указанные пре- имущества. Почему же он все-таки не применяет рефакторинг к своим программам? Возможны четыре причины. 1. Разработчик может не понимать, как выполнять рефакторинг. 2. Если выгоды ощутимы лишь в далекой перспективе, зачем тратить силы сейчас? В долгосрочной перспективе, когда придет время пожи- нать плоды, разработчик может оказаться вне проекта. 316
3. Рефакторинг кода является непроизводительной деятельностью, а разработчику платят за новые функции. 4. Рефакторинг может нарушить работу имеющейся программы. Все это законные причины для беспокойства, отмечает У. Апдайк. Ему не раз доводилось выслушивать их от персонала коммуникацион- ных и высокотехнологических компаний. Одни из них относятся к тех- нологиям, а другие - к администрированию. Все они должны быть рас- смотрены, прежде чем разработчики взвесят возможность рефакторинга своего программного обеспечения. Разберемся с каждой из них по оче- реди. Как и когда применять рефакторинг? Как можно научиться рефакто- рингу? Каковы инструменты и технологии? В каких сочетаниях они мо- гут принести какую-нибудь пользу? Когда следует их применять? Ответы на эти вопросы, по мнению У. Апдайка, даны в книге М. Фаулера [33]. В этой книге описано несколько десятков различных рсфакторингов, которыми автор пользуется в своей работе. Приведены примеры применения рсфакторингов для внесения в программы важных изменений. С этим можно согласиться, тем не менее в Интернете можно найти и отрицательные мнения об этой книге [2]. Однако следует заме- тить, что негативное мнение связано с ожиданием рекомендаций архи- тектурного рефакторинга ПС, в то время как М. Фаулер в своей моно- графии говорит в основном о рефакторинге программного кода, причем чаще на уровне «внутри класса» и значительно реже на уровне классов. Рефакторингу архитектуры объектно ориентированных ПС уделено внимания значительно меньше. В работе [6] в довольно концентрированной форме даны ответы на основные вопросы, связанные с проведением рефакторинга ПС, а также ряд рекомендаций, как проводить рефакторинг. Обсуждаются следую- щие вопросы: цель рефакторинга; простые и сложные методы рефакто- ринга; когда начинать рефакторинг; как начать рефакторинг; как не по- ломать рабочий код; когда рефакторинг не нужен? И в заключение даст- ся каталог методов рефакторинга и примеры рефакторинга. Перейдем к рассмотрению этих вопросов с позиций автора этой ра- боты. Цель рефакторинга. Определите задачи, которые перед вами стоят. В большинстве случаев это упрощение ввода новых функций. Но есть еще и другие цели. Профессиональные программисты, знакомые с пат- тернами проектирования [10], стараются привести структуру кода в по- рядок в соответствии с подходящими паттернами для улучшения под- держки, расширяемости и повторного использования кода. Имеется также возможность использовать автоматизированные сред- ства поиска кода, подлежащего рефакторингу. Это могут быть функции с большим количеством аргументов или слишком длинные функции. С помощью автоматических средств можно также выявлять структур- ное сходство различных методов. Такие функции являются кандидатами на проведение рефакторинга. Простые и сложные методы рефакторинга. Простые методы ре- факторинга — это базовые, фундаментальные методы, применяемые 317
в рефакторинге. Например, это выделение метода, перемещение поля класса, выделение класса, переименование метода, класса или поля и т.д. Простые методы рефакторинга — это фундамент, на основе кото- рого строится любой вид рефакторинга. Нельзя начать рефакторинг, не зная фундаментальных методов. Сложные методы рефакторинга — это, по сути, комбинация простых, но решающих одну базовую задачу мето- дов. Примером сложного метода рефакторинга может быть разделение наследования, который включает в себя выделение класса, перемещение метода, подъем метода и подъем поля. Когда начинать рефакторинг? Рефакторинг обычно применяется при добавлении новой функции. Причина, по которой стоит проводить рефакторинг, добавляя новую функцию, — это возможно несовместимый или неясный дизайн кода для добавления новых функций. Рефакторинг следует применять, если требуется исправить ошибку. При исправлении ошибок польза рефакторинга в том, что код становится более понятным. Если получается сообщение об ошибке, то это признак необходимости рефакторинга, потому что код не был достаточно ясным и разработчик не смог увидеть ошибку. Целесообразно применять рефакторинг при разборе кода. Разбор кода - это хорошая практика, когда в команде раз- работчики проверяют код друг друга. Как начать рефакторинг? Нужно убедиться, над какой частью кода будет производиться рефакторинг. Нужно точно знать, какую часть ко- да могут затронуть изменения. Определить методы, с помощью которых будет производиться рефакторинг. Выписать эти методы и для каждого составить последовательность шагов, которые будет необходимо выпол- нить. Проделать эту работу один раз для каждого метода, чтобы быть уверенным, что ничего не пропущено. Большинство методов рефакто- ринга уже имеют порядок действий и описание применения. Ознакомь- тесь с этими методами и применяйте их. Как не поломать рабочий код? Постройте набор тестов для перера- батываемой части кода. Тесты важны, так как даже при последователь- ном проведении рефакторинга можно допустить ошибки, а тесты помо- гут исключить возможные ошибки. Проводя рефакторинг, можно пола- гаться на тесты. Старайтесь покрыть тестами достаточную часть кода для работы функции. Тесты должны быть как «чистые», проверяющие вычисления правильных данных, так и «грязные», посылающие в тест недопустимые, не ожидаемые данные. Когда рефакторинг не нужен? Иногда рефакторинг не нужен. На- пример, когда надо переписать программу с нуля. Иногда имеющийся код настолько запутан, что подвергнуть его рефакторингу, конечно, можно, но проще начать все с самого начала. Явный признак необходи- мости переписать код - это его неработоспособность. Это обнаружива- ется при его тестировании, когда ошибок так много, что сделать код ус- тойчивым не удается. Другой случай, когда следует воздержаться от ре- факторинга, - это близость даты завершения проекта. Однако прибли- 318
жение срока окончания работ — единственный случай, когда можно от- ложить рефакторинг, ссылаясь на недостаток времени. Каталог методов рефакторинга и примеры рефакторинга. Большой каталог методов рефакторинга можно найти на refactoring.com. Вместо примеров кода тут применяются примеры на языке UML. Также хоро- ший рефакторинг можно найти в Wikipcadia (каждый метод снабжен примером на С#). Применение методов рефакторинга требует хорошего знания ООП, умения писать тесты, быть терпеливым, делая рефакто- ринг небольшими шагами, проверять каждый шаг и постоянно учиться. Автор статьи [6] дает несколько советов для проведения рефакторин- га. 1. Всегда выполняйте рефакторинг короткими шагами с перерывом на перекомпиляцию и запуском тестов. Чем меньше ваши шаги, тем лучше вы локализуете потенциальные ошибки и тем быстрее вы их уст- раните. То, что ошибки будут, можно не сомневаться. 2. Проводите рефакторинг снизу вверх, особенно если у вас длинная и запутанная цепочка наследования классов. Старайтесь всегда сначала производить изменения в потомственных классах, прежде чем присту- пать к базовым классам. 3. Вы должны знать основные методы рефакторинга. Для этого сове- тую вам купить книгу М. Фаулера «Рефакторинг». 4. У вас всегда должна быть цель, с которой вы производите рефак- торинг. Не делайте его там, где он не нужен, или в том случае, если это не первоочередная задача. Чем лучше вы понимаете, зачем вы это де- лаете, тем качественнее будет результат. 5. Не увлекайтесь рефакторингом: рефакторинг - не панацея. Рефак- торинг не добавляет функционала в программу, поэтому и каких-либо видимых результатов вы также не получите. Делайте перерывы для то- го, чтобы сделать что-то, что можно увидеть и оценить. Тогда ваш руко- водитель будет вами доволен. 6. Любой вид рефакторинга можно сделать за 5-20 минут, максимум за один час. Но в основном рефакторинг является комплексной задачей, которая может выполняться в течение недель или месяцев работы над действующим проектом. Идея в том, что двигаться нужно постепенно и небольшими шагами, возможно, уделяя этому не больше одного часа в день. Это хороший метод рефакторинга, потому как не занимает мно- го времени, и убедить начальство в необходимости будет намного про- ще. 7. В долгосрочной перспективе у вас будет красивый и легко сопро- вождаемый код. Код, который написан для людей, а не для машин. Код, которым вы можете гордиться и ставить в пример. Код, который работа- ет, так как вы этого хотите, и все это за невысокую цену рефакторинга. 319
7.2. Рефакторинг, проектирование и производительность Программ В своей монографии [33] М. Фаулер отмечает, что рефакторинг игра- ет особую роль в качестве дополнения к проектированию. Большинство начинающих программистов (в том числе и автор этой монографии в свое время) пишут программу и доводят ее до конца. Со временем становится ясно, что если заранее подумать об архитектуре программы, то можно избежать последующей дорогостоящей переработки. Поэтому программисты привыкают к этому стилю предварительного проектиро- вания. Многие считают, что проектирование важнее всего, а программи- рование представляет собой механический процесс. Но программа весьма отличается от физического устройства. Это бо- лее гибкий объект. Программа значительно более податлива и целиком связана с обдумыванием. Существует утверждение, что рефакторинг может быть альтернативой предварительному проектированию. В край- нем сценарии проектирование вообще отсутствует. Первое решение, пришедшее в голову, воплощается в коде, доводится до рабочего со- стояния, а потом обретает требуемую форму с помощью рефакторинга. Такой подход фактически может действовать. Действительно, есть про- граммисты, которые так работают и получают в итоге систему с очень хорошей архитектурой. Тех, кто поддерживает экстремальное программирование, часто изо- бражают пропагандистами такого подхода. Подход, ограничивающийся только рефакторингом, применим, но не является самым эффективным. Даже «экстремальные» программисты сначала разрабатывают некую ар- хитектуру будущей системы. Они пробуют разные идеи с помощью CRC-карт или чего-либо подобного [7], пока не получат внушающего доверия первоначального решения. Только после этого приступают к кодированию, а затем к рефакторингу. Смысл в том, что при использо- вании рефакторинга изменяется роль предварительного проектирова- ния. Если не рассчитывать на рефакторинг, то ощущается необходи- мость как можно лучше провести предварительное проектирование. Возникает чувство, что любые изменения проекта в будущем, если они потребуются, окажутся слишком дорогостоящими. Поэтому в пред- варительное проектирование вкладывается больше времени и усилий — во избежание таких изменений впоследствии. С применением рефакто- ринга акценты смещаются. Предварительное проектирование сохраня- ется, но теперь оно не имеет целью найти единственно правильное ре- шение. Все, что от него требуется, - это найти приемлемое решение. По мере реализации решения, с углублением понимания задачи становится ясно, что наилучшее решение отличается от того, которое было принято первоначально. Но в этом нет ничего страшного, если в процессе участ- вует рефакторинг, потому что модификация не обходится слишком до- рого. Важным следствием такого смещения акцентов является большее стремление к простоте проекта. До введения рефакторинга в свою рабо- 320
ту, как отмечает автор монографии [33], он всегда искал гибкие реше- ния. Для каждого технического требования он рассматривал возможно- сти его изменения в течение срока жизни системы. Поскольку измене- ния в проекте были дорогостоящими, он старался создать проект, спо- собный выдержать изменения, которые можно было предвидеть. Недос- таток гибких решений в том, что за гибкость приходится платить. Гиб- кие решения сложнее обычных. Создаваемые по ним программы в це- лом труднее сопровождать, хотя их легче перерабатывать в том направ- лении, которое предполагалось изначально. И даже простые решения не избавляют от необходимости разбирать- ся, как модифицировать проект. Для одной-двух функций это сделать не очень трудно, но изменения происходят по всей системе. Если преду- сматривать гибкость во всех этих местах, то вся система становится зна- чительно сложнее и дороже в сопровождении. Весьма разочаровывает, конечно, то, что вся эта гибкость и не нужна. Потребуется лишь какая- то часть ее, но невозможно заранее сказать какая. Чтобы достичь гибко- сти, приходится вводить ее гораздо больше, чем требуется в действи- тельности. Рефакторинг предоставляет другой подход к рискам модификации. Возможные изменения все равно надо пытаться предвидеть, как и рас- сматривать гибкие решения. Но вместо реализации этих гибких реше- ний следует задаться вопросом: «Насколько сложно будет с помощью рефакторинга преобразовать обычное решение в гибкое?» Если, как ча- ще всего случается, ответ будет «Весьма несложно», то надо просто реа- лизовать обычное решение. Рефакторинг позволяет создавать более про- стые проекты, не жертвуя гибкостью, благодаря чему процесс проекти- рования становится более легким и менее напряженным. С рефакторингом обычно связан вопрос о его влиянии на производи- тельность программы. С целью облегчения понимания работы програм- мы часто осуществляется модификация, приводящая к замедлению вы- полнения программы. Это важный момент. Программное обеспечение часто отвергалось как слишком медленное, а более быстрые машины ус- танавливают свои правила игры. Рефакторинг, несомненно, заставляет программу выполняться медленнее, но при этом делает ее более подат- ливой для настройки производительности. Секрет создания быстрых программ, если только они не предназна- чены для работы в жестком режиме реального времени, состоит в том, чтобы сначала написать программу, которую можно настраивать, а за- тем настроить ее так, чтобы достичь приемлемой скорости. М. Фаулер отмечает три подхода к написанию быстрых программ. Наиболее труд- ный из них связан с ресурсами времени и часто применяется в системах, работающих в режиме реального времени. В этой ситуации при деком- позиции проекта каждому компоненту выделяется бюджет ресурсов (по времени и памяти). Компонент не должен выйти за рамки своего бюджета, хотя разре- шен механизм обмена временными ресурсами. Такой механизм жестко сосредоточен на соблюдении времени выполнения. Это важно в таких 321
системах, как, например, кардиостимуляторы, в которых данные, полу- ченные с опозданием, всегда ошибочны. Данная технология избыточна в системах другого типа, например в корпоративных информационных системах. Второй подход предполагает постоянное внимание. В этом случае каждый программист в любой момент времени делает все от него зави- сящее, чтобы поддерживать высокую производительность программы. Это распространенный и интуитивно привлекательный подход, однако он не так хорош на деле. Модификация, повышающая производитель- ность, обычно затрудняет работу с программой. Это замедляет создание программы. На это можно было бы пойти, если бы в результате получа- лось более быстрое программное обеспечение, но обычно этого не про- исходит. С производительностью связано то интересное обстоятельство, что при анализе большинства программ обнаруживается, что большая часть времени расходуется небольшой частью кода (согласно закону Парето). Если в равной мере оптимизировать весь код, то окажется, что 90% оп- тимизации произведено впустую, потому что оптимизировался код, ко- торый выполняется не слишком часто. Время, ушедшее на ускорение программы, и время, потерянное из-за ее непонятности, - все это израс- ходовано напрасно [36]. Третий подход к повышению производительности программы осно- ван как раз на этой статистике. Он предполагает создание программы с достаточным разложением ее на компоненты без оглядки на достигае- мую производительность вплоть до этапа оптимизации производитель- ности, который обычно наступает на довольно поздней стадии разработ- ки и на котором осуществляется особая процедура настройки програм- мы. Начинается все с запуска программы под профайлером, контроли- рующим программу и сообщающим, где расходуются время и память. Благодаря этому можно обнаружить тот небольшой участок про- граммы, в котором находятся узкие места производительности. На этих узких местах сосредотачиваются усилия и осуществляется та же самая оптимизация, которая была бы применена при подходе с постоянным вниманием. Но благодаря тому, что внимание сосредоточено на выяв- ленных узких местах, удается достичь больших результатов при значи- тельно меньших затратах труда. Процесс поиска и ликвидации узких мест продолжается до достижения производительности, которая удовле- творяет пользователей. Хорошее разделение программы на компоненты способствует опти- мизации такого рода в двух отношениях. Во-первых, благодаря ему по- является время, которое можно потратить на оптимизацию. Имея хоро- шо структурированный код, можно быстрее добавлять новые функции и выиграть время для того, чтобы заняться производительностью. (Про- филирование гарантирует, что это время не будет потрачено зря.) Во- вторых, хорошо структурированная программа обеспечивает более вы- сокое разрешение для анализа производительности. Профайлер указы- вает на более мелкие фрагменты кода, которые легче настроить. 322
Благодаря большей понятности кода легче осуществить выбор воз- можных вариантов и разобраться в том, какого рода настройка может оказаться действенной. Таким образом, можно сделать вывод, что ре- факторинг позволяет писать программы быстрее. На некоторое время он делает программы более медленными, но облегчает настройку про- грамм на этапе оптимизации. В конечном счете, достигается большой выигрыш. 7.3. Когда применять рефакторинг1 Дублирование кода. Если в программе есть одинаковые кодовые структуры в нескольких местах, при их объединении программа только выиграет. Простейшая задача с дублированием кода возникает, когда одно и то же выражение присутствует в двух методах одного и того же класса. В этом случае надо лишь применить выделение метода (extract method) и вызывать код созданного метода из обеих точек. Другая задача с дублированием встречается, когда одно и то же вы- ражение есть в двух подклассах, находящихся на одном уровне. Устра- нить такое дублирование можно с помощью выделения метода для обо- их классов с последующим подъемом поля (pull up field). Если код по- хож, но не совпадает полностью, нужно применить выделение метода для отделения совпадающих фрагментов от различающихся. После это- го может оказаться возможным применить формирование шаблона ме- тода (form template method). Если оба метода делают одно и то же с помощью разных алгорит- мов, нужно выбрать более четкий из этих алгоритмов и применить заме- щение алгоритма (substitute algorithm). Если дублирующийся код нахо- дится в двух разных классах, можно применить выделение класса (extract class) в одном классе, а затем использовать новый компонент в другом. Бывает, что в действительности метод должен принадлежать только одному из классов и вызываться из другого класса, либо метод должен принадлежать третьему классу, на который будут ссылаться оба первоначальных. Необходимо решить, где оправдано присутствие этого метода, и обеспечить, чтобы он находился там и нигде более. Длинный метод. Программы, использующие объекты, работают на- дежно, когда методы этих объектов короткие. Программистам, не имею- щим опыта работы с объектами, часто кажется, что никаких вычислений не происходит, а программы состоят из нескончаемой цепочки делеги- рования действий. Однако, общаясь с такой программой на протяжении нескольких лет, становится ясным, какую ценность представляют собой маленькие методы. Уже на заре программирования стало ясно, что чем длиннее процедура, тем труднее понять, как она работает. В старых язы- ках программирования вызов процедур был связан с накладными расхо- дами, которые удерживали от применения маленьких методов. Совре- 1 Материал этого раздела в основном базируется на монографии М. Фаулера [33]. 323
менные объектно ориентированные языки в значительной мере устрани- ли издержки вызовов внутри процесса. Однако издержки сохраняются для того, кто читает код, поскольку приходится переключать контекст, чтобы увидеть, чем занимается про- цедура. Среда разработки, позволяющая видеть одновременно два мето- да, помогает устранить этот шаг, но главное, что способствует понима- нию работы маленьких методов, — это толковое присвоение им имен. Если правильно выбрать имя метода, нет необходимости изучать его те- ло. В итоге можно заключить, что следует активнее применять декомпо- зицию методов. Программисты придерживаются эвристического прави- ла, гласящего, что если ощущается необходимость что-то прокомменти- ровать, надо написать метод. В таком методе содержится код, который требовал комментариев, но его название отражает назначение кода, а не то, как он решает свою за- дачу. Такая процедура может применяться к группе строк или всего лишь к одной строке кода. К ней прибегают даже тогда, когда обраще- ние к коду длиннее, чем код, который им замещается, при условии, что имя метода разъясняет назначение кода. Главным здесь является не дли- на метода, а семантическое расстояние между тем, что делает метод, и тем, как он это делает. В 99% случаев, чтобы укоротить метод, требуется лишь выделение метода. Нужно найти те части метода, которые кажутся согласованными друг с другом, и образовать новый метод. Когда в методе есть масса па- раметров и временных переменных, это мешает выделению нового ме- тода. При попытке выделения метода в итоге приходится передавать столько параметров и временных переменных в качестве параметров, что результат оказывается ничуть не проще для чтения, чем оригинал. Устранить временные переменные можно с помощью замены вре- менной переменной вызовом метода (replace temp with query). Длинные списки параметров можно сократить с помощью приемов введения гра- ничного объекта (introduce parametr object) и сохранения всего объекта (preserve whole object). Если даже после этого остается слишком много временных переменных и параметров, приходится выдвигать тяжелую артиллерию — необходима замена метода объектом метода (replace method with method object). Как определить те участки кода, которые должны быть выделены в отдельные методы? Хороший способ - поискать комментарии: они часто указывают на такого рода семантическое расстояние. Блок кода с комментариями говорит о том, что его действие можно заменить мето- дом, имя которого основывается на комментарии. Даже одну строку имеет смысл выделить в метод, если она нуждается в разъяснениях. Условные операторы и циклы тоже свидетельствуют о возможности выделения. Для работы с условными выражениями подходит декомпо- зиция условных операторов (decompose conditional). Если это цикл, сле- дует выделить его и содержащийся в нем код в отдельный метод. Большой класс. Когда класс пытается выполнять слишком много ра- боты, это часто проявляется в чрезмерном количестве имеющихся у не- 324
го атрибутов. А это может привести и к дублированию кода. Можно применить выделение класса, чтобы связать некоторое количество атри- бутов. Нужно так выбирать для компонента атрибуты, чтобы они имели смысл для каждого из них. Обычно одинаковые префиксы или суффик- сы у некоторого подмножества переменных в классе наводят на мысль о создании компонента. Если разумно создание компонента как под- класса, то более простым оказывается выделение подкласса (extract subclass). Иногда класс не использует постоянно все свои переменные экземп- ляра. В таком случае оказывается возможным применить выделение класса или выделение подкласса несколько раз. Как и класс с чрезмерным количеством атрибутов, класс, содержа- щий слишком много кода, создаст хорошую среду для повторяющегося кода, хаоса и гибели. Простейшее решение — устранить избыточность в самом классе. Пять методов по сотне строк в длину иногда можно за- менить пятью методами по десять строк плюс еще десять двухстрочных методов, выделенных из оригинала. Как и для класса с большим числом атрибутов, обычное решение для класса с чрезмерным объемом кода состоит в том, чтобы применить вы- деление класса или выделение подкласса. Полезно установить, как кли- енты используют класс, и применить выделение интерфейса (extract interface) для каждого из этих вариантов. В результате может выяснить- ся, как расчленить класс еще далее. Если большой класс является клас- сом GUI, может потребоваться переместить его данные и поведение в отдельный объект предметной области. При этом может оказаться не- обходимым хранить копии некоторых данных в двух местах и обеспе- чить их согласованность. Дублирование видимых данных (duplicate observed data) предлагает путь, которым можно это осуществить. В дан- ном случае, особенно при использовании старых компонентов Abstract Windows Toolkit (AWT), можно в последующем удалить класс GUI и за- менить его компонентами Swing. Длинный список параметров. Когда-то при обучении программиро- ванию рекомендовали все необходимые подпрограмме данные переда- вать в виде параметров. Это можно было понять, потому что альтерна- тивой были глобальные переменные, а глобальные переменные часто пагубны и мучительны. Благодаря объектам ситуация изменилась, так как если какие-то данные отсутствуют, всегда можно попросить их у другого объекта. Поэтому, работая с объектами, следует передавать не все, что требуется методу, а столько, чтобы метод мог добраться до всех необходимых ему данных. Значительная часть того, что необходимо ме- тоду, есть в классе, которому он принадлежит. В объектно ориентиро- ванных программах списки параметров обычно гораздо короче, чем в традиционных программах. И это хорошо, потому что в длинных списках параметров трудно разбираться, они становятся противоречивыми и сложными в использо- вании, а также потому, что их приходится изменять по мере того, как возникает необходимость в новых данных. Если передавать объекты, то 325
изменений требуется мало, потому что для получения новых данных, скорее всего, хватит пары запросов, замена параметра вызовом метода (replace parameter with method) уместна, когда можно получить данные в одном параметре путем вызова метода объекта, который уже известен. Этот объект может быть полем или другим параметром. Сохранение все- го объекта (preserve whole object) позволяет взять группу данных, полу- ченных от объекта, и заменить их самим объектом. Если есть несколько элементов данных без логического объекта, вы- берите введение граничного объекта (introduce parameter object). Есть важное исключение, когда такие изменения не нужны. Оно касается си- туации, когда разработчик определенно не хочет создавать зависимость между вызываемым и более крупным объектами. В таких случаях ра- зумно распаковать данные и передать их как параметры, но необходимо учесть, каких трудов это стоит. Если список параметров оказывается слишком длинным или модификации слишком частыми, следует пере- смотреть структуру зависимостей. Расходящиеся модификации. Программы структурируются, чтобы облегчить их модификацию. Программист хочет, чтобы при модифика- ции можно было найти в системе одно определенное место и внести из- менения именно туда. Если этого сделать не удается, то тут могут поя- виться две тесно связанные проблемы. Расходящиеся (divergent) моди- фикации имеют место тогда, когда один класс часто модифицируется различными способами по разным причинам. Если, глядя на класс, раз- работчик отмечает для себя, что эти три метода придется модифициро- вать для каждой новой базы данных, а эти четыре метода придется мо- дифицировать при каждом появлении нового финансового инструмента, это может означать, что вместо одного класса лучше иметь два. Благодаря этому каждый класс будет иметь свою четкую зону ответ- ственности и изменяться в соответствии с изменениями в этой зоне. Не исключено, что это обнаружится лишь после добавления нескольких баз данных или финансовых инструментов. При каждой модификации, вы- званной новыми условиями, должен изменяться один класс, и вся типи- зация в новом классе должна выражать эти условия. Для того чтобы все это привести в порядок, определяется все, что изменяется по данной причине, а затем применяется выделение класса, чтобы объединить это все вместе. Множественные изменения. Эту ситуацию М. Фаулер назвал «стрельба дробью» [33]. Она похожа на расходящуюся модификацию, но является ее противоположностью. Увидеть ее можно, когда при вы- полнении любых модификаций приходится вносить множество мелких изменений в большое число классов. Если изменения разбросаны по- всюду, их трудно находить и можно пропустить важное изменение. В такой ситуации следует использовать перемещение метода (move method) и перемещение поля (move field), чтобы свести все изменения в один класс. Если среди имеющихся классов подходящего кандидата нет, нужно создать новый класс. Часто можно воспользоваться встраи- ванием класса (inline class), чтобы поместить целую связку методов 326
в один класс. Возникнет какое-то число расходящихся модификаций, но с этим можно справиться. Завистливые функции. Под такими функциями М. Фаулер понимает методы, которые больше интересуются не теми классами, в которых они находятся, а какими-то другими. Чаще всего предметом зависти являют- ся данные. Есть масса случаев, когда в программе мы сталкиваемся с методами, вызывающими полдюжины методов доступа к данным дру- гого объекта. Изменение здесь очевидно: метод явно напрашивается на перевод в другое место, что и достигается перемещением метода. Ино- гда завистью страдает только часть метода; в таком случае к завистли- вому фрагменту применяется выделение метода. Конечно, встречаются нестандартные ситуации. Иногда метод ис- пользует функции нескольких классов, так в который из них его лучше поместить? Нужно определить, в каком классе находится больше всего данных, и поместить метод вместе с этими данными. Иногда легче с по- мощью выделения метода разбить метод на несколько частей и помес- тить их в разные места. Есть несколько сложных схем, нарушающих это правило. Фундаментальное практическое правило гласит: то, что изме- няется одновременно, надо хранить в одном месте. Данные и функции, использующие эти данные, обычно изменяются вместе, но бывают и ис- ключения. Наталкиваясь на такие исключения, мы перемещаем функ- ции, чтобы изменения осуществлялись в одном месте. Группы данных. Часто в программе одни и те же три-четыре элемен- та данных попадаются в множестве мест: поля в паре классов, парамет- ры в нескольких сигнатурах методов. Связки данных, встречающихся совместно, надо превращать в самостоятельный класс. Сначала следует найти, где эти группы данных встречаются в качестве полей. Применяя к полям выделение метода, нужно преобразовать эти группы данных в класс. Затем обратить внимание на сигнатуры методов и применить введение граничного объекта или сохранение всего объекта, чтобы со- кратить их объем. В результате сразу удастся укоротить многие списки параметров и упростить вызов методов. Не стоит беспокоиться, что некоторые группы данных используют лишь часть полей нового объекта. Для проверки можно удалить одно из значений данных и посмотреть, сохранят ли при этом смысл остальные. Если нет, это верный признак того, что данные напрашиваются на объе- динение их в объект. Сокращение списков полей и параметров, несо- мненно, улучшает код. После создания классов можно поискать завист- ливые функции и обнаружить методы, которые желательно переместить в образованные классы. Одержимость элементарными типами. В большинстве программ- ных сред есть два типа данных. Тип «запись» позволяет структуриро- вать данные в значимые группы. Элементарные типы данных служат стандартными конструктивными элементами. С записями всегда связа- ны некоторые накладные расходы. Они могут представлять таблицы в базах данных, но их создание может оказаться неудобным, если они нужны лишь в одном-двух случаях. 327
Один из ценных аспектов использования объектов заключается в том, что они затушевывают или вообще стирают границу между при- митивными и большими классами. Нетрудно написать маленькие клас- сы, неотличимые от встроенных типов языка. В Java есть примитивы для чисел, но строки и даты, являющиеся примитивами во многих дру- гих средах, суть классы. Те, кто занимается ООП недавно, обычно неохотно используют ма- ленькие объекты для маленьких задач. В качестве примера можно при- вести, например, денежные классы, соединяющие численное значение и валюту, специальные строки типа телефонных номеров и почтовых индексов. Выйти в мир объектов помогает рефакторинг замена значения данных объектом (replace data value with object) для отдельных значе- ний данных. Когда значение данного является кодом типа, можно обра- титься к рефакторингу замена кода типа классом (replace type code with class), если значение не воздействует на поведение. Если есть условные операторы, зависящие от кода типа, может по- дойти замена кода типа подклассами или замена кода типа соспюяни- ем/стратегиеи (replace type code with state/strategy). При наличии группы полей, которые должны находиться вместе, можно применить выделение класса. Увидев примитивы в списках пара- метров, можно воспользоваться введением граничного объекта. Если обнаружится разборка на части массива, можно попробовать замену массива объектом (replace array with object). Операторы типа switch. Одним из признаков объектно ориентиро- ванного кода служит сравнительная немногочисленность операторов ти- па switch (или case). Проблема, обусловленная применением switch, по существу, связана с дублированием. Часто один и тот же блок switch оказывается разбросанным по разным местам программы. При добавле- нии в переключатель нового варианта приходится искать все эти блоки switch и модифицировать их. Понятие полиморфизма в ООП предостав- ляет элегантный способ справиться с этой проблемой. Как правило, заметив блок switch, следует подумать о полиморфиз- ме. Задача состоит в том, чтобы определить, где должен происходить полиморфизм. Часто переключатель работает в зависимости от кода ти- па. Необходим метод или класс, хранящий значение кода типа. Поэтому нужно воспользоваться выделением метода для выделения переключа- теля, а затем перемещением метода для вставки его в тот класс, где тре- буется полиморфизм. В этот момент следует решить, чем воспользо- ваться — заменой кода типа подклассами или заменой кода типа состоя- нием/стратегией. Определив структуру наследования, можно применить замену условного оператора полиморфизмом (replace conditional with polymorphism). Если есть лишь несколько вариантов переключателя, управляющих одним методом, и не предполагается их изменение, то применение по- лиморфизма оказывается чрезмерным. В данном случае хорошим выбо- ром будет замена параметра явными методами (replace parameter with 328
explicit method). Если одним из вариантов является null, можно попробо- вать прибегнуть к введению объекта Null (introduce null object). Параллельные иерархии наследования. Параллельные иерархии на- следования в действительности являются особым случаем «стрельбы дробью». В данном случае всякий раз при порождении подкласса одно- го из классов приходится создавать подкласс другого класса. Признаком этого служит совпадение префиксов имен классов в двух иерархиях классов. Общая стратегия устранения дублирования состоит в том, что- бы заставить экземпляры одной иерархии ссылаться на экземпляры дру- гой. С помощью перемещения метода и перемещения поля иерархия в ссылающемся классе исчезает. Ленивый класс. Чтобы сопровождать каждый создаваемый класс и разобраться в нем, требуются определенные затраты. Класс, существо- вание которого не окупается выполняемыми им функциями, должен быть ликвидирован. Часто это класс, создание которого было оправдан- но в свое время, но уменьшившийся в результате рефакторинга. Либо это класс, добавленный для планировавшейся модификации, которая не была осуществлена. В любом случае следует дать классу возможность умереть. При наличии подклассов с недостаточными функциями можно попробовать свертывание иерархии (collapse hierarchy). Почти бесполез- ные компоненты должны быть подвергнуты встраиванию класса. Теоретическая общность. Эффект такого кода возникает, когда го- ворят о том, что в будущем, наверное, потребуется возможность делать такие вещи, и хотят обеспечить набор механизмов для работы с вещами, которые не нужны. То, что получается в результате, труднее понимать и сопровождать. Если бы все эти механизмы использовались, их нали- чие было бы оправданно, в противном случае они только мешают, по- этому лучше от них избавиться. Если есть абстрактные классы, не приносящие большой пользы, от нужно них избавиться путем сворачивания иерархии. Ненужное делеги- рование можно устранить с помощью встраивания класса. Методы с не- используемыми параметрами должны быть подвергнуты удалению пара- метров (remove parameter). Методы со странными абстрактными имена- ми необходимо переименовать путем переименования метода (rename method). Теоретическая общность может быть обнаружена, когда единствен- ными пользователями метода или класса являются контрольные приме- ры. Найдя такой метод или класс, нужно удалить его и контрольный пример, его проверяющий. Если есть вспомогательный метод или класс для контрольного примера, осуществляющий разумные функции, его, конечно, надо оставить. Временное поле. Иногда обнаруживается, что в некотором объекте атрибут устанавливается только при определенных обстоятельствах. Та- кой код труден для понимания, поскольку естественно ожидать, что объекту нужны все его переменные. Трудно понять, для чего существу- ет некоторая переменная, когда не удается найти, где она используется. 329
С помощью выделения класса можно создать класс и поместить туда весь код, работающий с этими переменными. Возможно, удастся уда- лить условно выполняемый код с помощью введения объекта Null для создания альтернативного компонента в случае недопустимости пере- менных. Часто временные поля возникают, когда сложному алгоритму требу- ется несколько переменных. Тот, кто реализовывал алгоритм, не хотел пересылать большой список параметров, поэтому он разместил их в по- лях. Но поля действенны только во время работы алгоритма, а в другом контексте лишь вводят в заблуждение. В таком случае можно приме- нить выделение класса к переменным и методам, в которых они требу- ются. Новый объект является объектом метода. Цепочки сообщений. Цепочки сообщений появляются, когда клиент запрашивает у одного объекта другой, у которого клиент запрашивает еще один объект, у которого клиент запрашивает еще один объект, и т.д. Это может выглядеть как длинный ряд методов getThis или последова- тельность временных переменных. Такие последовательности вызовов означают, что клиент связан с навигацией по структуре классов. Любые изменения промежуточных связей означают необходимость модифика- ции клиента. Здесь применяется прием сокрытия делегирования (hide delegate). Это может быть сделано в различных местах цепочки. В принципе, можно делать это с каждым объектом цепочки, что часто превращает каждый промежуточный объект в посредника. Обычно лучше посмот- реть, для чего используется конечный объект. Можно попробовать с по- мощью выделения метода взять использующий его фрагмент кода и пу- тем перемещения метода передвинуть его вниз по цепочке. Если не- сколько клиентов одного из объектов цепочки желают пройти осталь- ную часть пути, добавьте метод, позволяющий это сделать. Посредник. Одной из главных характеристик объектов является ин- капсуляция — сокрытие внутренних деталей от внешнего мира. Инкапсу- ляции часто сопутствует делегирование. Однако это может завести слишком далеко. Например, рассмотрев интерфейс класса, обнаружива- ем, что половина методов делегирует обработку другому классу. Тут на- до воспользоваться удалением посредника (remove middle man) и об- щаться с объектом, который действительно знает, что происходит. При наличии нескольких методов, не выполняющих большой работы, с по- мощью встраивания метода следует поместить их в вызывающий метод. Если есть дополнительное поведение, то с помощью замены делегирова- ния наследованием (replace delegation with inheritance) можно преобразо- вать посредника в подкласс реального класса. Это позволит расширить поведение, не гонясь за всем этим делегированием. Неуместная близость. Иногда классы оказываются в слишком близ- ких отношениях и чаще, чем следовало бы, погружены в закрытые части друг друга. Классы должны следовать строгим правилам. Чрезмерно взаимосвязанные классы нужно разводить с помощью перемещения ме- 330
тода и перемещения поля, разделить части и уменьшить близость. Сле- дует посмотреть, нельзя ли прибегнуть к замене двунаправленной связи однонаправленной (change bidirectional association to unidirectional). Если у классов есть общие интересы, можно воспользоваться выделением класса, чтобы поместить общую часть в надежное место и превратить их в добропорядочные классы. Либо можно воспользоваться сокрытием делегирования, позволив выступить в качестве связующего звена друго- му классу. К чрезмерной близости может приводить наследование. Подклассы всегда знают о своих родителях больше, чем последним хотелось бы. Если пришло время расстаться с домом, примените замену наследования делегированием (replace inheritance with delegation). Альтернативные классы с разными интерфейсами. В этих случаях можно использовать переименование метода ко всем методам, выпол- няющим одинаковые действия, но различающимся сигнатурами. Часто этого оказывается недостаточно. В таких случаях классы еще недоста- точно деятельны. Нужно продолжить применять перемещение метода для передачи поведения в классы, пока протоколы не станут одинаковы- ми. Если для этого приходится осуществить избыточное перемещение кода, можно попробовать компенсировать это выделением родительско- го класса (extract superclass). Неполнота библиотечного класса. Повторное использование кода часто рекламируется как цель применения объектов. Значение этого ас- пекта переоценивается (достаточно простого использования). Не следу- ет отрицать, однако, что программирование во многом основывается на применении библиотечных классов. Разработчики библиотечных клас- сов не всеведущи, и их не следует осуждать за это. Проблема в том, что часто считается дурным тоном и обычно оказывается невозможным мо- дифицировать библиотечный класс, чтобы он выполнял какие-то жела- тельные действия. Это означает, что испытанная тактика вроде переме- щения метода оказывается бесполезной. Для этой работы есть пара специализированных инструментов. Если в библиотечный класс надо включить всего лишь один-два новых мето- да, можно выбрать введение внешнего метода (introduce foreign method). Если дополнительных функций достаточно много, необходимо приме- нить введение локального расширения (introduce local extension). Классы данных. Такие классы содержат поля, методы для получения и установки значений этих полей и ничего больше. Это бессловесные хранилища данных, которыми другие классы наверняка манипулируют излишне обстоятельно. На ранних этапах в этих классах могут быть от- крытые поля, и тогда необходимо немедленно, пока никто их не обнару- жил, применить инкапсуляцию поля (incapsulate field). При наличии по- лей коллекций нужно проверить, инкапсулированы ли они должным об- разом, и если нет, применить инкапсуляцию коллекции (incapsulate collection). Применить удаление метода установки значения (remove setting method) ко всем полям, значение которых не должно изменяться. 331
Посмотреть, как эти методы доступа к полям используются другими классами. Далее попробовать с помощью перемещения метода перемес- тить методы доступа в класс данных. Если метод не удается перемес- тить целиком, обратиться выделению метода, чтобы создать такой ме- тод, который можно переместить. Через некоторое время можно начать применять сокрытие метода к методам получения и установки значений полей. Отказ от наследования. Подклассам полагается наследовать методы и данные своих родителей. Но как быть, если наследование им не требу- ется? Обычная причина этого - неправильно задуманная иерархия. Не- обходимо создать новый класс на одном уровне с потомком и с помо- щью спуска метода (push down method) и спуска поля (push down field) вытолкнуть в него все бездействующие методы. Благодаря этому в ро- дительском классе будет содержаться только то, что используется со- вместно. Часто встречается совет делать все родительские классы абстрактны- ми. М. Фаулер этого не советует и замечает, что, по крайней мере, это годится не на все случаи жизни. С другой стороны, он считает нормаль- ным стилем работы создание подклассов для повторного использования части функций. Также он отмечает, что если с непринятым наследством связаны какие-то проблемы, то стоит следовать обычному совету. Одна- ко не следует думать, что это надо делать всегда. Если подкласс повтор- но использует функции родительского класса, но не желает поддержи- вать его интерфейс, то нет возражений против отказа от реализаций, но от интерфейса отказываться не следует. В этом случае не надо возиться с иерархией, ее надо разрушить с помощью замены наследования деле- гированием. Комментарии. Просто удивительно, как часто встречается код с обильными комментариями, которые появились в нем лишь потому, что код плохой. По мнению М. Фаулера, после рефакторинга коммента- рии часто оказываются ненужными. Если для объяснения действий бло- ка требуется комментарий, он рекомендует применить выделение мето- да. Если метод уже выделен, но по-прежнему нужен комментарий для объяснения его действия, стоит воспользоваться переименованием ме- тода. А если требуется изложить некоторые правила, касающиеся необ- ходимого состояния системы, лучше применить введение утверждения (introduce assertion). Почувствовав потребность написать комментарий, попробуйте сна- чала изменить структуру кода так, чтобы любые комментарии стали из- лишними. Комментарии полезны, когда программист не знает, как по- ступить. Помимо описания происходящего, комментарии могут отме- чать те места, в которых программист не уверен. Правильным будет по- местить в комментарии обоснование своих действий. Это пригодится тем, кто будет модифицировать код в будущем. 332
7.4. Уровни рефакторинга По определению, рефакторинг - процесс изменения внутренней структуры программы, не затрагивающий ее внешнего поведения и имеющий целью облегчить понимание ее работы, В основе рефакто- ринга лежит последовательность небольших эквивалентных (т.е. сохра- няющих поведение) преобразований. Иногда понятие рефакторинга за- ключается в определении регламентированного способа реструктуриза- ции кода с применением небольших итерационных шагов. Прежде всего рефакторинг обеспечивает постепенное развитие кода во времени, в ре- зультате чего реализуется эволюционный подход к программированию. Особенностью рефакторинга является то, что он сохраняет функцио- нальную семантику базового кода. Многие разработчики, организую- щие свою работу на принципах адаптивного программирования, и в ча- стности, как отмечено выше, специалисты по экстремальному програм- мированию, полагают, что рефакторинг является ведущим подходом к разработке. На практике можно столь же часто встретить примеры применения рефакторинга небольших фрагментов кода, как и введения операторов if или циклов [36]. Первым уровнем рефакторинга можно считать такое изменение кода, которое не затрагивает структуру классов (количество и взаимосвязи, интерфейсы) объектно ориентированной программной системы, т.е. ре- факторингу подвергается программный код внутри классов. Это могут быть методы и алгоритмы, реализуемые методами, поля и т.п. Многие разработчики программного обеспечения считают, что при рефакторин- ге лучше полагаться на интуицию, основанную на опыте, но можно вы- делить наиболее очевидные причины, когда код нужно подвергнуть процессу рефакторинга. Сюда относятся дублирование фрагментов ко- да, длинный метод реализации, использование достаточно длинного списка параметров, применение и использование избыточных времен- ных переменных, изменение сигнатуры метода, инкапсуляция поля, за- мена условного оператора полиморфизмом и т.п. Второй уровень рефакторинга относится к изменению структуры классов программной системы, добавлению новых классов, выделению и разбиению больших классов, встраиванию классов, переносу или до- бавлению новых методов, выделению интерфейсов, сокрытию делеги- рования и др. Основными стимулами его проведения являются следую- щие задачи: • необходимо добавить новую функцию, которая недостаточно ук- ладывается в принятое архитектурное решение программного модуля; • необходимо исправить ошибку, причины возникновения которой не выделены четко структурированной базовой внешней формой; • проблематика в командной разработке, которая обусловлена слож- ностью логики программного продукта. К третьему уровеню рефакторинга (большому рефакторингу) мож- но отнести: 333
• разделение наследования (tease apart inheritance) в ситуации, когда в программе запутанная иерархия наследования, в которой различные варианты представляются объединенными вместе так, что это сбивает с толку; • преобразование процедурного проекта в объекты (convert procedural design to object), что содействует решению классической про- блемы преобразования процедурного кода; • отделение предметной области от представления (separate domain from presentation), которое используется для разделения бизнес-логики и кода интерфейса пользователя (опытные объектно ориентированные разработчики поняли, что такое разделение жизненно важно для долго живущих систем); • выделение иерархии (extract hierarchy), упрощающей чрезмерно сложные классы путем превращения их в группы подклассов. Рефакторинг архитектуры программных систем является четвер- тым уровнем рефакторинга. Трудно представить сегодня серьезное коммерческое приложение, не взаимодействующее с базой данных, что подразумевает уровень архитектуры, обеспечивающий взаимодействие с БД. Рефакторинг этого уровня — довольно трудоемкий процесс. Операции рефакторинга архитектуры программной системы и БД концептуально являются более сложными, чем операции рефакторинга кода, поскольку при проведении операций рефакторинга кода необходи- мо заботиться лишь о сохранении функциональной семантики структу- ры фрагментации, а при осуществлении операций рефакторинга БД воз- никает процесс необходимости сохранения информационной семанти- ки. Достаточно важным фактором является то, что усложнение опера- ций рефакторинга БД может быть обусловлено наличием большого ко- личества связей, поддерживаемых архитектурой БД. Рефакторинг кода может быть связан не только с изначальной архитектурной проблемати- кой. Процесс необходимо осуществлять после комплексного анализа структуры и выявления «фрагментарных участков, необходимых для оптимизации со стороны команды разработчиков». В этом случае про- водится рефакторинг фрагментарного участка программы. Профилиров- ка поможет его определить. При осуществлении процесса рефакторинга необходимо четко пред- ставлять, какой из методов лучше и оптимальнее для данной структуры, ведь нелогичное использование методов приведет к трудностям работы программы. Опытные разработчики четко представляют и разделяют процесс оптимизации кода и процесс рефакторинга. При процессе ре- факторинга разработчик старается сделать так, чтобы код стал удобнее для понимания и поддержки, а при оптимизации кода приходится де- лать процедуры, которые приводят к обратному эффекту, что приводит к проблемам читаемости кода, но за счет этого возрастает скорость его выполнения. 334
7.5. Методы рефакторинга 7.5.1. Основные методы К наиболее часто употребляемым методам рефакторинга можно от- нести [17]: • изменение сигнатуры метода (change method signature); • инкапсуляцию поля (encapsulate field); • выделение класса (extract class); • выделение интерфейса (extract interface); • выделение локальной переменной (extract local variable); • выделение метода (extract method); • генерализацию типа (generalize type); • встраивание (inline); • введение фабрики (introduce factory); • введение параметра (introduce parameter); • подъем поля/метода (pull up); • спуск поля/метода (push down); • замену условного оператора полиморфизмом (replace conditional with polymorphism). Пожалуй, наилучшие рекомендации и практические советы по опи- санию и иллюстрации использования методов рефакторинга программ- ного кода в объектно ориентированных программах можно найти в мо- нографии М. Фаулера [33]. Рефакторинг кода должен осуществляться до полного исчерпания его возможностей, поскольку наибольшая производительность может быть достигнута только в условиях работы с исходным кодом макси- мально высокого качества. При возникновении необходимости добавить к коду новые возможности следует оценить качество реплицируемого кода в аспектах данного проекта, что позволит успешно реализовать требуемые средства. Если ответ на этот вопрос является положитель- ным, то можно приступать к добавлению новых функциональных средств. При отрицательном решении данного аспектного внедрения код вна- чале должен быть подвергнут рефакторингу, для того чтобы он имел оп- тимальный вариант, и только после этого возможно осуществление про- цесса добавления новых функций. Применение указанного подхода при- водит к значительному увеличению объема работы, но практика пока- зывает, что если доработка начинается с высококачественного исходно- го кода, после чего постоянно осуществляется рефакторинг этого кода для поддержки его в том же состоянии, то все новые замыслы реализу- ются чрезвычайно показательно и эффективно. Читателям, желающим получить более подробную информацию и рассмотреть конкретные примеры рефакторинга, рекомендуется обра- титься к литературе, перечень которой дан в конце главы. 335
7.5.2. Формализация процесса рефакторинга на основе символьной записи структуры классов В большом количестве опубликованных работ рассматриваются ис- пользование UML-диаграмм для решения задачи синтеза и декларатив- ные подходы к решению задачи анализа объектно ориентированных программных систем. Однако решение задачи анализа на основе UML не формализовано, а сами диаграммы дают небольшие возможности вы- полнять формальные преобразования над классами с целью рефакторин- га проекта. Исключением в определенной части являются инструмен- тальные средства IBM Rational. Один из возможных подходов к автома- тизации процессов рефакторинга предложен в работе [28]. Автор работы на основе ряда публикаций делает вывод, что класс - это триплет вида С = {Pv, Д, Ph}, где Д, Pt, Рь — множество частных, за- щищенных и публичных данных и методов класса. С другой стороны, основываясь на свойстве инкапсуляции, класс — это пара С = {/?, М}, где F — множество полей, а М - множество методов класса. Таким обра- зом, С = {PvF и PvM, PtF и PtM, PbF и PbM, (7.1) где PvF, PtF, PbF — множество частных, защищенных и публичных по- лей класса; PvM, PtM, PbM — множество частных, защищенных и пуб- личных методов класса. Очевидно, что PvF е F, PtF е F, PbF е F, PvM е М, PtM е М, РЬМ<еМ, PvFnPtF=0, PvFr\PbF=0, PtF n PbF = 0, PvM n PtM =0, PvM n PbM = 0, PtM n PbM = 0, PvF PtF PbF = F, PvM о PtM и PbM = F. Для описания структуры класса в виде символической записи пред- лагается следующая нотация. Символ, обозначающий атрибут класса, записывается как N®1*, где N— имя поля, a type — тип поля. Метод класса предлагается записывать как Meth^p5m (param), где Meth — идентификатор метода, param — перечень формальных параметров, a prim с [virtual, abstract, override}. Допустимо, что prim = 0 и (или) param = 0. Класс определяется как Спатесрнм, где Cprint = abstract |0. Синтаксис символьной записи структуры класса определяется грам- матикой, представленной в табл. 1. Таблица 7.1. Грамматика символьной записи структуры класса <ClassDare> ~^<CN>=<CNbase>+ {; [<PrtF> ,<PrtM>]; [<PubF>,<PubM>]} <CN> —»< id><Cprim > <FIELD><id><type> < PrvF>^<FIELDS> <FIELD>^>0 < PrtF>^><FIELDS> <METHODS>—^<METHOD> 336
<PubF>-t<FlELDS> <FIELDS>^KFIELD> <METHODS>^><METHOD>, <METHODS> < PrvM >^<METHODS> <METHOD>^<id> < type><prim > <param> < PrtM >^><METHODS> <Cprim>—> abstarct 0 < PuhM >^<METHODS> <prim>virtual abstract override 0 <FIELDS>^<FIELD>, <F1ELDS> <CNbase>—><id>+ 0 Таким образом, в символьном виде описание нового класса newClass, порожденного от базового класса baseCalss, - это выражение вида newClass = baseClass + {[PvF, РгЛ/], [PtF, PtM], [PbF, Phi\f] }. Рассмотрим пример символьной записи структуры классов, показан- ной в виде UML-диаграммы на рис. 7.1. Рис. 7.1. UML-диаграмма представления классов геометрических фигур 337
TPoint = । I ^tXyvnal (newX*). getY^O.setYyimal (newY* )] J [[Al” Л1” ]-[]-[^ом_л0. hide^Q. TLine = Tpoint + getXl^, Q.^tAT,^ („еиХ1“). getYY^ Q. setYl,^ ЫУ1")] [[A”][][^<wm,^O ^„^O-getRSLiO- - 7 . I ^‘Z -* *• OTe777fle •/ ОУ Cillat 7 О MttUOl Vz TCircle = Tpoint + 5 [лЯЯ^ДлемЯ”)] Очевидно, что символьная запись структуры классов компактнее со- ответствующей ей UML-диаграммы. Определим на основании предло- женной символьной записи операции над классами для решения задачи анализа структуры проекта и его последующего рефакторинга. Опреде- лим для двух произвольных классов С1 и С2 операцию пересечения, ко- торую будем записывать как Cl n С2. Введем в рассмотрение функцию 7(х(,х2) = [Х] \(х, ах2), х2 \(xj ах2)]. Пусть операция Cl n С2 в соответ- ствии со спецификацией (1) определяется как baseC = < [PvFcl П PvFC2,PvMc1 A PvMC2],y [PtFCi A PtFC2, PtMcl A PtMC2], > [PbFcl A PbFC2, PЬМС1 A PbMC2] J r [I (PvFcl,PvFC2), I (PvMcl, PvMC2)]? Cl A C2 = baseC +< [I(PtFcl,PtFC2),I{PtMcl,PtMC2)], . [I(PbFcl, PbFC2),KPbMcv PbMC2)]\ Базовый класс baseC - это ближайший общий предок в дереве иерар- хии или единый базовый класс BaseObject (NSObject, java.lang.Object, System.Object, TObject и т.п.), если такого не существует. Если реализа- ция методов с одинаковым объявлением PvM, PtM, РЬМ различна для операндов операции пересечения, то в результате пересечения они по- лучают дополнительный префикс abstract. Проиллюстрируем операцию строгого пересечения на примере UML-диаграммы классов, представ- ленных на рис. 7.2. 338
TLine fXI : int -fYl ; int -fX2 : int -fY2 : int +shcw() +hide() ♦ move() TEllipse -fX1 : int -fY1 :int -fRl : int (R2 : int +show() +hide() +rnove() TCircle 4X1 7 int -fY1 : int -fR: ini +show() +hideO +move4) Рис. 7.2. UML-диаграмма классов без общего предка TEllipse A TLin BaseObject + *{ [sFcWvirtuaixidstractO* ] hidevirtuai abstract07 0] I TEllipse A TCircle = TEllipse A TLine, TLine A TCircle = TEllipse A TLine. *3) Результатом пересечения классов, как правило, является новый класс или общий предок классов, заявленных в качестве операндов. Таким же образом определяем операцию вычитания классов С1\С2: [PvFc1\PvFC2,PvMc1\PvMc1 + ^abstract] 1 С1\С2 = <[PtFCi\PtFC2rPtMCi\PtMC2 V м tec i abstract 1 г [PbFcl\PbFC2, PbMCi\PhMcl + ^Mieci^^abstract] , (7.4) Операция вычитания есть традиционное вычитание множеств полей и методов класса с учетом необходимости перегрузки абстрактных ме- тодов. Образование новых классов в результате выполнения операции пересечения является основой для процесса рефакторинга, описанного в [28]. При этом следует действовать по следующему формальному ал- горитму вне зависимости от лингвистических сред и особенностей реа- лизации. Шаг 1. Выполнить преобразование в символьную запись всех объяв- лений классов программной системы. Шаг 2. Для всех классов, имеющих общего предка в дереве иерархии выполняется операция строгого пересечения: TParentObject = Г\ С(7.5) Шаг 3. Если результат операции пересечения не равен TParentOb- ject Ф BaseObject, определяем новый класс предка. Шаг 4. Выполняем рефакторинг путем вычитания из каждого класса С, класса TParentObject. 339
Рассмотрим пример рефакторинга программных классов, представ- ленных на рисунке 7.2. TFigure = BaseObject + < f i > I 0- hidetimal aislml (). moveOjl TLme = TFigure* {[jX2*.A2*],D,[sW^0- TEllipse = TFigure + {[/Rl“. /R2“ ]. []. [s/iow^O. hidem<mje ()]) 4 TCircle = TFigure* {[JK“ ].[].[«*ои’вмтИ.0-*«^<п.тл0]} (7.6) Таким образом, формальный подход к решению задачи рефакторин- га программных систем на основе символьной записи структуры клас- сов является основой для построения программного продукта, позво- ляющего выполнять автоматизированный рефакторинг архитектуры программных систем практически без участия программиста. 7.6. Архитектурный рефакторинг. Архитектурные паттерны 7.6.1. Когда нужен архитектурный рефакторинг Потребность в изменении существующей программной системы мо- жет возникнуть в ходе решения широкого круга задач по ее модерниза- ции [14, 15]. В общем случае изменения существующей программной системы затрагивают не только программный код, но и все остальные артефакты, связанные с трансформируемой программной системой. Часто существенным является изменение архитектуры программной системы. В качестве примеров можно привести следующие сценарии, требующие изменения архитектуры существующей ПС. 1. Преобразования, обусловленные функциональными изменениями ПС. Желательно, чтобы внедрение новой функциональности не затрону- ло существующую логику системы. Также желательно, чтобы слож- ность внедрения новой функциональности в существующую систему не превышала существенным образом сложность реализации этой функ- циональности в рамках нового проекта. Изменение существующей ар- хитектуры - хороший шаг на пути внедрения новой функциональности, облегчающий и дальнейшую эволюцию системы. 2. Смена платформы ПС (как аппаратной, так и общего программно- го обеспечения, в частности операционной системы) должна минималь- но затрагивать существующий код. Желательно ограничиться измене- ниями только в узкой, платформенно-зависимой прослойке системы. Выделение такой прослойки - архитектурная задача. Ее решение всегда сопряжено с необходимостью изменения архитектуры. 340
3. Обновление технологии разработки программного продукта, свя- занное, например, с переходом на компонентное программирование, внедрением комплексной среды коллективной разработки с возможно- стями гибкого, формального и смешанного планирования и ведения от- четности, доступными на одной платформе, например IBM Rational Team Concert. 4. Преобразования, связанные с реорганизацией компании, ведущей разработку. Примером такой реорганизации может стать аутсорсинг. Ре- шение об использовании аутсорсинга - типичный шаг по оптимизации производства. К сожалению, этот шаг зачастую затрудняется проблемой выделения и передачи компонентов для внешней разработки. Измене- ние архитектуры программной системы способно облегчить решение этой задачи. При описании методов рефакторинга принято использовать частич- но формализованный формат - шаблон или паттерн [32, 38]. Это повто- римая конструкция, представляющая собой решение проблемы проекти- рования в рамках некоторого часто возникающего контекста. Обычно шаблон не является законченным образцом, который может быть прямо преобразован в код; это лишь пример решения задачи, который можно использовать в различных ситуациях. Ориентированные шаблоны пока- зывают отношения и взаимодействия между классами или объектами, без определения того, какие конечные классы или объекты приложения будут использоваться [36]. «Низкоуровневые» шаблоны, учитывающие специфику конкретного языка программирования, называются идиома- ми. Это хорошие решения проектирования, характерные для конкретно- го языка или программной платформы, и потому не универсальные. На наивысшем уровне существуют архитектурные шаблоны. Они охваты- вают собой архитектуру всей системы. Любой паттерн описывает и именует типовую задачу, которая посто- янно возникает в работе, а также принцип ее решения, причем таким об- разом, что это решение можно использовать потом снова и снова. Пат- терн именует, абстрагирует и идентифицирует ключевые аспекты струк- туры общего решения. Помимо прочего, паттерны формируют словарь в проблемной области и позволяют двум специалистам в этой области именовать типовые решения и понимать друг друга, не объясняя каж- дый раз суть самих решений. Например, в [33] паттерн имеет следующую структуру: • название паттерна; • краткая сводка ситуаций, в которых требуется данный метод; • мотивировка применения; • пошаговое описание применения метода рефакторинга. Известно, что рефакторинг объектно ориентированного кода зареко- мендовал себя как эффективный способ решения задач эволюции и со- провождения программ. Однако, и это отмечается в статьях М. Ксензова [14, 15], опубликованных в 2004 г., практически не существует иссле- дований, освещающих рефакторинг на более высоком уровне абстрак- ции - уровне архитектуры ПО. Ситуация мало изменилась и к настоя- 341
щему времени. Поэтому вызывает интерес, возможен ли перенос дан- ной методологии на более высокий уровень абстракции с целью получе- ния аналогичной методики систематического изменения архитектуры ПО. Специфика исследования и трансформации архитектуры программ- ного обеспечения заключается в том, что архитектура не имеет явного представления, за исключением, может быть, тех случаев, когда она яв- но задокументирована. Однако даже в последнем случае трудно гаран- тировать соответствие задокументированной архитектуры фактической высокоуровневой логической структуре, которая на самом деле сущест- вует. Способом описания архитектуры и ее изменений могут стать структурные модели. В настоящее время существует большое количест- во нотаций и инструментов, поддерживающих структурное моделирова- ние программных систем. В свете соответствия модели фактической структуре существующего кода представляется исключительно важной возможность автоматического извлечения таких моделей из кода про- граммных систем, поскольку в этом случае гарантируется их точность. 7.6.2. Построение архитектуры ПС по ее программному коду Данной задаче посвящен ряд работ. Одной из них, наиболее интерес- ной и универсальной по предлагаемому подходу, является статья В. Ми- ронова [17]. В этой работе рассматривается технология анализа про- грамм большого размера на основе построения графов, что обеспечива- ет навигацию по исходным файлам с показом необходимых свойств (на- пример, используемых классов, методов, функций и др.). Предваритель- но стоит рассмотреть возможные подходы и инструментальные средст- ва анализа и формального представления текстов программ. 1 . Язык UML, предназначенный для определения, визуализации, конструирования и документирования артефактов программных систем. Он позволяет в объектно ориентированном виде описать систему прак- тически со всех возможных точек зрения, в том числе и разные аспекты поведения системы. Основной недостаток - неточная семантика. Язык UML предназначен для графического представления системы на сред- нем и высоком уровнях, но малоэффективен для описания детальной ло- гики программ нижнего уровня. 2 . CASE-технологии - программные комплексы, автоматизирующие технологический процесс анализа, проектирования, разработки и сопро- вождения сложных программных систем. CASE-технологии использова- лись в реинжиниринге с момента их появления. Однако по своей приро- де CASE-средства малопригодны для низкоуровневого описания струк- туры ПС. Кроме того, они предполагают рассмотрение ПС со стороны организации бизнес-процессов, потоков и создания оптимальной ИС для предприятия. Несмотря на высокие потенциальные возможности CASE-технологии, далеко не все разработчики ПС, использующие CASE-средства, достигают ожидаемых результатов. 342
3 . Комплекс программ построения графов Graphviz [4], разработан- ный специалистами лаборатории AT&T, представляет собой пакет ути- лит по автоматической визуализации графов, заданных в виде описания на языке dot. В пакет утилит Graphviz входит автоматический визуали- затор dot для формирования ориентированных графов на основе входно- го текстового описания (на специальном языке описания объектов, свя- зей и их характеристик) в виде графического, векторного или текстово- го файла. Данная технология является вспомогательным средством, обеспечивающим поддержку оптимального расположения вершин гра- фа. 4 . Kscope — программа для исследования и редактирования исходных текстов больших проектов на языке С9. (Сейчас Kscope не поддержива- ется. Прошлые релизы и репозитарий исходного кода еще доступны на SourceForge.net.) Программа позволяет построить граф-дерево вызовов, которое наглядно показывает отношения между функциями. В Kscope используется свой подход к процессу визуализации — составление и вы- зов запроса. Недостатками данного подхода являются малая информа- тивность (очень сложно увидеть на графе всю программу целиком) и ог- раниченность синтаксисом языка С. Рассмотрим принципы подхода к решению задачи построения архи- тектуры ПС по ее программному коду [17]. Одним из способов проведе- ния анализа является формирование графов с различным уровнем ото- бражения внутренних объектов и связей. Модель данных большой про- граммной системы, реализованной на языках программирования C/C++, представляет собой наборы директорий, содержащих различные по ис- пользованию файлы: с исходными кодами, с описанием типов объектов и вспомогательные. Основные принципы построения архитектуры ПС: • отображение графа связей файлов проекта (показывает, каким об- разом исходные файлы проекта подключают друг друга); • отображение иерархии наследования классов; • отображение диаграммы вызовов функций (диаграмма показыва- ет, каким образом управление попадает в выбранную функцию и куда оно передается из нее; связь на диаграмме соответствует вызову функ- ции); • регулирование объема отображаемой информации путем выделе- ния любого элемента программы с отображением только тех элементов, которые взаимодействуют с ним; • для любого элемента программы необходима возможность про- смотреть участки кода, в которых этот элемент используется; • сохранение исходных файлов исследуемой программы в хранили- ще данных; • возможность выделения из всей программы только необходимой функциональной части и сборка только этой части. Основные задачи анализа: • выявление описанных, но неиспользуемых элементов; • изучение исходного кода, на который отсутствует документация; 343
• исследование исходного кода для дальнейшей модернизации про- граммного продукта; • исследование исходного кода для дальнейшего переноса про- граммного продукты на другую платформу; • реструктуризация исходного кода через специально созданное хранилище данных. Для решения задач было разработано средство анализа исходных текстов. Анализ включает в себя выделение из текстов программ необ- ходимой разработчику информации для поиска процедур и функций, для поиска имен описания типов объектов и самих объектов. На основе выделения формируются структуры различных типов — по функциям и процедурам, по объектам (их именам, их типам) с отображением адре- са нахождения искомого объекта и его связей с другими объектами (на- пример, список всех подчиненных функций одного модуля). Основной подход при разработке средств анализа исходных тек- стов— создание кросс-платформенного продукта. Использованы скри- птовые языки Python, Lua. Это стабильные языки, использующиеся во многих проектах в качестве базовых или для создания расширений. Для построения графического интерфейса пользователя использована биб- лиотека wxWidgets [5], которая позволяет приложению выглядеть оди- наково на всех платформах. Основной код wxWidgets вызывает элемент интерфейса платформы, вместо того чтобы повторно его реализовывать. Это предоставляет быстрый, естественно выглядящий интерфейс на ка- ждой платформе. Для качественной визуализации используется ком- плекс Graphviz. Данные технологии поддерживают большое количество платформ и распространяются под свободными лицензиями. Структура данных системы. При анализе исходного текста програм- ма загружает объекты в виртуальное хранилище данных и позволяет отобразить различные представления: функциональную связь (рис. 7.3), связи по глобальным объектам (рис. 7.4), связь по описателям объектов (рис. 7.5). Схема функциональных связей предоставляет информацию о нали- чии описания различных функций в расположенных в разных директо- риях файлах и о том, где именно эти функции вызываются (каждая связь на диаграмме соответствует вызову функции). На диаграмме пока- зано, как будет выглядеть рекурсия, - замыкание функции 7 самой в се- бя. Каждая функция имеет адрес вида «Директория !, Файл !.с, Функ- ция!». Для построения графа связей конкретной функции можно сде- лать соответствующий запрос. Схема связей по глобальным объектам похожа на предыдущую, но отображает использование глобальных переменных, констант, объектов. Схема описателей объектов отображает зависимость расположенных в разных файлах описаний классов, типов, структур. Связь вида «Тип! —> Тип2» обозначает, что Тип2 является одним из составляющих Тип!. В классах это отношение «Потомок —> Родитель». Именно такой вид представлений позволяет получить исчерпывающую низкоуровне- вую информацию о рабочем проекте. При достаточно больших проектах 344
происходит консолидация всей необходимой для разработчика инфор- мации в одном месте. Директория 1| Директория^ | Файл_1. с функция_1 функция 2 фуч КЦИ Я—З Фа йл_2. с функция_4 функция_5 функция_б Файл З.с функция_7 фуНКЦИЯ-Й функция 9 Рис. 7.3. Отображение функциональных связей Системные функции Сист_фун кц _1 Ск1ст_функц_2 Сист_фун кц_3 Сист_фуцкц_4 Фаил_4.С фучкция_э функция_Ь функция_с Рис. 7.4. Отображение связей между объектами 345
Рис. 7.5. Отображение связей по описателям объектов Это позволяет разработчику объективно увидеть и оценить работу программы, найти неиспользуемые объекты (например, объект а на рис. 7.4), обнаружить рекурсии, увидеть родительские классы и их отли- чие от потомков. Технологические цепочки. Программа работает следующим образом: при выборе проекта для анализа программа производит поиск исходных файлов и при обнаружении начинает загружать их в собственное храни- лище данных, разбивая на простейшие объекты и сохраняя их зависимо- сти. На втором этапе происходит работа непосредственно с хранили- щем, а именно: в зависимости от целевого представления анализируют- ся необходимые объекты данных и строятся зависимости, происходит обращение к Graphviz для получения графических координат элементов. Третий этап заключается в отображении на экране полученной инфор- мации. В данной программе у программиста есть функционал сборки прило- жения либо части приложения. При обычном способе сборки, который используется большинством популярных IDE, будут получены испол- няемый файл и набор библиотек для работы программы. Библиотеки включают в себя все функции, и вычленить только часть не представля- ется возможным. Обычная сборка показана на рис. 7.6. Что делать, если требуется собрать программу только с одной или двумя основными функциями? Традиционный способ - переработка ис- ходных текстов, ручной поиск зависимостей вызовов функций и вклю- чение в итоговый продукт только выбранных функций. Система позво- ляет автоматизировать этот процесс. На рис. 7.7 отображен процесс сборки только одной функции (функция_1). 346
Файловая система Директория 1| Файл_1,с функция_1 функция_2 функция 3 — Файл_2. с функция_4 функция^ функция 6 Дмректория 2 tSj Оайл_3. с функция_7 функция_8 функция 9 -------к Файл 4.с функцияа функция Ь функция с Компиляция и линкоэка Полученные файлы Исполняемый файл ] Библиотека функций 1 * | Библиотека I функций N Рис. 7.6. Обычная сборка Пользователь системы, выбрав нужную компоновку (требуемые функции) и расположение файлов целевого приложения, запускает про- цедуру обработки. Происходит обращение к хранилищу данных, подго- товка данных, создание новой иерархии директорий и выгрузка необхо- димых объектов (классов, функций и т.д.) в микросреду для сборки — файлов нового проекта. Файлы имеют расположение, выбранное поль- зователем. Далее происходят компиляция и линковка. Результатом явля- ются исполняемый файл с требуемыми функциями и библиотека, кото- рая содержит требуемые зависимости. Возможность выделения необхо- димых функций полезна при модификации приложения, при коллектив- ной работе, при аудите исходных текстов сторонней организацией. Дальнейшее изложение материала этого раздела следует статьям М. Ксензова. Для исследования архитектуры программных систем ис- пользуется нотация структурного моделирования, принятая в инстру- менте KLOCwork Architect. Этот инструмент предоставляет возмож- ность автоматического извлечения моделей из программного кода и их редактирования. Далее рассматривается эта нотация. Модели программных систем, используемые в KLOCwork Architect (в дальнейшем— модель) [14], отдаленно напоминают модели типа «сущность — отношение» (entity-relation models). Основными единицами модели являются архитектурные блоки и отношения. 347
Рис. 7.7. Процесс сборки одной функции 7.6.3. Рефакторинг архитектуры многослойной иерархической ПС Архитектурный блок (architecture block). Архитектурные блоки — это основные элементы, составляющие модель. Архитектурные блоки ото- бражают структурные элементы программной системы вне зависимости от того уровня абстракции, на котором идет моделирование. Архитек- турные блоки обладают по меньшей мере двумя основными атрибута- ми: именем и типом. Имена архитектурных блоков предопределяются именами тех структурных элементов системы, которые они представля- ют в модели. Типы архитектурных блоков существенно зависят от уров- ня абстракции, на котором проходит моделирование, и конкретной зада- чи, в рамках которой проводится исследование архитектуры. 348
При моделировании взаимодействия «клиент — сервер» основной ис- пользуемый тип архитектурных блоков — «подсистема». При моделиро- вании систем, построенных в рамках компонентных технологий, основ- ной используемый тип— «компоненты». При моделировании системы сборки ПО основные используемые типы - «папки» и «файлы». При объ- ектно-ориентированном анализе основной используемый тип - «класс». Отношение (relation). В модели KLOCwork Architect под отношени- ем понимается некоторая односторонняя связь между парой архитектур- ных блоков. Между любой парой блоков в модели может быть произ- вольное количество разнонаправленных отношений, при этом их типы также могут различаться. Как и архитектурные блоки, отношения могут быть различных типов. В качестве примера можно привести следующие типы отношений: • инстанциация: А инстанциирует В (блок А - функция, блок В — класс); • наследование: А наследует В (блоки А и В - классы); • чтение данных: А читает данные из В (блок А - функция, блок В - класс, атрибут класса или функция); • обращение: А вызывает В (блоки А и В - функции); • А использует В (блок А — класс или функция, блок В - класс или атрибут класса). Модели обладают следующими свойствами. 1 . Иерархичность. Каждый архитектурный блок может содержать другие архитектурные блоки, при этом связи между архитектурными блоками суммируются. Например, если в модели есть блок А, который содержит блок А1, и блок В, который содержит В1и В2, и между блока- ми А1 и В1 есть связь, а также между блоками А1 и В2 есть связь, то считается, что между А и В есть две связи, поскольку они содержат два множества блоков, и количество связей между блоками из различных множеств равно двум. 2 . Точность. Для каждого элемента модели можно указать «стоящий за ним» в моделируемой системе набор файлов и строк кода. Точность модели обеспечивается способностью инструмента KLOCwork Architect автоматически извлекать их из программного кода. Над моделями в KLOCwork Architect определены следующие опера- ции редактирования: • добавление блока (в модель допустимо добавлять новые блоки); • удаление блока (из модели допустимо удалить произвольный блок); • перенос блока (из модели допустимо вырезать блок и перенести его внутрь другого блока); • переименование блока (в модели допустимо переименовывать блоки); • объединение блоков в группу (модель позволяет объединять блоки в группы, при этом создается новый блок, и группируемые элементы пе- реносятся внутрь него). 349
Важным свойством этих операций является то, что они сохраняют основные свойства модели, т.е. ее иерархичность и точность. Инструмент KLOCwork Architect способен автоматически извлекать из программного кода базовые структурные модели, отражающие физи- ческую структуру исследуемой программной системы. В состав таких моделей входят: • архитектурные блоки, представляющие папки, в которых находят- ся файлы с исходным кодом системы; • архитектурные блоки, представляющие собственно файлы (такие архитектурные блоки находятся внутри соответствующих блоков, кото- рые представляют папки, содержащие эти файлы); • архитектурные блоки классов, переменных, функций, находящих- ся внутри соответствующих блоков файлов. В [14, 15] отмечается специфическая черта рефакторинга архитекту- ры: для достижения промежуточных целей, возникающих в ходе архи- тектурного рефакторинга, как правило, приходится выполнять более од- ного шага. Эти шаги относятся к различным фазам решения поставлен- ных архитектурных задач. Можно условно выделить следующие фазы архитектурного рефакторинга: фазу «раскопки» архитектуры, фазу трансформации архитектуры, фазу семантического анализа подсистем и фазу проецирования изменений модели на программный код. «Раскопка» архитектуры характеризуется тем, что соответствующие действия, применяемые к модели, не ориентированы на последующее проецирование на программный код. Они нужны только для понимания и структуризации модели. Для шагов, относящихся к трансформации архитектуры, в отличие от шагов фазы «раскопки», типично последующее проецирование их на реальный код программной системы. Шаги этой фазы четко связаны с реальной модификацией кода системы и в конечном счете ориентиро- ваны на его улучшение. Следует также отметить, что часть методов ре- факторинга архитектуры не может быть строго отнесена к одной из на- званных категорий («раскопка» и трансформация). На практике это оз- начает, что решение о проецировании этих шагов на код принимает раз- работчик, руководствуясь поставленной задачей. Как правило, между шагами описанных выше фаз архитектурного рефакторинга предпринимаются шаги, которые можно отнести к фазе семантического анализа подсистем: по ходу трансформаций часто вста- ет задача выявления смысловой нагрузки подсистем. Для решения по- добных задач даже в первом приближении зачастую приходится иссле- довать реальный программный код (здесь опять-таки помогает точность модели), анализировать сигнатуры функций и комментарии, а при от- сутствии последних - и сам код функций. Задача специалиста, вовле- ченного в процесс архитектурного рефакторинга, — по возможности ми- нимизировать объем семантического анализа (например, путем удале- ния вспомогательных блоков) и сделать его последовательным и на- правленным. 350
Результат последнего шага — редактирование модели должно быть спроецировано на реальный программный код системы. Действительно, при проецировании удаления блоков из модели необходимо определить множество строк и файлов, которое соответствует удаленному блоку в программном коде. После этого необходимо удалить из программного проекта выявленные строки и файлы. При проецировании на код пере- носа блока в модели переносятся соответствующие строки и файлы в исходном коде программной системы и т.д. Производимые таким об- разом трансформации можно рассматривать как архитектур но-управ- ляемый рефакторинг программного кода. 7.6.4. Слои в архитектуре ПС. Паттерн выделения слоев Концепция слоев — одна из общеупотребительных моделей, исполь- зуемых разработчиками программного обеспечения для разделения сложных систем на более простые части (см. главы 2 и 6). В архитекту- рах компьютерных систем, например, различают слои кода на языке программирования, функций операционной системы, драйверов уст- ройств, наборов инструкций центрального процессора и внутренней ло- гики микросхем. В главе 2 отмечалось, что если система разбита на ряд слоев, то слой п — это набор компонентов системы, которые используют только компоненты слоя и - 1 и могут быть использованы только ком- понентами слоя п + 1. Слой п + 1 использует слой я, следовательно, абстракция понятий слоя п + 1 по меньшей мере не ниже, чем у слоя и, а в идеале, если архи- тектура системы эффективна, то уровень абстракции слоя должен быть выше. Соответственно, слой п скрывает (инкапсулирует) логику работы с понятиями, определенными на этом слое, позволяя, таким образом, слою п + 1 реализовать работу с более сложными понятиями, организо- вать более сложную логику, используя выразительные средства нижеле- жащего слоя. Можно выбирать альтернативную реализацию базовых слоев: компоненты верхнего слоя способны работать без каких-либо из- менений в нижележащих слоях при условии сохранения интерфейсов. Зависимость между слоями, т.е. фактически интерфейсы, предоставляе- мые нижними слоями верхним, можно свести к минимуму. Такая мини- мизация интерфейсов позволяет увеличивать гибкость системы. Слои способны удачно инкапсулировать многое, но не все: модифи- кация одного слоя подчас связана с необходимостью внесения каскад- ных изменений в остальные слои. Наличие избыточных слоев нередко снижает производительность системы. При переходе от слоя к слою данные обычно подвергаются преобразованиям из одного представле- ния в другое. Несмотря на это, инкапсуляция нижележащих функций за- частую позволяет достичь весьма существенного преимущества. Напри- мер, оптимизация слоя транзакций обычно приводит к повышению про- изводительности всех вышележащих слоев. В статье [15] рассматривается один из паттернов выделения слоев — представитель целого семейства паттернов выделения слоев. Подвидов 351
у этого паттерна существует достаточно много, и каждый из них облада- ет своей спецификой. Рассматриваемый паттерн имеет следующую структуру (следуя терминологии автора статей). Имя', выделение слоев. Ситуация', на диаграмме представлены элементы, для которых вер- ны следующие условия: • исходящие связи ведут только в последний выделенный слой (слой с номером п) или их нет, если ранее не был выделен ни один слой; • «кандидаты на объединение в новый слой» с номером п + 1 долж- ны обладать общим смыслом и (или) функциональностью (простейшей проверкой на наличие общности является простой критерий: если для кандидатов можно подобрать «общее определение», то можно считать, что они обладают требуемой общностью). Рецепт', объединить блоки в новый слой п + 1. Для двух произволь- ных слоев слой, обладающий большим порядковым номером, считается вышележащим. Если в результате применения паттерна было выделено п слоев и еще остались блоки, которые в силу ограничений не смогли быть отнесены ни к одному из выделенных слоев и формально не могут быть выделены в новый слой, то эти блоки по умолчанию считаются п + 1 слоем, который в дальнейшем именуется чердаком. Различают строгие слои, которые не допускают никаких отклонений в строгой структуре, и потому встречаются относительно редко, и не- строгие слои. Последние допускают связи вышележащего слоя с не- сколькими нижележащими слоями (потенциально - со всеми), а не толь- ко с непосредственным соседом снизу. Как отмечается в [14], архитек- тура с нестрогими слоями может быть как результатом эрозии, так и осознанным решением. Возможным дефектом архитектуры с нестро- гими слоями является нарушение абстракции. Это затрудняет анализ системы. Кроме того, изменения слоя в такой архитектуре значительно сложнее локализовать: волна изменений прокатится по всем слоям, ра- ботающим с изменяемым слоем. Как показано в главе 6, рассмотренные виды слоев можно модифи- цировать, позволив включать в произвольные слои сильносвязанные компоненты (СК). При таком подходе сильносвязанные компоненты рассматриваются фактически как атомарный элемент. Слои, содержа- щие СК, в [14] названы поглощающими. Без подобного смягчения усло- вий как сам СК, так и все блоки, которые могли бы попасть в вышеле- жащие слои, будут отправлены на чердак. Заметим, что не всегда СК на структурных диаграммах свидетельствуют о плохой архитектуре систе- мы. Возможным дефектом архитектуры с поглощающими слоями может стать эффект пропавшего слоя: дефектная связь приводит к появлению СК, которые по смыслу должны находиться на разных слоях. Паттерн выделения слоев может быть применен для чистого анализа («раскопки») архитектуры. Выделение слоев — это прием, который по- зволит сократить и сделать более направленным семантический анализ системы за счет структурного анализа. Это, несомненно, хорошо, по- скольку семантический анализ - более ресурсоемкий процесс. При этом 352
нет никакой необходимости отражения слоев на программный код и ин- фраструктуру его хранения. Например, при анализе архитектурного ко- да программной системы, написанной на Java, выделение слоев не пред- полагает обязательного выделения пакетов, соответствующих этим сло- ям. Если специалист, проводящий архитектурный рефакторинг, прини- мает решение все-таки не отображать слои в пакеты языка Java, то мож- но считать, что выделение слоев было применено для чистого анализа архитектуры. Выделение слоев - хорошая основа для улучшения системы. Найти строгие слои в произвольной программной системе значительно труд- нее, чем найти слои с некоторыми допустимыми отклонениями, как, на- пример, сильносвязанные компоненты. Тем не менее для каждого из до- пустимых отклонений известны побочные эффекты, которые можно устранять, по возможности приводя слои к строгим. В заключение этого раздела отметим, что существуют и другие архи- тектурные паттерны, часть из которых рассмотрена в [14]. В этой же ра- боте предложены такие направления дальнейшего развития рефакторин- га архитектуры. 1. Каталогизация. Направление, связанное с дальнейшим сбором, обобщением и классификацией паттернов рефакторинга. 2. Автоматизация. Представляет большой интерес возможность об- легчения и автоматизации применения как существующих, так и вновь предлагаемых паттернов. 3. Верификация. Вызывает интерес возможность верификации со- хранности поведения программной системы при архитектурном рефак- торинге. 4. Направленность. Желательно вооружить разработчика, знающего паттерны архитектурного рефакторинга и имеющего соответствующие инструменты моделирования, процедурой, определяющей последова- тельность применения паттернов, чтобы сделать процесс эффективным. Создание подобных процедур представляет значительный исследова- тельский интерес. 7.7. Архитектурным рефакторинг для повышения производительно- сти многослойных программных систем 7.7.1. Возможный подход к созданию программных систем В свое время М. Фаулер высказал идею рефакторинга БД, однако об ахитектурном рефакторинге программных систем речи не было. Надо сказать, и в настоящее время вопросам архитектурного рефакторинга посвящено незначительное количество работ. В то же время эволюция сложных программных систем требует от разработчика повышенного внимания к выбору архитектуры. Практически всегда во время разра- ботки появляются новые требования со стороны заказчика, и прихо- диться пересматривать первоначальную архитектуру. Выделяют сле- дующие фазы архитектурного рефакторинга: 1) «раскопки» архитекту- 353
ры, 2) трансформация архитектуры, 3) семантический анализ подсистем и 4) проецирование изменений на программный код. В настоящей рабо- те рассматриваются и решаются вопросы рефакторинга многослойных программных систем, целью которого является повышение производи- тельности системы. Как правило, значительная часть программных систем создается в срочном порядке. Требуется автоматизировать (создать поддерживаю- щую ПС) некоторую совокупность взаимодействующих бизнес-процес- сов. Часто наспех составленное техническое задание передается вы- бранной (возможно, без предварительного анализа или на основе тенде- ра) компьютерной фирме, которая обещает выполнить работу в требуе- мые (как правило, минимальные) сроки и за приемлемую стоимость. Подобные фирмы обычно используют гибкие технологии создания программных систем, основанные на итерационном и инкрементном подходе к созданию ПО. Это может быть Scrum- или Agile-методология с элементами экстремального программирования. В этом случае можно избежать масштабного проектирования наперед и не тратить много сил на проектирование раньше времени. Такой поход к разработке ПС по- зволяет достаточно быстро создать совокупность программных моду- лей, автоматизирующих заданный набор бизнес-процессов В. Однако зачастую эти модули часто создаются независимо друг от друга, и в этом случае могут быть пересечения по функциям, реализуе- мым модулями. Возможны (и это чаще) ситуации, когда один модуль может обращаться к другому для выполнения некоторых функций, реа- лизуемых этим модулем. Здесь нужно заметить, что под модулем пони- мается достаточно произвольный структурный элемент ПС (подсистема, компонент, отдельный программный модуль, группа классов, отдель- ный класс), который можно выделить, определив интерфейс взаимодей- ствия между этим модулем и всем, что его окружает. Часты ситуации, когда разрабатываемая ПС слабо документируется, и об архитектуре создаваемой системы и ее целесообразности разработ- чики особенно не задумываются. Однако архитектура разрабатываемой системы существует, и она, собственно, создана ее авторами-разработ- чиками независимо от их желания. В первом приближении архитектуру ПС в этом случае можно представить некоторым множеством про- граммных модулей: М = {ту | г = 1, 2,..., A, j = 1, 2,..., и,}, N 1 (7.7) где N — количество бизнес-процессов; К — количество модулей в про- граммной системе; i — номер бизнес-процесса; j — номер модуля, реали- зующего у-функцию /-го бизнес-процесса; л, — количество функций, реа- лизуемых /-м бизнес-процессом; Л/, - подмножество модулей, автомати- зирующих /-й бизнес-процесс Ь.е В. 354
В общем случае справедливо соотношение jV ПЛ/,*0. (7.8) I Каждый модуль mf/ можно представить следующими параметрами спецификации: P:J = {Name, , Otj, 4,}, (7.9) где Name - имя модуля т,,; 1у — параметры входного интерфейса модуля ту', Оу — параметры выходного интерфейса модуля ту, А у — абстракция алгоритма, реализуемого модулем ту. Заметим, что абстракция через спецификацию позволяет абстрагиро- ваться от алгоритма, описанного в теле модуля, до уровня знания лишь того, что данный модуль должен в итоге реализовать. Существует ото- бражение вида О: В —» М, которое определяет подмножества модулей, автоматизирующих функции конкретных бизнес-процессов. Таким об- разом, существуют отображения О.bj; > Mt с М, г = 1, 2, . ..,ЛЛ При этом возможны непустые пересечения: М, у = 1,2,...,Л\ (7.10) Это свидетельствует о возможном дублировании некоторых функ- ций бизнес-процессов в автоматизируемых их модулях ПС. Однако воз- можны ситуации, когда для некоторого отображения Os |Л7,| < | hi |, что говорит том, что ПС реализует не все функции бизнес-процесса Ь,. Но даже если в этом плане нет претензий к разработанной программной системе, как отмечено выше, часто архитектура ПС не только предвари- тельно не разрабатывается, но и недостаточно (или совсем) не докумен- тируется. Отсюда в интересах дальнейшей разработки системы или ее сопровождения возникает проблема «раскопки» архитектуры, как ее часто называют в литературе [2, 3]. 7.7.2. Представление созданной архитектуры ПС Удобным и наглядным способом представления архитектуры про- граммных систем является использование графов. В работе [4] строится модель ПС на основе исходного кода, когда может отсутствовать ин- формация о составляющих систему блоках. В нашем случае рассматри- вается пример разработки ПС на основе гибкой технологии, когда в раз- рабатываемую систему последовательно добавляются новые модули. В этом случае каждый модуль ту системы можно представить именем Name и частью параметров Оу из спецификации модуля — именами мо- дулей, которые могут быть вызваны из модуля ту. Для удобства и про- стоты дальнейших построений каждый модуль будем представлять в следующем виде: 355
Шу —»< namberX, namberl, патЬегЗ,... >, (7.11) где number \ — номер модуля т^, отождествляемый с его именем Name\ number! — номер 1-го модуля, к которому может обращаться модуль number! - номер 2-го модуля, к которому может обращаться модуль mih и т.д. Таким образом, в целом перечень всех модулей и их взаимосвязей можно представить списком следующего вида: (7.12) где S., i = 1,2,..., К — элементы списка следующей структуры: • 5, =<1, $,к, • 5*2 =с !)l9t9 f \... >, • SK =< К, g, z, м,... >, где 5, к, т, I, t, f,..., g, z,и ... — номера модулей. На основе списка £ можно построить граф G, отображающий струк- туру ПС. Однако в таком представлении трудно сделать вывод о типе архитектуры программной системы и ее качестве. Известно, что значи- тельная часть современных программных систем имеет многослойную архитектуру. В таких архитектурах модули нижнего слоя для выполне- ния своих функций не обращаются к другим слоям. Организация выше- лежащих слоев может быть различной. Поэтому при анализе получен- ной архитектуры ПС первой задачей является выделение слоев модулей. Многослойная архитектура обеспечивает группировку связанной функ- циональности приложения в разных слоях, выстраиваемых вертикально, поверх друг друга. Функциональность каждого слоя объединена общей ролью или ответственностью. Слои слабо связаны, и между ними осу- ществляется явный обмен данными. Правильное разделение приложе- ния на слои помогает поддерживать строгое разделение функциональ- ности, что обеспечивает гибкость, а также удобство и простоту разра- ботки. Слои приложения могут размещаться физически на одном компью- тере (на одном уровне) или быть распределены по разным компьютерам (л уровней). Связь между компонентами разных уровней осуществляет- ся через строго определенные интерфейсы. Далее будем рассматривать ПС одного определенного языкового уровня с хорошо определенными синтаксическими единицами в соответствии с хорошо определенными синтаксическими правилами и хорошо определенной семантикой эле- ментарных операторов и синтаксических конструкций. Из рассмотрения исключим все вопросы, относящиеся к другим языковым уровням, та- 356
ким, например, как интерпретация элементарных операторов в терми- нах более примитивных составляющих. Элементарные операторы данного языкового уровня будем рассмат- ривать как модули базового уровня, составляющие базовый слой. Это можно сделать потому, что все элементарные операторы ПС повсемест- но доступны. Заметим, что здесь не учитываются привилегированные операторы машинного языка, которые не могут использоваться в при- кладных программных системах. Модули, построенные из модулей ба- зового уровня, могут рассматриваться только как модули нулевого уровня. При этом не требуется, чтобы все модули нулевого уровня были равнодоступны. Например, в программах, написанных на языке, допус- кающем блочную структуру, некоторые модули нулевого уровня могут быть локализованы в каком-либо блоке и, следовательно, доступны только внутри этого блока и его подблоков. С другой стороны, при кон- струировании модулей высших уровней в некоторых языках можно ис- пользовать модули разных уровней и даже того же самого уровня, что и конструируемый модуль (рекурсия, сопрограммы). Введем в рассмотрение матрицу R размером К х К, каждый элемент которой образуется по правилу f 1, если j е St-, [О — в противном случае. (7.13) Далее можно следовать алгоритму, который дается ниже. 0. Начало, 1 = 0. 1. Находим в матрице номера строк, все элементы которых равны нулю. 2. Фиксируем вершины с этими номерами, образующими 1-слой. 3. I =1+ 1. 4. Если остались столбцы с ненулевыми элементами, обнуляем столбцы с номерами найденных вершин. Переходим к п. 1. 5. Если все столбцы содержат только нулевые элементы, конец. Надо заметить, что данный алгоритм позволяет построить послой- ную архитектуру ПС, которая удовлетворяет одному из вариантов, рас- смотренных в [5]. Однако если в слоях ПС имеются горизонтальные связи или сильносвязанные модули, то полностью определить структу- ру ПС без дополнительного анализа не удастся. 7.7.3. Анализ на соответствие послойной архитектуре (выделение слоев) Рассмотрим порядок проведения анализа на конкретном примере. Пусть задана списком S некоторая совокупность модулей ПС (9 моду- лей), которая представляется следующей матрицей R: 357
гО О о о о о о о Lo 1 о о о о о о о о I 1 о о с с о ОО1ОООО 0 0 110 11 0 0 0 0 1 0 0 0 0 0 0 1 1 0 0 1 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Действуя по приведенному выше алгоритму, находим нулевые стро- ки и таким образом определяем модули нулевого слоя: Lo= {8, 9}, Вы- черкиваем восьмой и девятый столбцы. Находим нулевую строку с но- мером 7, определяющую слой L\ = {7}. Вычеркиваем седьмой столбец и определяем слой L2 = {4, 5}. Вычеркиваем столбцы 4 и 5. По ставшим нулевыми строкам определяем модули третьего слоя: = {2, 3, 6}. Вы- черкиваем строки 2, 3 и 6. Вычеркиваем столбцы с этими номерами и определяем модули четвертого слоя = {1}. Получив распределение модулей по слоям ПС, можно построить граф G программной системы (рис. 7.8). Анализируя полученный граф, следует отметить, что он не отвечает каноническим правилам многослойной структуры. В частности, мо- дуль 6 не отвечает этим требованиям. Известно, что выделение слоев - хорошая основа для улучшения системы. Найти строгие слои в произ- вольной программной системе достаточно трудно, поскольку, как уже отмечалось, они могут содержать горизонтальные связи и сильносвязан- ные компоненты. 358
Поэтому целесообразно расширить понятие слоя, позволив включать в произвольные слои сильносвязанные компоненты. Эти компоненты при таком подходе можно рассматривать как атомарные модули. Заме- тим, что не всегда сильносвязанные компоненты на структурных диа- граммах свидетельствуют о плохой архитектуре системы. Возможным дефектом архитектуры с поглощающими слоями может стать эффект пропавшего слоя: дефектная связь приводит к появлению модулей, ко- торые по смыслу должны находиться на разных слоях. 7.7.4. Коррекция (трансформация) архитектуры в интересах ее рефакторинга В основе рефакторинга лежит последовательность небольших экви- валентных (т.е. сохраняющих поведение) преобразований, сохраняю- щих функциональную семантику базового кода. По ходу трансформа- ций часто встает задача выявления смысловой нагрузки модулей. Задача специалиста, вовлеченного в процесс архитектурного рефакторинга, — по возможности минимизировать объем семантического анализа (на- пример, путем удаления вспомогательных блоков) и сделать его после- довательным и направленным. Первым уровнем рефакторинга можно считать такое изменение кода, которое не затрагивает структуру моду- лей, т е. рефакторингу подвергается программный код внутри классов. Второй уровень рефакторинга относится к изменению структуры моду- лей или классов программной системы, добавлению новых классов, вы- делению и разбиению больших классов, переносу или добавлению но- вых методов, выделению интерфейсов и др. Следующий, третий, уровень рефакторинга М. Фаулер называет крупным рефакторингом. В монографии идет речь о четырех рсфакто- рингах третьего уровня. Это разделение наследования, преобразование процедурного проекта в объекты, отделение предметной области от представления и выделение иерархии. Рефакторинг архитектуры про- граммных систем является четвертым уровнем рефакторинга. Необхо- димость в архитектурном рефакторинге может быть связана со следую- щими причинами. 1. Плохо структурированный код из-за часто вносимых изменений разработчиками, которые не до конца понимают архитектуру ПС. 2. Повышение производительности ПС. Рефакторинг первого и вто- рого уровней, несомненно, заставляет программу выполняться медлен- нее, но при этом делает ее более понятной и податливой для настройки производительности. 3. Потребность в функциональных изменениях ПС. Изменение суще- ствующей архитектуры может быть хорошим шагом на пути внедрения новой функциональности, облегчающим дальнейшую эволюцию систе- мы. 4. Смена платформы ПС. Желательно ограничиться изменениями только в узкой, платформенно-зависимой прослойке системы. Выделе- 359
ние такой прослойки всегда сопряжено с необходимостью изменения архитектуры. 5. Обновление технологии разработки программного продукта, свя- занное, например, с переходом на более совершенную технологию про- граммирования, внедрением комплексной среды коллективной разра- ботки и др. 6. Преобразования, связанные с реорганизацией компании, ведущей разработку. Например, введение аутсорсинга. Изменение архитектуры ПС способно облегчить решение этой задачи. Прежде чем говорить о коррекции архитектуры, следует задаться вопросом, как оценить качество структуры ПС. Из практики проектиро- вания известно, что лучшее решение обеспечивается иерархической структурой в виде дерева. Степень отличия реальной проектной струк- туры от дерева характеризуется невязкой структурой (см. главу 6). Зна- чение невязки лежит в диапазоне от 0 до 1. Если Nev = 0, то проектная структура является деревом, если Nev = 1, то проектная структура — пол- ный граф. Ясно, что невязка даст грубую оценку структуры. Для увели- чения точности оценки следует применить характеристики связности и сцепления. Вернемся к структуре, приведенной на рис. 7.8. Ясно, что модуль 6 не может находиться с модулем 3 в одном слое. Так как модуль 3 для выполнения своих функций обращается к модулю 6, то последний дол- жен быть перемещен в нижележащий слой. Возможный вариант новой структуры показан на рис. 7.9. Рис. 7.9. Первый вариант коррекции архитектуры ПС 360
Заметим, что в данном случае модуль 2 должен быть перемещен в нижележащий слой 3. Следует обратить внимание на увеличение ко- личества слоев ПС после выполненной коррекции. Этот важный факт может привести к увеличению времени работы ПС, Отметим также, что после такой коррекции структуры не изменилась сложность ПС, опреде- ляемая по значению Nev, поскольку число вершин и ребер осталось прежним. Возможен другой вариант коррекции архитектуры, связанный с объ- единением модулей 3 и 6 (на рис. 7.10 это модуль 3-6). При этом не ме- няется количество слоев ПС, но изменяется число вершин и ребер (8 вершин и 12 ребер). Это несколько снижает сложность ПС по значе- нию Nev, Однако объединенный модуль возрастает по объему, усложня- ется его программирование. Формализовать процесс коррекции архитектуры ПС или тем более построить алгоритм коррекции довольно затруднительно. Однако в ряде случаев, выделив отдельные фрагменты структуры ПС, можно их пре- образовать, стремясь к получению наилучшей структуры, например к дереву. Чаще всего это удается сделать путем объединения (поглоще- ния) модулей. Некоторые примеры такой коррекции приведены ниже. Рис. 7.10, Второй вариант коррекции архитектуры ПС На рис. 7.11 а) показана последовательная цепочка модулей 2 и 3, ко- торые используются только модулем 1. Может быть, это стало следст- вием желания распараллелить работу по программированию этих моду- лей. Возможный вариант улучшения структуры ПС путем объединения модулей 2 и 3 показан на рис. 7.11 б). Заметим, что если в этом случае модуль 1 обращается только к модулям 2-3 и 4, то сокращается число слоев ПС. 361
Слой 1 Слой 1 Рис. 7.11. Коррекция поглощением нижележащим слоем На рис. 7.12 а) показан случай, когда результаты работы модулей 1 и 2 используются только модулем 3. Улучшение структуры ПС можно получить объединением модулей 1 и 2, как показано на рис. 7.12 б). б) Рис. 7.12. Коррекция объединением (вариант 1) Другой случай объединения модулей 2 и 3 приведен на рис. 7.13 а). Он возможен в том случае, если к модулям 2 и 3 обращается только мо- дуль 1, а сами модули 2 и 3 обращаются только к модулю 4. В результа- те объединения модулей 2 и 3, как показано на рис. 7.13 б), упрощается структура ПС. При коррекции архитектуры ПС, кроме объединения модулей и пе- ремещения по слоям, возможны ситуации разделения модулей, как по- казано на рис. 7.14, Однако в каждом конкретном случае решение о той или иной коррекции структуры ПС должно приниматься после деталь- ного его анализа и оценки целесообразности. 362
Рис. 7.13. Коррекция объединением (вариант 2) Рис. 7.14. Коррекция разделением Результат коррекции архитектуры должен быть спроецирован на ре- альный программный код системы. При проецировании удаления моду- лей из модели необходимо определить множество строк и файлов, кото- рое соответствует удаленному блоку в программном коде. После этого необходимо удалить из программного проекта выявленные строки и файлы. При проецировании на код переноса модуля в модели перено- сятся соответствующие строки и файлы в исходном коде программной системы и т.д. Производимые таким образом трансформации можно рассматривать как архитектурно-управляемый рефакторинг программ- ного кода. 7.7.5. Рефакторинг архитектуры в интересах повышения производительности ПС Варианты многослойных структур. Предварительно остановимся на вариантах построения многослойных ПС. На рис. 7.15 и 7.16 показаны 363
возможные две общие структуры организации слоев программы. С по- нятием слоев ПС связана концепция многоуровневых виртуальных ма- шин. На рис. 7.15 показан подход, когда задача построения архитекту- ры программы рассматривается как создание машины пользователя, или виртуальной машины (и) начиная с самого низшего уровня (0) аппарату- ры (или, возможно, операционной системы). Последовательность уров- ней, называемых абстрактными машинами, определяется так, что каж- дая следующая машина строится на основе предыдущих, расширяя их возможности. Каждый уровень может ссылаться только на один, отлич- ный от него самого уровень, а именно на тот, который непосредственно ему предшествует. Рис. 7.15. Вариант классической архитектуры В структуре, изображенной на рис. 7.16, уровни не являются полны- ми абстракциями более низких уровней, каждый из них может ссылать- ся на все предшествующие уровни. Возможен и третий вариант, являю- щийся промежуточным между двумя первыми. В этом случае слою (i) разрешается использовать только некоторые из команд, обеспечивае- мых слоями (1), (2), ..., (г — 1). Каждый вариант имеет свои достоинства и недостатки. Рис. 7.16. Вариант структуры со ссылками слоев на все предшествующие Остановимся на особенностях основных вариантов многослойных структур. Если в варианте по рис. 7.15 каждый слой имеет доступ к ко- мандам только одного слоя, разработчик должен иметь в виду только предыдущий слой. Хотя с точки зрения проектирования этот вариант 364
кажется привлекательным, он может оказаться очень неэффективным. Например, если некоторое средство, предоставляемое слоем (2), потре- буется в слое (z), то каждый из слоев (3), (4), (z — 1) должен обеспе- чить это средство. Это значит, что запрос данного средства слоем (?) должен просачиваться вниз через слой (z— 1), пока не достигнет слоя (2), который способен выполнить запрос. Такой подход связан с допол- нительными затратами времени на трансляцию запросов. Эти трудно- сти, связанные с проблемой эффективности, могут склонить к принятию структуры по рис. 7.16, в которой каждый слой (?), где 2 < i < я, может непосредственно обращаться к слою (2). Таким образом, с точки зрения производительности весьма актуаль- ной становится задача определения оптимальной структуры многослой- ной ПС. Постановка задачи. Представим структуру многослойной про- граммной системы в обобщенном виде, показанном на рис. 7.17. В дан- ном случае каждый слой показан в виде одного модуля с возможностью организации связей с любым произвольным слоем системы. Такая обоб- щенная схема позволяет рассмотреть любую структуру н-слойной про- граммной системы, лежащую в диапазоне структур, приведенных на рис. 7.15 ирис. 7.16. Произвольная структура описывается некоторым множеством булевых переменных: X = Ц- е {0,1} | i = и, л-1, 2; j = п -1, п - 2,..., 1; i >;}, (7.14) где Ху =1, если существует связь между слоями i nj\ и Ху =0, если такой связи нет. Так как между смежными слоями всегда имеется связь, то (V/|z = ,/' + l)(xv- = 1), i = n, л-1,...,2. (7.15) Если в многослойной структуре программы, представленной выра- жением (7.14), принимают единичное значение только переменные, описываемые условием (7.15), то эта программа имеет структуру, соот- ветствующую варианту по рис. 8. Если справедливо условие (3; I (г = л, п -1,..2))&((3/1 (г - j > 2))(^ = 1), (7.16) то программа имеет структуру, соответствующую промежуточному ва- рианту между вариантами структур, представленными на рис. 7.15 и рис. 7.16. Если справедливо условие (Vz|/ = «,«-l>...,2)(3/|(i--j>2))(x..=l), (7.17) то программа имеет структуру, соответствующую варианту, представ- ленному на рис. 7.15. Дальнейшую постановку задачи удобно провести на конкретном примере. Учитывая, что число слоев в большинстве существующих про- грамм, как правило, не превышает трех-пяти, рассмотрим пятислойную программу, структура которой приведена на рис. 7.17, В данном случае 365
структура разработанной ПС имеет вариант классической архитектуры, т.е. удовлетворяет условию 7.15. Пунктирными линиями на рис. 7.17 по- казаны возможные дополнительные связи между слоями ПС. Каждой линии поставлена в соответствие переменная, единичное значение кото- рой означает наличие межслойной связи, нулевое — отсутствие такой связи. Будем считать, что ПС прошла полный этап тестирования и в про- цессе отладки определены временные характеристики модулей. Уста- новлены также частоты обращения модулей произвольного слоя к моду- лям нижележащих слоев. Предположим, что передача (трансляция) за- проса через слой i дополнительно (кроме выполнения собственных функций) загружает этот слой на некоторый промежуток времени tj. Ес- ли *5 = 0 (т.е. отсутствует связь между слоями 5 и 3), то модуль т4 до- полнительно работает в течение промежутка времени t5. Если = 1 (т.е. вводится связь между слоями 5 и 3), то дополнительное время модулю т4 не потребуется. Однако в этом случае будет необходимо в программу добавить межмодульный интерфейс для взаимодействия модулей т$ и Будем считать, что это увеличит программу на некоторую величи- ну Аналогичные рассуждения справедливы и для других перемен- ных, показанных на рис. 7.17. Слон Аппаратура Рис. 7.17. Структура пятислойной программы Таким образом, дополнительные связи между слоями программы со- кращают время выполнения ее функций, но увеличивают размер про- граммы. Необходимо также учесть тот факт, что дополнительно созда- ваемые связи между слоями могут работать с различной нагрузкой. Так, например, если создается связь, обозначаемая переменной х5, то модуль т4 освобождается от трансляции только тех запросов, которые модуль т5 адресует модулю т3. Поэтому целесообразно каждой переменной л; поставить в соответствие определенную интенсивность взаимодействия некоторой пары модулей X,. Таким образом, задача архитектурного ре- 366
факторинга ПС сводится к определению такой структуры многослойной программной системы, которая обеспечивает наилучшую производи- тельность программы при заданных ограничениях на размер дополни- тельных межмодульных интерфейсов. Математическая постановка и решение задачи. В нашем случае структура многослойной программной системы может быть представле- на вектором X = {х; 11• = 5, 6,..., 9} (заметим, что всегда X] = х2 = х2 = = х4=1, так как эти переменные определяют связи между смежными слоями). Поэтому требуется найти такое значение Хор(, при котором обеспечивается максимальный выигрыш во времени работы ПС: МахТ = £'=’ X, х f, х , (7.18) при выполнении ограничения на допустимое увеличение программы Е за счет дополнительных межмодульных интерфейсов Х^ХХ,. <Е. (7.19) У читывая двоичный характер переменных, следует добавить ограни- чение WU 6 {0,1}). (7.20) Сформулированная задача относится к классу задач линейного про- граммирования с булевыми переменными (в данном случае это задача о загрузке рюкзака). Малая размерность рассмотренной задачи позволя- ет легко решить ее полным перебором наборов переменных, представ- ляющих допустимые решения в условиях принятых ограничений. Предложенный подход к архитектурному рефакторингу ПС целесо- образно применить на этапе заключительной отладки системы, когда получены количественные значения временных и частотных параметров работы модулей. В реальных программных системах, содержащих по нескольку модулей в каждом слое, размерность задачи может сущест- венно вырасти, и потребуется применить более сложные алгоритмы ре- шения задачи. Однако в связи с вероятностным характером исходных данных в рассматриваемой задаче применять методы получения опти- мального решения не имеет смысла, так что можно ограничиться при- ближенными, быстро работающими алгоритмами. Результат решения должен быть спроецирован на реальный программный код системы. Литература к главе 7 1. Model-View-Controller [Электронный ресурс]// URL: http://ru. wikipedia.org/wiki/Model-view-controllcr 2. Refactoring (Рефакторинг) (комментарии) [Электронный ресурс] // URL: http://www.gamedev.ru/code/forum/?id=131858 3. Smalltalk?! [Электронный ресурс] // URL: http://www.smalltalk.ru/ articles/smal ltalk-2.html 367
4. Welcome to Graphviz [Электронный ресурс] H URL: http://graph- viz.org/ 5. wxWidgets [Электронный ресурс] // URL: http ://www. wxwidgets. org/ 6. Ворожко Я. Refactoring [Электронный ресурс] // URL: http:// pro 1 OOpro.com/category/refactoring 7. Гагарина Л.Г., Кокорева E.B., Виснадул БД. Технология разработ- ки программного обеспечения: учебное пособие / под ред. Л.Г. Гагари- ной. - М.: ФОРУМ; ИНФРА-М, 2008. - 400 с. %. Деревянко В, Рефакторинг в Visual Studio [Электронный ресурс] // URL: http://so ft. sib net.ru/re view/? id=623 9. Дубаков M. Рефакторинг: улучшение существующего кода [Элек- тронный ресурс] // URL: http://www.kv.by/index2003302301.htm 10. Дубина О, Обзор паттернов проектирования [Электронный ре- сурс] // URL: http://citforum.ru/SE/project/pattem/p_4.shtml 11. Жидков А. П. Культура программирования [Электронный ре- сурс] И URL: http://www.javaportal.ru/articles/culture_of_programming. html 12. Кериевски Дж. Рефакторинг с использованием шаблонов.: пер. с англ. — М.: Издательский дом «Вильямс», 2008. - 400 с. 13. Краковецкий А. Как писать высококлассный код. Ч. 2: Возможно- сти Visual Studio 2010 [Электронный ресурс] И URL: http://msug.vn.ua/Posts/DetailsZ4165 14. Ксензов М.В. Рефакторинг архитектуры программного обеспече- ния [Электронный ресурс] // URL: http://www.ispras.ru/ru/proceedings/ docs/2004/8/1 /isp 2004 8 1 211 .pdf 15. Ксензов М.В. Рефакторинг архитектуры программного обеспече- ния: выделение слоев. - 2004. — С. 211—227. 16. Методы улучшения качества кода: рефакторинг [Электронный ресурс] // URL: http://social.msdn.microsoft.com/Forums/ru-ru/ fordesktopru/thread/63d9bal 9-491 b-4e75-9796-6de5132с 1 а5 6 17. Миронов В.О. Применение графов для анализа сложных систем на основе исходного кода программ [Электронный ресурс] // URL: http://berestneva.am.tpu.ru/Papers/KONF2009/%f7%c9%ce%c5%d2%cf%d7 %d3%cb%c9%c5%20%de%d4%c5%ce%c9%dl/2009%20(F)/fscommand/p dfZ056.pdf 18. Нильссон Дж. Глава из книги «Применение DDD и шаблонов проектирования: проблемно-ориентированное проектирование прило- жений с примерами на C# и .NET» [Электронный ресурс] // URL: http://rsdn.ru/res/book/prog/ddd.xml 19. Обзор средств автоматизированного рефакторинга в Java IDE [Электронный ресурс] // URL: http://www.javaportal.ru/java/ide/review_ refactoring.html 20. Переименовать рефакторинг (С#) Visual Studio 2010 [Электронный ресурс] // URL: http://msdn.microsoft.com/ru-ru/library/ 6kxxabwd.aspx 21. Панацея [Электронный ресурс] // URL: http://sly-and-fluffy. blogspot.com/2011/03/blog-post_25.html 368
22. Рефакторинг [Электронный ресурс] И URL: http://ru.wikipedia. org/wiki/%D0% A0%D0%B5%D 1 %84%D0%B 0%D0%B A%D 1 %82%D0% BE%D 1 %80%D0%B8%D0%BD%D0%B3#.D0.9C.D0.B5.D 1.82.D0.BE.D0 .B4.D 1 8B.D 1.80.D0.B5.D 1.84.D0.B0.D0.BA.D 1.82.D0.BE.D1.80.D0.B8. D0.BD.D0.B3.D0.B0#.D0.9C.D0,B5.D 1.82.D0.BE.D0.B4.D 1 8B_.Dl.80.D0 .B5.D1.84.DO.BO.DO.BA.DI 82.D0.BE.D1.80.D0.B8.D0.BD.D0.B3.D0.B0 23. Рефакторинг кода [Электронный ресурс] // URL: http://dev.inindillusion.ru/refactoring/ 24. Рефакторинг [Электронный ресурс] // URL: http://redoe.blogspot. com/2011/06/b log-post_29.html 25. Рефакторинг [Электронный ресурс] // URL: http://ru.wikipedia. org/wiki/%D0%E5%F4%E0%EA%F2%EE%F0%E8%ED%E3 26. Рефакторинг на уровне класса [Электронный ресурс] И URL: http://www.riotlabs.ru/post/ Class-level-refactoring.aspx 27. Рефакторинг на уровне метода [Электронный ресурс] // URL: http://www.riotlabs.ru/post/ method-level-refactorings, aspx 28. Сергеев Г.Г. Формализация процессов рефакторинга на основе символьной записи структуры классов: сборник научных трудов «Вест- ник НТУ ХПИ». - Харьков, 2010. - С. 100 - 104. 29. Синдром рефакторинга [Электронный ресурс] // URL: http:// habrahabr.ru/blogs/refactoring/120000/ 30. Сперанский В. Рефакторинг: меняем архитектуру [Электронный ресурс] // URL: http://www.pcmag.ru/solutions/sub_detail.php?lD=30214& SUB_PAGE=1 31. Тепляков С, Шаблоны проектирования. История успеха. RSDN Magazine #2-2010 [Электронный ресурс] // URL: http://sergeyteplyakov. blogspot, com/2 010/01 /blog-post.html 32. Технический долг [Электронный ресурс] // URL: http:// habrahabr.ru/blogs/refactoring/119490/ 33. Фаулер М., Бек К., Брант Д., Робертс Д., Апдайк У. Рефакторинг: улучшение существующего кода. - СПб.: Символ-Плюс, 2009. - С. 432. 34. Фаулер М. Архитектура корпоративных программных приложе- ний: пер. с англ. — М.: Издательский дом «Вильямс», 2006. — 544 с. 35. Томас Д. Программист-прагматик. Путь от подмастерья к масте- ру: пер. с англ. — Лори, 2009. — 270 с. 36. Черный С. Б Оптимизация процесса структуризации кода [Элек- тронный ресурс] // URL: http://www.nbuv.gov.ua/portal/natural/Vejpt/ 2011_2_2/2011_2_2/3 l_34.pdf 37. Шаблон проектирования [Электронный ресурс] // URL: http://ru. wikipedia.org/wiki/%D8%E0%E1%EB%EE%ED_%EF%F0%EE%E5%EA %F2%E8%F0%EE%E2%E0%ED%E8%FF 38. Эмблер С.} Садаладж П. Рефакторинг баз данных: эволюцион- ное проектирование: пер. с англ. — М.: Издательский дом «Вильямс», 2007.-368 с. 39. Эффект второй системы [Электронный ресурс] // URL: http:// habrahabr.ru/blogs/refactoring/121213/http://www.saasworld.ru/analytics/ven d/
СОДЕРЖАНИЕ Предисловие................................................3 Введение...................................................5 Глава 1. Проблемы создания больших программных систем......9 1.1. Особенности разработки сложных (больших) программных систем.........................................9 1,2, Проблемы создания ПС.................................13 1.3. Кризис программирования. Инженерный подход к разработке ПС...........................................15 1.4. Становление и развитие программной инженерии.........16 1.5, Развитие технологий программирования.................20 Литература к главе 1......................................28 Глава 2. Архитектуры программных систем...................31 2.1. Понятие архитектуры программной системы .............31 2.2. Почему важна архитектура.............................35 2.3. Как появляется архитектура. Кто и что влияет на архитектуру.37 2.4. Архитектурные образцы, эталонные модели и эталонные варианты архитектуры............................39 2.5. Что определяет и на что влияет выбранная архитектура.41 2.6. Архитектурные структуры и представления..............50 2.7. Отношения между структурами..........................54 2.8. Варианты архитектур программных систем...............55 2.8.1. Архитектура, основанная на уровнях абстракций..55 2.8.2. Архитектуры, основанные на портах..............60 2.8.3. Архитектуры, основанные на потоках данных......65 2.8.4. Архитектуры независимых компонентов............65 2.8.5. Сервис-ориентированные архитектуры (SOА).......67 2.9. Архитектурные представления программных систем.......69 2.9.1. Архитектурный вид — структура многослойной программной системы....................................70 2.9.2. Архитектурный вид - размещение программной системы....? 1 2.9.3. Архитектурный вид — размещение программной системы, основанной на потоках данных.................80 2.9.4. Архитектурный вид — распределение работ по группам разработчиков..............................81 Литература к главе 2......................................86 Глава 3. Жизненный цикл программных систем................88 3.1. Понятие жизненного цикла программных систем..........88 3.2. Основные процессы ЖЦ ПС..............................89 3.3. Вспомогательные процессы ЖЦ ПО.......................94 3.4. Организационные процессы ЖЦ ПС.......................97 3.5. Взаимосвязь между процессами ЖЦ ПС...................98 3.6. Модели и стадии ЖЦ ПС.......................................99 3.7. Виды моделей ЖЦ ПС и технологии создания программных систем...............................................103 370
3.7.1. Каскадная модель (классический жизненный цикл)..103 3.7.2. Итерационная модель ЖЦ ПС.......................105 3.7.3. Макетирование...................................106 3.7.4. Стратегии конструирования ПС....................108 3.7.5. Инкрементная модель.............................108 3.7.6. Спиральная модель ЖЦ ПО.........................109 3.7.7. Рациональный унифицированный процесс............111 3.7.8. Scrum-методология...............................117 3.7.9. Agile-методологии...............................122 3.7.10. Управление жизненным циклом приложений.........130 Литература к главе 3...................................... 140 Глава 4. Проектирование программных систем. Определение требований и целей программного продукта...................142 4.1. Процесс проектирования как последовательная трансляция требований, предъявляемых к системе........................142 4.2. Методология решения задач проектирования ПС по Г. Майерсу.. 148 4.3, Уровни требований к программным системам..............150 4.4. Определение требований к программным системам.........154 4.4.1. Постановка задачи и принципы разработки требований.... 154 4.4.2. Бизнес-моделирование............................159 4.4.3. Определение функциональных требований...........165 4.4.4. Определение нефункциональных (эксплуатационных) требований.............................................169 4.5. Анализ и управление требованиями......................170 4.6. Требования и риски....................................175 4.7. Проверка правильности требований......................180 4.8. Цели программного продукта............................183 4.9. Постановка целей для программной системы..............187 4.9.1. Цели продукта...................................187 4.9.2. Цели проекта....................................189 Литература к главе 4.......................................190 Глава 5. Разработка предварительного внешнего проекта......192 5.1. Представление и анализ требований.....................192 5.1.1. Требования в V-модели Халла.....................192 5.1.2. Моделирование в определении требований и спецификаций ........................................194 5.1.3. Разработка программных систем, управляемая моделями... 196 5.2. Анализ требований и определение спецификаций. Структурный подход.........................................199 5.2.1. Спецификации....................................199 5.2.2. Структурный подход представления спецификаций...204 5.2.3. Метод функционального моделирования.............205 5.2.4. Диаграммы потоков данных........................211 5.2.5. Диаграммы переходов состояний...................218 5.3. Анализ требований и определение спецификаций при объектном подходе......................................219 371
5.3.1. Общие сведения о языке UML как языке моделирования сложных систем..........................219 5.3.2. Определение прецедентов использования..........224 5.3.3. Концептуальная модель предметной области.......228 5.3.4. Описание поведения системы.....................234 5.4. Разработка предварительного внешнего проекта.........241 5.4.1. Процесс внешнего проектирования................241 5.4.2. Проектирование взаимодействия с пользователем..243 5.4.3. Подготовка внешних спецификаций................245 5.4.4. Проверка правильности внешних спецификаций.....250 Литература к главе 5......................................253 Глава 6. Проектирование архитектуры программных систем.......255 6.1. Методология проектирования...........................255 6.2. Модульность......................................... 258 6.2.1. Модули, модульно-интерфейсный подход, модульное программирование............................258 6.2.2. Обоснование модульности........................260 6.2.3. Внутренняя характеристика модуля — связность (прочность)...........................................262 6.2.4. Сцепление модулей - внешняя характеристика модуля.268 6.3. Сложность программной системы........................269 6.3.1. Методы оценки сложности........................269 6.3.2. Оценка сложности на основе связности и сцепления модулей...................................274 6.4. Представление архитектуры программных систем.........279 6.4.1. Модульно-интерфейсный подход...................279 6.4.2, Объектно ориентированный поход.................281 6.4.3. Компонентный подход............................285 6.5. Слои программного продукта...........................287 6.6. Методы структурного проектирования...................292 6.6.1. Метод восходящей разработки («снизу вверх»)....292 6.6.2. Метод нисходящей разработки («сверху вниз»)....295 6.6.3. Заключительные замечания по структурному проектированию........................................297 6.7. Формальное описание методики разработки модульной архитектуры программной системы ..........................298 6.7.1. Проектирование «сверху вниз»...................298 6.7.2. Проектирование «снизу вверх»...................303 6.7.3. Еще раз о проектировании архитектуры ПС на основе объектно ориентированной и компонентной методологии......305 Литература к главе 6......................................307 Глава 7. Рефакторинг программных систем...................310 7.1. Что такое рефакторинг................................310 7.2. Рефакторинг, проектирование и производительность программ..320 7.3. Когда применять рефакторинг..........................323 7.4. Уровни рефакторинга..................................333 372
7.5. Методы рефакторинга...................................335 7.5.1. Основные методы.................................335 7.5.2. Формализация процесса рефакторинга на основе символьной записи структуры классов....................336 7.6. Архитектурный рефакторинг.............................340 7.6.1. Когда нужен архитектурный рефакторинг...........340 7.6.2. Построение архитектуры ПС по ее программному коду.342 7.6.3. Рефакторинг архитектуры многослойной иерархической ПС.......................................348 7.6.4. Слои в архитектуре ПС. Паттерн выделения слоев....351 7.7. Архитектурный рефакторинг для повышения производительности многослойных программных систем.........353 7.7.1. Возможный подход к созданию программных систем....353 7.7.2. Представление созданной архитектуры ПС............355 7.7.3. Анализ на соответствие послойной архитектуре (выделение слоев)........................................357 7.7.4. Коррекция (трансформация) архитектуры в интересах ее рефакторинга............................359 7.7.5. Рефакторинг архитектуры в интересах повышения производительности ПС..................................363 Литература к главе 7.......................................367
По вопросам приобретения книг обращайтесь: Отдел продаж «ИНФРА-М» (оптовая продажа): 127214, Москва, ул. Полярная, д. 31 В, стр. I Тел. (495) 280-33-86 (доб. 218, 222) E-mail: bookwarc@infra-m.ni Отдел «Книга—почтой»: тел. (495) 280-33-86 (доб. 222) ФЗ Издание не подлежит маркировке № 436-ФЗ в соответствии с п. 1 я. 2 ст. 1 Научное издание Назаров Станислав Викторович АРХИТЕКТУРА И ПРОЕКТИРОВАНИЕ ПРОГРАММНЫХ СИСТЕМ Монография Подписано в печать 28.06.2022. Формат 60x90/16. Печать цифровая. Бумага офсетная. Гарнитура Newton. Усл. псч. л. 23,38. ППТ10. Заказ № 00000 ГК 401500-1895672-280316 ООО «Научно-издательский центр ИНФРА-М» 127214, Москва, ул. Полярная, д. 31В, стр. 1. Тел.: (495) 280-15-96, 280-33-86. Факс: (495) 280-36-29. E-mail: books@infra-m.ru http: //www.infra-m.ru Отпечатано в типографии ООО «Науч но-изд ателье кий центр ИНФРА-М 127214, Москва, ул. Полярная, д. 31В, стр. 1 Тел.: (495) 280-15-96, 280-33-86. Факс: (495) 280-36-29