практические работы. Методические указания к лабораторной работе (1). Федерации федеральное агентство по образованию государственное
Скачать 0.67 Mb.
|
Декомпозиция задачи. Структурный и модульный подход к проектированиюАрхитектуры программного средстваАрхитектура ПС − это его строение как оно видно (или должно быть видно) извне его, т.е. представление ПС как системы, состоящей из некоторой совокупности взаимодействующих подсистем. В качестве таких подсистем вы- ступают обычно отдельные программы. Разработка архитектуры является пер- вым этапом борьбы со сложностью ПС, на котором реализуется принцип выде- ления относительно независимых компонент. Основные задачи разработки архитектуры ПС: выделение программных подсистем и отображение на них внешних функций (заданных во внешнем описании) ПС; определение способов взаимодействия между выделенными программ- ными подсистемами. С учетом принимаемых на этом этапе решений производится дальнейшая конкретизация и функциональных спецификаций. Различают следующие основные классы архитектур программных средств: цельная программа; комплекс автономно выполняемых программ; слоистая программная система; коллектив параллельно выполняемых программ. Методы проектированияПриступая к разработке каждой программы ПС, следует иметь в виду, что она, как правило, является большой системой, поэтому мы должны принять меры для ее упрощения. Для этого такую программу разрабатывают по частям, которые называются программными модулями. А сам такой метод разработки программ называют модульным программированием. Программный модуль − это любой фрагмент описания процесса, оформ- ляемый как самостоятельный программный продукт, пригодный для использо- вания в описаниях процесса. Это означает, что каждый программный модуль программируется,компилируется и отлаживается отдельно от других модулей программы, и тем самым, физически разделен с другими модулями программы. Более того, каждый разработанный программный модуль может включаться в состав разных программ, если выполнены условия его использования, деклари- рованные в документации по этому модулю. Таким образом, программный мо- дуль может рассматриваться и как средство борьбы со сложностью программ, и как средство борьбы с дублированием в программировании (т.е. как средство накопления и многократного использования программистских знаний). Модульное программирование является воплощением, в процессе разра- ботки программ, борьбы со сложностью, обеспечивает независимость компо- нент системы, и использование иерархических структур. Для избегания сложностей формулируются определенные требования, ко- торым должен удовлетворять программный модуль, т.е. выявляются основные характеристики «хорошего» программного модуля и используют древовидные модульные структуры программ (включая деревья со сросшимися ветвями). В узлах такого дерева размещаются программные модули, а направленные дуги (стрелки) показывают статическую подчиненность модулей, т.е. каждая дуга показывает, что в тексте модуля, из которого она исходит, имеется ссылка на модуль, в который она входит. Другими словами, каждый модуль может об- ращаться к подчиненным ему модулям, т.е. выражается через эти модули. При этом модульная структура программы,в конечном счете,должна включать и совокупность спецификаций модулей, образующих эту программу. Спецификацияпрограммного модуля содержит: синтаксическую спецификацию его входов, позволяющую построить на используемом языке программирования синтаксически правильное обращение к нему (к любому его входу), функциональную спецификацию модуля (описание семантики функ- ций, выполняемых этим модулем по каждому из его входов). Функциональная спецификация модуля строится так же,как и функцио- нальная спецификация ПС. В процессе разработки программы ее модульная структура может по-раз- ному формироваться и использоваться для определения порядка программиро- вания и отладки модулей, указанных в этой структуре. Поэтому можно гово- рить о разных методах разработки структуры программы. Обычно в литературе обсуждаются два метода: метод восходящей разработки и метод нисходящей разработки. Метод восходящей разработки заключается в следующем. Сначала строится модульная структура программы в виде дерева. Затем поочередно про- граммируются модули программы,начиная с модулей самого нижнего уровня (листья дерева модульной структуры программы), в таком порядке, чтобы для каждого программируемого модуля были уже запрограммированы все модули, к которым он может обращаться. После того, как все модули программы запро- граммированы, производится их поочередное тестирование и отладка в принци- пе в таком же (восходящем) порядке, в каком велось их программирование. Метод нисходящейразработкизаключается в следующем. Как и в преды- дущем методе сначала строится модульная структура программы в виде дерева. Затем поочередно программируются модули программы, начиная с модуля самого верхнего уровня (головного), переходя к программированию какого- либо другого модуля только в том случае,если уже запрограммирован модуль, который к нему обращается. После того, как все модули программы запрограм- мированы, производится их поочередное тестирование и отладка в таком же (нисходящем) порядке. При этом первым тестируется головной модуль про- граммы, который представляет всю тестируемую программу и поэтому тести- руется при «естественном» состоянии информационной среды, при котором на- чинает выполняться эта программа. При этом те модули, к которым может об- ращаться головной,заменяются их имитаторами(так называемыми заглушка- ми). Каждый имитатор модуля представляется весьма простым программным фрагментом, который, в основном, сигнализирует о самом факте обращения к имитируемому модулю, производит необходимую для правильной работы про- граммы обработку значений его входных параметров (иногда с их распечаткой) и выдает, если это необходимо, заранее запасенный подходящий результат. По- сле завершения тестирования и отладки головного и любого последующего мо- дуля производится переход к тестированию одного из модулей, которые в дан- ный момент представлены имитаторами, если таковые имеются. Для этого ими- татор выбранного для тестирования модуля заменяется самим этим модулем и, кроме того,добавляются имитаторы тех модулей,к которым может обращаться выбранный для тестирования модуль. При этом каждый такой модуль будет те- стироваться при «естественных» состояниях информационной среды, возни- кающих к моменту обращения к этому модулю при выполнении тестируемой программы. Таким образом, большой объем «отладочного» программирования при восходящем тестировании заменяется программированием достаточно про- стых имитаторов используемых в программе модулей. Кроме того, имитаторы удобно использовать для того, чтобы подыгрывать процессу подбора тестов пу- тем задания нужных результатов, выдаваемых имитаторами. При таком поряд- ке разработки программы вся необходимая глобальная информация формирует- ся своевременно,т.е.ликвидируется весьма неприятный источник просчетов при программировании модулей. Некоторым недостатком нисходящей разработки, приводящим к опреде- ленным затруднениям при ее применении, является необходимость абстрагиро- ваться от базовых возможностей используемого языка программирования, вы- думывая абстрактные операции, которые позже нужно будет реализовать с по- мощью выделенных в программе модулей. Однако способность к таким аб- стракциям представляется необходимым условием разработки больших про- граммных средств, поэтому ее нужно развивать. Особенностью рассмотренных методов восходящей и нисходящей разра- боток(которые мы будем называтьклассическими)является требование,чтобы модульная структура программы была разработана до начала программирова- ния (кодирования) модулей. Конструктивныйподходк разработке программы представляет собой мо- дификацию нисходящей разработки, при которой модульная древовидная структура программы формируется в процессе программирования модулей. Разработка программы при конструктивном подходе начинается с програм- мирования головного модуля, исходя из спецификации программы в целом. При этом спецификация программы принимается в качестве спецификации ее головного модуля, который полностью берет на себя ответственность за выпол- нение функций программы.В процессе программирования головного модуля,в случае, если эта программа достаточно большая, выделяются подзадачи (вну- тренние функции), в терминах которых программируется головной модуль. Это означает, что для каждой выделяемой подзадачи (функции) создается специфи- кация реализующего ее фрагмента программы, который в дальнейшем может быть представлен некоторым поддеревом модулей.Важно заметить,что здесь также ответственность за выполнение выделенной функции несет головной (может быть, и единственный) модуль этого поддерева, так что спецификация выделенной функции является одновременно и спецификацией головного мо- дуля этого поддерева. В головном модуле программы для обращения к выде- ленной функции строится обращение к головному модулю указанного поддере- ва в соответствии с созданной его спецификацией. Таким образом, на первом шаге разработки программы (при програм- мировании ее головного модуля) формируется верхняя начальная часть дерева, например, такая, которая показана на рисунок 6. Рисунок6 –Первый шаг формирования модульной структуры программы при конструктивном подходе Аналогичные действия производятся при программировании любого дру- гого модуля, который выбирается из текущего состояния дерева программы из числа специфицированных, но пока еще не запрограммированных модулей. В результате этого производится очередное доформирование дерева программы, например, такое, которое показано на рисунке 7. Архитектурный подход к разработке программы представляет собой мо- дификацию восходящей разработки, при которой модульная структура про- граммы формируется в процессе программирования модуля.Но при этом ста- вится существенно другая цель разработки: повышение уровня используемого языка программирования, а не разработка конкретной программы. Это означа- ет, что для заданной предметной области выделяются типичные функции, каж- дая из которых может использоваться при решении разных задач в этой обла- сти, и специфицируются, а затем и программируются отдельные программные модули, выполняющие эти функции. Так как процесс выделения таких функций связан с накоплением и обобщением опыта решения задач в заданной предмет- ной области, то обычно сначала выделяются и реализуются отдельными моду- лями более простые функции, а затем постепенно появляются модули, исполь- зующие ранее выделенные функции. Рисунок 7 – Второй шаг формирования модульной структуры программы при конструктивном подходе Такой набор модулей создается в расчете на то, что при разработке той или иной программы заданной предметной области в рамках конструктивного подхода могут оказаться приемлемыми некоторые из этих модулей. Это позво- ляет существенно сократить трудозатраты на разработку конкретной програм- мы путем подключения к ней заранее заготовленных и проверенных на практи- ке модульных структур нижнего уровня. Так как такие структуры могут много- кратно использоваться в разных конкретных программах,то архитектурный подход может рассматриваться как путь борьбы с дублированием в програм- мировании. В связи с этим программные модули, создаваемые в рамках архи- тектурного подхода, обычно параметризуются для того, чтобы усилить приме- нимость таких модулей путем настройки их на параметры. Все эти методы имеют еще различные разновидности в зависимости от того, в какой последовательности обходятся узлы (модули) древовидной струк- туры программы в процессе ее разработки. Это можно делать, например, по слоям (разрабатывая все модули одного уровня, прежде чем переходить к сле- дующему уровню). При нисходящей разработке дерево можно обходить также в лексикографическом порядке(сверху вниз,слева направо).Возможны и дру- гие варианты обхода дерева. Так, при конструктивной реализации для обхода дерева программы целесообразно следовать идеям Фуксмана, которые он ис- пользовал в предложенном им методе вертикального слоения. Сущность такого обхода заключается в следующем. В рамках конструктивного подхода сначала реализуются только те модули, которые необходимы для самого простейшего варианта программы, которая может нормально выполняться только для весьма ограниченного множества наборов входных данных, но для таких данных эта задача будет решаться до конца.Вместо других модулей,на которые в такой программе имеются ссылки, в эту программу вставляются лишь их имитаторы, обеспечивающие, в основном, сигнализацию о выходе за пределы этого частно- го случая. Затем к этой программе добавляются реализации некоторых других модулей (в частности, вместо некоторых из имеющихся имитаторов), обеспечи- вающих нормальное выполнение для некоторых других наборов входных дан- ных. И этот процесс продолжается поэтапно до полной реализации требуемой программы. Таким образом, обход дерева программы производится с целью кратчайшим путем реализовать тот или иной вариант (сначала самый простей- ший)нормально действующей программы. В связи с этим такая разновидность конструктивной реализации получила название методацеленаправленной конструктивной реализации. Достоинством этого метода является то, что уже на достаточно ранней стадии создается работающий вариант разрабатываемой программы. Психологически это играет роль допинга, резко повышающего эф- фективность разработчика. Поэтому этот метод является весьма привлекатель- ным. Рисунок 8 – Классификация методов разработки структуры программ Подводя итог сказанному, на рисунке 8 представлена общая классифика- ция рассмотренных методов разработки структуры программы. Декомпозиция программы по SADT технологииСущность структурного подхода к разработке ПС заключается в ее де- композиции (разбиении) на автоматизируемые функции: система разбивается на функциональные подсистемы, которые в свою очередь делятся на подфунк- ции, подразделяемые на задачи и так далее. Процесс разбиения продолжается вплоть до конкретных процедур. При этом автоматизируемая система сохраня- ет целостное представление, в котором все составляющие компоненты взаимо- увязаны. При разработке ПС используется методы проектирования, описанные выше. Все наиболее распространенные методологии структурного подхода бази- руются на ряде общих принципов. В качестве двух базовых принципов исполь- зуются следующие: принцип "разделяй и властвуй" – принцип решения сложных проблем путем их разбиения на множество меньших независимых задач, легких для понимания и решения; принцип иерархического упорядочивания – принцип организации со- ставных частей проблемы в иерархические древовидные структуры с добавлением новых деталей на каждом уровне. Выделение двух базовых принципов не означает,что остальные принци- пы являются второстепенными, поскольку игнорирование любого из них может привести к непредсказуемым последствиям (в том числе и к провалу всего проекта). Основными из этих принципов являются следующие: –принцип абстрагирования – заключается в выделении существенных ас- пектов системы и отвлечения от несущественных; –принцип формализации – заключается в необходимости строгого мето- дического подхода к решению проблемы; –принцип непротиворечивости – заключается в обоснованности и согла- сованности элементов; –принцип структурирования данных–заключается в том,что данные должны быть структурированы и иерархически организованы. В струк- турном анализе используются в основном две группы средств, иллюстри- рующих функции, выполняемые системой и отношения между данными. Каждой группе средств соответствуют определенные виды моделей (диа- грамм), наиболее распространенными среди которых являются следую- щие: SADT (Structured Analysis and Design Technique) модели и соответству- ющие функциональные диаграммы; DFD (Data Flow Diagrams) диаграммы потоков данных; ERD (Entity-Relationship Diagrams)диаграммы "сущность-связь". Перечисленные модели в совокупности дают полное описание ПС незави- симо от того, является ли она существующей или вновь разрабатываемой. Со- став диаграмм в каждом конкретном случае зависит от необходимой полноты описания системы. На стадии проектирования ПС модели расширяются,уточняются и до- полняются диаграммами, отражающими структуру программного обеспечения: архитектуру ПО, структурные схемы программ и диаграммы экранных форм. Методология SADT разработана Дугласом Россом и получила дальней- шее развитие в работе. На ее основе разработана, в частности, известная мето- дология IDEF0 (Icam DEFinition), которая является основной частью програм- мы ICAM (Интеграция компьютерных и промышленных технологий), проводи- мой по инициативе ВВС США. Методология SADT представляет собой совокупность методов, правил и процедур, предназначенных для построения функциональной модели объекта какой-либо предметной области.Функциональная модельSADTотображает функциональную структуру объекта, т.е. производимые им действия и связи между этими действиями. Основные элементы этой методологии основываются на следующих концепциях: графическое представление блочного моделирования. Графика блоков и дуг SADT-диаграммы отображает функцию в виде блока, а интер- фейсы входа/выхода представляются дугами, соответственно входя- щими в блок и выходящими из него. Взаимодействие блоков друг с другом описываются посредством интерфейсных дуг, выражающих "ограничения", которые в свою очередь определяют, когда и каким об- разом функции выполняются и управляются; строгость и точность. Выполнение правил SADT требует достаточной строгости и точности, не накладывая в то же время чрезмерных огра- ничений на действия аналитика. Правила SADT включают: ограничение количества блоков на каждом уровне декомпозиции (пра- вило 3-6 блоков); связность диаграмм (номера блоков); уникальность меток и наименований (отсутствие повторяющихся имен); синтаксические правила для графики (блоков и дуг); разделение входов и управлений (правило определения роли данных). отделение организации от функции, т.е. исключение влияния органи- зационной структуры на функциональную модель. Методология SADT может использоваться для моделирования широкого круга систем и определения требований и функций, а затем для разработки си- стемы, которая удовлетворяет этим требованиям и реализует эти функции. Для уже существующих систем SADT может быть использована для анализа функ- ций, выполняемых системой, а также для указания механизмов, посредством которых они осуществляются. Результатом применения методологии SADT является модель, которая состоит из диаграмм,фрагментов текстов и глоссария,имеющих ссылки друг на друга. Диаграммы - главные компоненты модели, все функции ПС и интер- фейсы на них представлены как блоки и дуги. Место соединения дуги с блоком определяет тип интерфейса. Управляющая информация входит в блок сверху, в то время как информация, которая подвергается обработке, показана с левой стороны блока, а результаты выхода показаны с правой стороны. Механизм(че- ловек или автоматизированная система), который осуществляет операцию, представляется дугой, входящей в блок снизу (рисунок 9). Одной из наиболее важных особенностей методологии SADT является постепенное введение все больших уровней детализации по мере создания диа- грамм, отображающих модель. Рисунок 9 – Функциональный блок и интерфейсные дуги На рисунке 10, где приведены четыре диаграммы и их взаимосвязи, пока- зана структура SADT-модели. Каждый компонент модели может быть декомпо- зирован на другой диаграмме. Каждая диаграмма иллюстрирует "внутреннее строение" блока на родительской диаграмме. Иерархия диаграммПостроение SADT-модели начинается с представления всей системы в виде простейшей компоненты - одного блока и дуг, изображающих интерфейсы с функциями вне системы. Поскольку единственный блок представляет всю си- стему как единое целое, имя, указанное в блоке, является общим. Это верно и для интерфейсных дуг - они также представляют полный набор внешних интер- фейсов системы в целом. Затем блок, который представляет систему в качестве единого модуля, де- тализируется на другой диаграмме с помощью нескольких блоков, соединен- ных интерфейсными дугами.Эти блоки представляют основные подфункции исходной функции. Данная декомпозиция выявляет полный набор подфункций, каждая из которых представлена как блок, границы которого определены ин- терфейсными дугами. Каждая из этих подфункций может быть декомпозирова- на подобным образом для более детального представления. Во всех случаях каждая подфункция может содержать только те элемен- ты, которые входят в исходную функцию. Кроме того, модель не может опу- стить какие-либо элементы, т.е., как уже отмечалось, родительский блок и его интерфейсы обеспечивают контекст. К нему нельзя ничего добавить, и из него не может быть ничего удалено. Модель SADT представляет собой серию диаграмм с сопроводительной документацией, разбивающих сложный объект на составные части, которые представлены в виде блоков.Детали каждого из основных блоков показаны в виде блоков на других диаграммах. Каждая детальная диаграмма является де- композицией блока из более общей диаграммы. На каждом шаге декомпозиции более общая диаграмма называется родительской для более детальной диаграм- мы. Дуги, входящие в блок и выходящие из него на диаграмме верхнего уров- ня, являются точно теми же самыми, что и дуги, входящие в диаграмму нижне- го уровня и выходящие из нее, потому что блок и диаграмма представляют одну и ту же часть системы. Рисунок 10 – Структура SADT-модели. Декомпозиция диаграмм На рисунках 11 – 12 представлены различные варианты выполнения функций и соединения дуг с блоками. Рисунок 11 – Одновременное выполнение Рисунок 12 – Соответствие должно быть полным и непротиворечивым Некоторые дуги присоединены к блокам диаграммы обоими концами, у других же один конец остается не присоединенным. Не присоединенные дуги соответствуют входам, управлениям и выходам родительского блока. Источник или получатель этих пограничных дуг может быть обнаружен только на роди- тельской диаграмме. Не присоединенные концы должны соответствовать дугам на исходной диаграмме.Все граничные дуги должны продолжаться на роди- тельской диаграмме, чтобы она была полной и непротиворечивой. На SADT-диаграммах не указаны явно ни последовательность, ни время. Обратные связи, итерации, продолжающиеся процессы и перекрывающиеся (по времени) функции могут быть изображены с помощью дуг. Обратные связи мо- гут выступать в виде комментариев, замечаний, исправлений и т.д. (рисунок 13). Рисунок 13 – Пример обратной связи Как было отмечено,механизмы(дуги с нижней стороны)показывают средства, с помощью которых осуществляется выполнение функций. Механизм может быть человеком, компьютером или любым другим устройством, которое помогает выполнять данную функцию (рисунок 14). Рисунок 14 – Пример механизма Каждый блок на диаграмме имеет свой номер. Блок любой диаграммы может быть далее описан диаграммой нижнего уровня,которая,в свою оче- редь, может быть далее детализирована с помощью необходимого числа диа- грамм. Таким образом, формируется иерархия диаграмм. Для того, чтобы указать положение любой диаграммы или блока в иерар- хии, используются номера диаграмм. Например, А21 является диаграммой, ко- торая детализирует блок 1 на диаграмме А2. Аналогично, А2 детализирует блок 2 на диаграмме А0, которая является самой верхней диаграммой модели. На ри- сунке 15 показано типичное дерево диаграмм. Рисунок 15 – Иерархия диаграмм Порядок разработки и описание программного модуляПри разработке программного модуля целесообразно придерживаться следующего порядка: изучение и проверка спецификации модуля, выбор языка программирова- ния; выбор алгоритма и структуры данных; программирование (кодирование) модуля; шлифовка текста модуля; проверка модуля; компиляция модуля. Первый шаг разработки программного модуля в значительной степени представляет собой смежный контроль структуры программы снизу: изучая спецификацию модуля, разработчик должен убедиться, что она ему понятна и достаточна для разработки этого модуля. В завершении этого шага выбирается язык программирования: хотя язык программирования может быть уже предопределен для всего ПС, все же в ряде случаев (если система программиро- вания это допускает)может быть выбран другой язык,более подходящий для реализации данного модуля (например, язык ассемблера). На втором шаге разработки программного модуля необходимо выяснить, не известны ли уже какие-либо алгоритмы для решения поставленной и или близкой к ней задачи. И если найдется подходящий алгоритм, то целесообразно им воспользоваться. Выбор подходящих структур данных, которые будут ис- пользоваться при выполнении модулем своих функций, в значительной степени предопределяет логику и качественные показатели разрабатываемого модуля, поэтому его следует рассматривать как весьма ответственное решение. На третьем шаге осуществляется построение текста модуля на выбранном языке программирования. Обилие всевозможных деталей, которые должны быть учтены при реализации функций, указанных в спецификации модуля, лег- ко могут привести к созданию весьма запутанного текста, содержащего массу ошибок и неточностей. Искать ошибки в таком модуле и вносить в него требуе- мые изменения может оказаться весьма трудоемкой задачей. Поэтому весьма важно для построения текста модуля пользоваться технологически обоснован- ной и практически проверенной дисциплиной программирования. Впервые на это обратил внимание Дейкстра, сформулировав и обосновав основные принци- пы структурного программирования. На этих принципах базируются многие дисциплины программирования, широко применяемые на практике. Следующий шаг разработки модуля связан с приведением текста модуля к завершенному виду в соответствии со спецификацией качества ПС. При про- граммировании модуля разработчик основное внимание уделяет правильности реализации функций модуля, оставляя недоработанными комментарии и допус- кая некоторые нарушения требований к стилю программы. При шлифовке тек- ста модуля он должен отредактировать имеющиеся в тексте комментарии и, возможно, включить в него дополнительные комментарии с целью обеспечить требуемое качества. С этой же целью производится редактирование текста про- граммы для выполнения стилистических требований. Шаг проверки модуля представляет собой ручную проверку внутренней логики модуля до начала его отладки (использующей выполнение его на компьютере), реализует общий принцип, сформулированный для обсуждаемой технологии программирования, о необходимости контроля принимаемых реше- ний на каждом этапе разработки ПС. И, наконец, последний шаг разработки модуля означает завершение про- верки модуля (с помощью компилятора) и переход к процессу отладки модуля. При программировании модуля следует иметь в виду, что программа должна быть понятной не только компьютеру, но и человеку: и разработчик мо- дуля, и лица, проверяющие модуль, и тестовики, готовящие тесты для отладки модуля,и сопроводители ПС,осуществляющие требуемые изменения модуля, вынуждены будут многократно разбирать логику работы модуля. В связи с этим Дейкстра и предложил строить программу как композицию из нескольких типов управляющих конструкций (структур), которые позволяют сильно повы- сить понимаемость логики работы программы. Программирование с использо- ванием только таких конструкций назвали структурным. Рисунок 16 – Основные управляющие конструкции структурного про- граммирования Основными конструкциями структурного программирования являются: следование,разветвление и повторение(рисунок16).Компонентами этих конструкций являются обобщенные операторы (узлы обработки) S, S1, S2 и условие (предикат) P. В качестве обобщенного оператора может быть либо про- стой оператор используемого языка программирования (операторы присваива- ния, ввода, вывода, обращения к процедуре), либо фрагмент программы, являю- щийся композицией основных управляющих конструкций структурного про- граммирования. Существенно, что каждая из этих конструкций имеет по управ- лению только один вход и один выход. Тем самым, и обобщенный оператор имеет только один вход и один выход. |