1.4. Подпрограммы
Другое решение задачи оформления варианта заключается в использовании для этой цели аппарата подпрограмм. Вариантный фрагмент извлекается из окружающего его текста и выносится в самостоятельный модуль. На его месте в исходном тексте записывается оператор вызова подпрограммы.
Вынесенный вариантный фрагмент превращают в подпрограмму, для этого к нему добавляется заголовок, содержащий имя подпрограммы и спецификации ее параметров. Новый вариантный фрагмент также оформляют в виде подпрограммы, расположенной в отдельном модуле. Тексты заголовков подпрограмм для старого и нового вариантных фрагментов совпадают (рис.1.2).
Рис. 1.2. Оформление варианта посредством подпрограмм.
а исходный текст, б отредактированный текст, в, г подпрограммы;
, вариантные части, совпадающие части
В последующей сборке любой конкретной версии выполняемой программы участвует только один из модулей-подпрограмм. Заметим, что обычно такое попеременное участие в сборке проще организовать, если сформированные сменные модули-подпрограммы допускают автономную трансляцию.
При описанном способе оформления варианта в программном фонде дублируются только заголовки формируемых подпрограмм. Размеры заголовков, вообще говоря, существенно меньше, чем размеры повторяющегося текста при размножении окрестности, рассмотренном в предыдущем разделе.
Однако с точки зрения безболезненности выполнения изменений текста применение подпрограмм значительно уступает размножению окрестности. Исходная версия программы здесь претерпевает значительные изменения: добавляются оператор вызова подпрограммы, заголовок подпрограммы и т. д. Работоспособность исходной версии сохранится лишь в случае, если все сопутствующие редактирующие действия будут выполнены безошибочно. Предположение о безусловной безошибочности редактирования нельзя отнести к реалистичным. Отсюда следует, что применение аппарата подпрограмм для оформления варианта критерию безболезненности не отвечает.
Не будет безболезненным и обратное преобразование текста программы, выполняемое при ликвидации вариантности. Объем и сложность редактирующих действий тут несколько меньше, но все же возможность внесения ошибки не исключается. Если, однако, не стремиться к строгому откату, восстанавливающему первоначальное состояние текста программы, и разрешить оставить в безвариантной программе как сформированную подпрограмму, так и обращение к ней, то ликвидация вариантности сведется к исключению утратившей силу подпрограммы и пройдет безболезненно.
Некоторое разнообразие привносит в рассмотренную схему объектно-ориентированная среда. Она позволяет, например, оформить исходную подпрограмму как метод некоторого суперкласса, а новую подпрограмму как переопределение этого метода в новом, специально созданном подклассе. Тогда, если при ликвидации вариантности остается в силе старая подпрограмма, можно ограничиться ликвидацией подкласса. Но если будет принято решение о предпочтительности новой подпрограммы, оставлять в окончательной версии программы и суперкласс, и подкласс, вероятно, уже не стоит. Более адекватным решением было бы симметричное оформление обеих подпрограмм в качестве двух равноправных реализаций виртуального метода класса, но и здесь при ликвидации вариантности, по-видимому, будет лучше, если от отработавшей виртуальности не останется никаких следов.
Впрочем, потребность в образовании или в расформировании подпрограммы (в частности, в превращении подвыражения в функцию и обратно) возникает не только при оформлении варианта. Эти же действия выполняются при решении многих других задач реформирования имеющегося исходного текста. Есть основания надеяться, что частота применения и полезность данных операций будут вскоре замечены разработчиками операционной среды, и в программистском обиходе появятся надлежащие средства их поддержки. Такие средства обеспечат, разумеется, и безболезненность выполняемых преобразований.
Применение подпрограмм несет в себе угрозу безболезненности только при оформлении первого или при ликвидации последнего варианта. Если хотя бы две вариантные подпрограммы уже так или иначе полностью оформлены, то подключение и отключение третьего и последующих вариантов проводятся безболезненно. Дело в том, что подключение варианта сводится здесь к добавлению, а отключение к ликвидации соответствующего сменного модуля-подпрограммы. При этом существующие исходные тексты никак не затрагиваются, а значит, остающиеся версии программы не могут потерять работоспособность.
Организация вариантных подпрограмм, равно как и размножение окрестности, сопряжены с известными сложностями, возникающими при подборе имен для вновь формируемых сменных модулей. Тут, к сожалению, не всегда работает традиционная чрезвычайно удобная схема именования, предписывающая присваивать модулю имя содержащейся в нем программы. Подпрограммы с вариантными телами (равно как и части программы в файлах-близнецах, порождаемых при размножении окрестности) для сохранения интерфейса с остальными частями программы должны иметь совпадающие имена. Соответствующие модули при традиционной схеме именования также получили бы совпадающие имена, и поэтому их нельзя было бы разместить в одном каталоге программного фонда.
Из этого затруднения возможны, вообще говоря, два выхода: либо размещать сменные модули в различных каталогах, либо отказаться от традиционной схемы именования. Первый из них влечет за собой появление иерархии каталогов и модулей, а это заметно осложняет ведение программного хозяйства.
Второй выход присваивать сменным модулям различные имена, тем самым позволяя им сосуществовать в рамках одного каталога. Он представляется более предпочтительным, хотя также имеет ряд недостатков. В частности, отказ от традиционной схемы означает определенное утяжеление механизма именования, что очевидно утяжелит и процедуры доступа к элементам программного фонда. Прежде чем говорить об остальных недостатках, поясним, как подбираются имена сменных модулей.
В качестве имени обычно используется идентификатор решения, влекущего за собой появление этого сменного модуля. Так, для сменных модулей доступа к таблице, упоминавшихся в разд. 1.1, подойдут имена ЛИНЕЙНЫЙ_ПОИСК и ХЕШИРОВАНИЕ. Или пусть, например, вариантным является использование в расчете численного метода Эйлера или Ньютона, и пусть вариант оформляется посредством подпрограммы. Тогда общим именем вариантных подпрограмм, которое записывается в операторе вызова и в заголовках, можно выбрать МЕТОД, а соответствующие сменные модули назвать ЭЙЛЕР и НЬЮТОН.
Приведенную схему именования сменных модулей, позволяющую размещать их в одном общем каталоге, далеко не всегда удается органично «вписать» в архитектуру существующих штатных средств программного обеспечения. При работе с вариантами весьма желательно, например, иметь возможность формулировать запросы вида: «Какие варианты реализации подпрограммы МЕТОД хранятся в программном фонде?». И оформление подобных запросов, и поиск ответа на них на базе штатных средств обычно весьма затруднительны. Дополнительные сложности возникают в связи с тем, что наряду с вариантной продолжает действовать и традиционная схема именования, а штатные средства не дают возможности отличить сменный модуль от обычного.
Упомянем и некоторые особенности работы с вариантными объектными модулями. Во-первых, тут может резко возрасти объем рутинных действий по перетрансляции после проведенного редактирования. Обычно операционная среда берет на себя отслеживание соответствия между исходным текстом и объектным модулем, автоматически вызывая перетрансляцию при изменении исходного текста. Если же операционная среда этого не делает, то при изменении дублируемого в сменных модулях (или в файлах-близнецах см. разд. 1.3) текста необходимо проследить не только за коррекцией всех существующих текстовых дублей, но и за их перетрансляцией.
Во-вторых, коллизия имен сменных модулей, преодоленная на уровне исходных текстов, может вновь заявить о себе на уровне объектных модулей. Если транслятор присваивает объектному модулю в качестве ключа не имя модуля, содержащего исходный текст, а имя расположенной в нем программы, то вариантные объектные модули (подобно сменным текстовым при традиционной схеме именования) не смогут сосуществовать в одном каталоге. Впрочем, проблему вариантных объектных модулей легко решить, слегка пожертвовав эффективностью: достаточно принять, что при каждом переходе к альтернативному варианту производится его перетрансляция.
И все же, несмотря на отмеченные недостатки, совсем отказаться от вариантных подпрограмм, вероятно, еще долго не удастся. Дело в том, что если вариантные фрагменты записываются на различных языках, то оформление их в виде подпрограмм может оказаться единственно возможным решением.
На практике разноязычные вариантные фрагменты встречаются достаточно часто. Например, нередко возникает желание переписать на ассемблере фрагмент программы, существенно влияющий на быстродействие. Исходный фрагмент, написанный на языке высокого уровня, в этом случае обычно имеет смысл сохранить для обеспечения мобильности.
Лишь немногие операционные среды допускают чередование в одном исходном тексте разноязычных фрагментов. В большинстве же сред формирование выполняемой программы из частей, написанных на различных языках, вообще говоря, допускается, но требуется, чтобы эти части представляли собой самостоятельные модули трансляции. Поскольку в распространенных алгоритмических языках подпрограмма является обычно минимальной автономно транслируемой единицей, у программиста временами просто нет другого выхода: разноязычные вариантные фрагменты могут быть оформлены только в виде подпрограмм.
|