Наиболее распространенными и применимыми являются: метод вос-ходящей разработки и метод нисходящей разработки ПС. Метод восходящей разработки заключается в следующем. Сначала строится модульная структура программы в виде дерева. Затем поочередно программируются модули программы, начиная с модулей самого нижнего уровня (листья дерева модульной структуры программы), в таком порядке, чтобы для каждого программируемого модуля были уже запрограммированы все модули, к которым он может обращаться. После того, как все модули программы запрограммированы, производится их поочередное тестирование и отладка в принципе в таком же (восходящем) порядке, в каком велось их программирование. Такой порядок разработки программы на первый взгляд кажется вполне естественным: каждый модуль при программировании выра-жается через уже запрограммированные непосредственно подчиненные мо-дули, а при тестировании использует уже отлаженные модули. Однако, со-временная технология не рекомендует такой порядок разработки программы. Во-первых, для программирования какого-либо модуля совсем не требуется наличия текстов используемых им модулей - для этого достаточно, чтобы каждый используемый модуль был лишь специфицирован (в объеме, позво-ляющем построить правильное обращение к нему), а для тестирования его возможно (и даже, как мы покажем ниже, полезно) используемые модули за-менять их имитаторами (заглушками, драйверами). Во-вторых, каждая про-грамма в какой-то степени подчиняется некоторым внутренним для нее, но глобальным для ее модулей соображениям (принципам реализации, предпо-ложениям, структурам данных и т.п.), что определяет ее концептуальную це-лостность и формируется в процессе ее разработки. При восходящей разра-ботке эта глобальная информация для модулей нижних уровней еще не ясна в полном объеме, поэтому очень часто приходится их перепрограммировать, когда при программировании других модулей производится существенное уточнение этой глобальной информации (например, изменяется глобальная структура данных). В-третьих, при восходящем тестировании для каждого модуля (кроме головного) приходится создавать ведущую программу (мо-дуль), которая должна подготовить для тестируемого модуля необходимое состояние информационной среды и произвести требуемое обращение к нему. Это приводит к большому объему «отладочного» программирования и в то же время не дает никакой гарантии, что тестирование модулей производи-лось именно в тех условиях, в которых они будут выполняться в рабочей программе. Метод нисходящей разработки заключается в следующем. Как и в предыдущем методе сначала строится модульная структура программы в ви-де дерева. Затем поочередно программируются модули программы, начиная с модуля самого верхнего уровня (головного), переходя к программированию какого-либо другого модуля только в том случае, если уже запрограммиро-ван модуль, который к нему обращается. После того, как все модули про-граммы запрограммированы, производится их поочередное тестирование и отладка в таком же (нисходящем) порядке. При этом первым тестируется го-ловной модуль программы, который представляет всю тестируемую про-грамму и поэтому тестируется при «естественном» состоянии информацион-ной среды, при котором начинает выполняться эта программа. При этом те модули, к которым может обращаться головной, заменяются их имитаторами (так называемыми заглушками). Каждый имитатор модуля представляется весьма простым программным фрагментом, который, в основном, сигнализи-рует о самом факте обращения к имитируемому модулю, производит необхо-димую для правильной работы программы обработку значений его входных параметров (иногда с их распечаткой) и выдает, если это необходимо, зара-нее запасенный подходящий результат. После завершения тестирования и отладки головного и любого последующего модуля производится переход к тестированию одного из модулей, которые в данный момент представлены имитаторами, если таковые имеются. Для этого имитатор выбранного для тестирования модуля заменяется самим этим модулем и, кроме того, добав-ляются имитаторы тех модулей, к которым может обращаться выбранный для тестирования модуль. При этом каждый такой модуль будет тестировать-ся при «естественных» состояниях информационной среды, возникающих к моменту обращения к этому модулю при выполнении тестируемой програм-мы. Таким образом, большой объем «отладочного» программирования при восходящем тестировании заменяется программированием достаточно про-стых имитаторов используемых в программе модулей. Кроме того, имитато-ры удобно использовать для того, чтобы подыгрывать процессу подбора тес-тов путем задания нужных результатов, выдаваемых имитаторами. При та-ком порядке разработки программы вся необходимая глобальная информация формируется своевременно, т.е. ликвидируется весьма неприятный источник просчетов при программировании модулей. Некоторым недостатком нисхо-дящей разработки, приводящим к определенным затруднениям при ее при-менении, является необходимость абстрагироваться от базовых возможно-стей используемого языка программирования, выдумывая абстрактные опе-рации, которые позже нужно будет реализовать с помощью выделенных в программе модулей. Однако способность к таким абстракциям представляет-ся необходимым условием разработки больших программных средств, по-этому ее нужно развивать. Особенностью рассмотренных методов восходящей и нисходящей разработок (которые мы будем называть классическими) является требование, чтобы модульная структура программы была разработана до начала про-граммирования (кодирования) модулей. Это требование находится в полном соответствии с водопадным подходом к разработке ПС, так как разработка модульной структуры программы и ее кодирование производятся на разных этапах разработки ПС: первая завершает этап конструирования ПС, а второе - открывает этап кодирования. Однако эти методы вызывают ряд возражений: представляется сомнительным, чтобы до программирования модулей можно было разработать структуру программы достаточно точно и содержательно. На самом деле это делать не обязательно, если несколько модернизировать водопадный подход. Ниже предлагаются конструктивный и архитектурный подходы к разработке программ, в которых модульная структура формирует-ся в процессе программирования (кодирования) модулей. Конструктивный подход к разработке программы представляет собой модификацию нисходящей разработки, при которой модульная древовидная структура программы формируется в процессе программирования модулей. Разработка программы при конструктивном подходе начинается с програм-мирования головного модуля, исходя из спецификации программы в целом. При этом спецификация программы принимается в качестве спецификации ее головного модуля, который полностью берет на себя ответственность за выполнение функций программы. В процессе программирования головного модуля, в случае, если эта программа достаточно большая, выделяются под-задачи (внутренние функции), в терминах которых программируется голов-ной модуль. Это означает, что для каждой выделяемой подзадачи (функции) создается спецификация реализующего ее фрагмента программы, который в дальнейшем может быть представлен некоторым поддеревом модулей. Важ-но заметить, что здесь также ответственность за выполнение выделенной функции несет головной (может быть, и единственный) модуль этого подде-рева, так что спецификация выделенной функции является одновременно и спецификацией головного модуля этого поддерева. В головном модуле про-граммы для обращения к выделенной функции строится обращение к голов-ному модулю указанного поддерева в соответствии с созданной его специфи-кацией. Таким образом, на первом шаге разработки программы (при про-граммировании ее головного модуля) формируется верхняя начальная часть дерева. Аналогичные действия производятся при программировании любого другого модуля, который выбирается из текущего состояния дерева про-граммы из числа специфицированных, но пока еще не запрограммированных модулей. В результате этого производится очередное деформирование дерева программы. Архитектурный подход к разработке программы представляет собой модификацию восходящей разработки, при которой модульная структура программы формируется в процессе программирования модуля. Но при этом ставится существенно другая цель разработки: повышение уровня исполь-зуемого языка программирования, а не разработка конкретной программы. Это означает, что для заданной предметной области выделяются типичные функции, каждая из которых может использоваться при решении разных за-дач в этой области, и специфицируются, а затем и программируются отдель-ные программные модули, выполняющие эти функции. Так как процесс вы-деления таких функций связан с накоплением и обобщением опыта решения задач в заданной предметной области, то обычно сначала выделяются и реа-лизуются отдельными модулями более простые функции, а затем постепенно появляются модули, использующие ранее выделенные функции. Такой на-бор модулей создается в расчете на то, что при разработке той или иной про-граммы заданной предметной области в рамках конструктивного подхода могут оказаться приемлемыми некоторые из этих модулей. Это позволяет существенно сократить трудозатраты на разработку конкретной программы путем подключения к ней заранее заготовленных и проверенных на практике модульных структур нижнего уровня. Так как такие структуры могут много-кратно использоваться в разных конкретных программах, то архитектурный подход может рассматриваться как путь борьбы с дублированием в програм-мировании. В связи с этим программные модули, создаваемые в рамках ар-хитектурного подхода, обычно параметризуются для того, чтобы усилить применимость таких модулей путем настройки их на параметры. В классическом методе нисходящей разработки рекомендуется снача-ла все модули разрабатываемой программы запрограммировать, а уж затем начинать нисходящее их тестирование, что опять-таки находится в полном соответствии с водопадным подходом. Однако такой порядок разработки не представляется достаточно обоснованным: тестирование и отладка модулей может привести к изменению спецификации подчиненных модулей и даже к изменению самой модульной структуры программы, так что в этом случае программирование некоторых модулей может оказаться бесполезно проде-ланной работой. Нам представляется более рациональным другой порядок разработки программы, известный в литературе как метод нисходящей реа-лизации, что представляет некоторую модификацию водопадного подхода. В этом методе каждый запрограммированный модуль начинают сразу же тестировать до перехода к программированию другого модуля. Все эти методы имеют еще различные разновидности в зависимости от того, в какой последовательности обходятся узлы (модули) древовидной структуры программы в процессе ее разработки. Это можно делать, напри-мер, по слоям (разрабатывая все модули одного уровня, прежде чем перехо-дить к следующему уровню). При нисходящей разработке дерево можно об-ходить также в лексикографическом порядке (сверху вниз, слева направо). Возможны и другие варианты обхода дерева. Так, при конструктивной реа-лизации для обхода дерева программы целесообразно следовать идеям Фуксмана, которые он использовал в предложенном им методе вертикально-го слоения. Сущность такого обхода заключается в следующем. В рамках конст-руктивного подхода сначала реализуются только те модули, которые необ-ходимы для самого простейшего варианта программы, которая может нор-мально выполняться только для весьма ограниченного множества наборов входных данных, но для таких данных эта задача будет решаться до конца. Вместо других модулей, на которые в такой программе имеются ссылки, в эту программу вставляются лишь их имитаторы, обеспечивающие, в основ-ном, сигнализацию о выходе за пределы этого частного случая. Затем к этой программе добавляются реализации некоторых других модулей (в частности, вместо некоторых из имеющихся имитаторов), обеспечивающих нормальное выполнение для некоторых других наборов входных данных. И этот процесс продолжается поэтапно до полной реализации требуемой программы. Таким образом, обход дерева программы производится с целью крат-чайшим путем реализовать тот или иной вариант (сначала самый простейший) нормально действующей программы. В связи с этим такая разновидность конструктивной реализации получила название метода целенаправленной конструктивной реализации. Достоинством этого метода является то, что уже на достаточно ранней стадии создается работающий вариант разрабаты-ваемой программы. Психологически это играет роль допинга, резко повы-шающего эффективность разработчика. Поэтому этот метод является весьма привлекательным.