Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги / Надежность программного обеспечения систем обработки данных

..pdf
Скачиваний:
8
Добавлен:
12.11.2023
Размер:
8.74 Mб
Скачать

Однако ни один из стандартов программирования не должен рассматриваться как абсолютное правило, кото рое не может быть нарушено. Назначением стандартов является прежде всего создание основ для некоторых приемов работы, с тем чтобы любое предлагаемое откло­ нение могло быть тщательно исследовано для определе­ ния, оправдано ли оно. Не имеет смысла перечислять воз­ можный набор стандартов, так как он достаточно велик, и, кроме того, большинство организаций имеет свой, кон­ кретный набор, соответствующий их среде. Однако для понимания проблемы можно сослаться на некоторые наи­ более важные положения, относящиеся к методологии стандартов: форматы документации; режимы компиляции; соглашения по поводу записи комментария; характеристи­ ки языка программирования, которые необходимо избе­ гать.

7.2. ПРОЕКТИРОВАНИЕ И КОДИРОВАНИЕ МОДУЛЕЙ

Ранее подчеркивалось, что завершающими процессами разработки являются внешнее проектирование и логичес­ кое проектирование модулей. Рассмотрим содержание этих процессов.

Внешнее проектирование модулей. Начальным шагом в проектировании модуля является определение его внешних характеристик. Эта информация содержится во внешней спецификации модуля, которая включает все данные, необ­ ходимые для модулей, вызывающих данный модуль. В особенности необходимо отметить, что внешняя специфи­ кация модуля не должна содержать указаний о внутрен­ нем представлении данных или о логике модуля. Кроме того, недопустимо, чтобы спецификация содержала какиелибо ссылки на вызывающие модули или на контексты, в которых данный модуль используется. Допустйм сле­ дующий набор информации внешней спецификации:

L Имя модуля. Имеется в виду имя, используемое для вызова модуля. Для модуля с несколькими точками входа это имя является именем точки входа. Для каждой точки входа существует своя отдельная спецификация.

2.Функция. Формулируется определение функции или функций, выполняемых модулем. Этот элемент специфи­ кации не должен описывать логику модуля или контекст,

вкотором модуль используется.

3.Список параметров. Определяется количество и по­ рядок параметров, передаваемых модулю.

Г41

4 Вход Это точное описание всех входных параметров модуля Оно включает в себя определение формата, размера, признаков, единицы измерения и действительные области всех входных данных.

5. Выход. Это точное описание всех выходных данных возвращаемых модулем. Оно должно включать определе­ ние формата, размера, признаков, единиц измерения и действительных областей всех выходных данных. Описа­ ние должно привязывать все выходные данные к входным способом «причина—следствие», т. е. какие выходные дан­ ные генерируются из конкретных входных данных. Выход­ ные данные, генерируемые модулем, когда входные данные недействительны (неверны), также должны быть опреде­ лены Для модуля, который должен рассматриваться как полностью специфицированный, его поведение прн всех условиях ввода должно быть точно выражено

6 Внешние действия Описание некоторых действий, принятых как внешние по отношению к программе или системе, где этот модуль вызывается. Примерами внеш­ них действий являются печать сообщений, чтение коман­ ды, чтение вспомогательного файла (файла сообщений), выдача сообщений об ошибке. Внешние действия модуля могут включать в себя некоторые внешние действия под­ чиненных модулей. Например, если модуль А вызывает модуль В, а В печатает сообщение, то описания внешних действий появятся во внешних спецификациях как модуля А, так и модуля В.

Необходимо отличать внешнюю спецификацию модуля от другой документации, такой, как описание логики мо­ дуля, так как логика модуля может изменяться без воз­ действия вызывающих модулей, а изменение внешних спецификаций модуля обычно приводит к изменениям в вызывающих модулях. Внешняя спецификация модуля может принимать ряд физических форм в зависимости от включения шести перечисленных выше категорий.

П р о е к т и р о в а н и е л о г и к и м о д у л е й . Конечным процессом

в последовательности работ по проектированию ПО явля­ ется проектирование и кодирование логики каждого моду­ ли. Слишком часто на завершающем этапе проектирова­ ния под воздействием ограничений графика проведения работ исключается тщательное планирование разработки. В результате модуль разрабатывается без достаточной подготовки случайным способом. В целях обеспечения надежности создаваемого продукта ПО процесс проекти­ рования модуля должен быть тщательно спланирован.

142

Покажем систему шагов, обеспечивающих дисциплиниро­ ванный подход к проектированию модуля.

Ш а т 1. Выбор языка программирования. Выбор языка программирования обычно диктуется требованиями заказ­ чика, имеющимися ресурсами или принятыми организа­ ционными стандартами. Выбор языка программирования производится в период определения частоты применения проектируемого продукта ПО. Существенное влияние на выбор языка оказывают его возможности обеспечивать надежный процесс получения программ, наличие и спе­ цифические особенности компилятора, наличие тестовых средств и т д

Ша г 2 Проектирование внешних спецификаций модуг ля Этот процесс рассмотрен в предыдущем разделе.

Ша г 3 Проверка внешних спецификаций модуля. Же­ лательно, чтобы спецификация для каждого модуля была бы проверена путем сравнения ее с информацией интерг фейса, разработанного при проектировании структуры программы Спецификация данного модуля должна быть просмотрена программистами всех модулей, выбывающих данный модуль.

Ша г 4. Выбор алгоритма и структуры данных. Одним из принципиальных решений в процессе логического проек­ тирования является выбор алгоритма и соответствующих структур данных. К настоящему времени разработано зна­ чительное количество алгоритмов и имеется определенная возможность удовлетворить потребности проектировщи­ ков Считается, что, прежде чем принять решение на разработку алгоритма, лучше сосредоточить внимание иа поиске уже известных. Если же поиск не приводит к жела­ тельному результату, то алгоритм необходимо разрабо­ тать

Обычно проектировщик может обнаружить несколькф функционально эквивалентных алгоритмов и структур данных и сделать среди иих выбор. Так как многие совре­

менные вычислительные системы имеют многоуровневую память (основную память, виртуальную память, высоко­ скоростные буферы памяти), тенденцией для эффективно мыслящего программиста является возврат к простым ал­ горитмам и структурам данных (например, в системе с многоуровневой памятью алгоритм двоичного поиска может не быть ощутимо быстрее, чем простое сканиро­ вание). Это один из достаточно редких примеров, где эффективность и надежность становятся совместимыми целями

Ш аг 5 Запись начальных и конечных операторов

предусматривает проведение оформления модуля в соот­ ветствии с требованиями принятого языка программиро­ вания.

Ша г 6. Объявление всех данных интерфейса. Состоит

взаписи программных операторов, определяющих или объявляющих все переменные в интерфейсе модуля.

Ша г 7. Объявление оставшихся данных. Предусмат­ ривается запись операторов программы, определяющих или описывающих все оставшиеся переменные, которые

должны быть использованы в модуле. Из-за трудностей предсказания всех необходимых переменных этот шаг час­ то пересекается с последующими шагами.

Ш а г 8. Усовершенствование кода. Используется ите­ ративный процесс последовательного усовершенствования логики модуля, начиная с абстрактного определения ло­ гики и заканчивая разработкой кода для модуля. Алгоритм проведения работы на данном шаге базируется, как пра­ вило, на элементах структурного программирования.

Ш аг 9. Просмотр кода с целью упрощения понимания.

Осуществляется с целью обеспечения простоты чтения, доступности и упрощения. Для ответа на вопросы, которые могут возникнуть у читателя кода, записываются допол­ нительные комментарии. Эта работа производится на осно­ ве принципов стиля программирования.

Шаг 10. Проверка кода. Выполняется ручная про­ верка модуля на корректность. Процедуры и последо­ вательность выполнения работы рассматриваются в оче­ редном разделе книги.

Шаг 11. Компиляция модуля. Работа над созданием модуля в определенном смысле завершается компиляцией, но это уже и следующий этап разработки — начало тести­ рования модуля, так как компилятор проведет контроль синтаксиса и частично семантики исходной программы.

При проектировании логики модуля необходимо предусмотреть совокупность контрольных мероприятий, обеспечивающих функционирование модуля. Совокуп­ ность контрольных мероприятий отождествляется с приемами защитного программирования. Один из принци­ пов защитного программирования состоит в том, что проектировщик обязан предусмотреть контроль правиль­ ности входной информации модуля до начала его работы. Действительно, прием неверных входных данных может привести к получению и выдаче неверных, но правдопо­ добных выходных данных.

144

Для разрешения этой проблемы алгоритм модуля до­ полняется схемами тестирования по соответствующим признакам, областям действия, полноте и приемлемости. Выбор необходимых схем производится путем просмотра кода модуля с целью выяснения всех возможных предпо­ ложений, касающихся входных данных, которые имеются в проектируемом модуле, с последующим рассмотрением этих проверок каждый раз, когда вызывается модуль. Аналогичные действия должны быть предприняты и по выходным данным модуля.

Реализация концепций защитного программирования требует проведения тщательного анализа необходимости выполнения работ, так как это может привести к нежела­ тельным последствиям в смысле действенности контроля и надежности функционирования защищаемого объекта.

Результатом анализа может быть некоторый список желательных проверок. Для каждого элемента списка необходимо оценить сложность ожидаемой проверки, ве­ роятность того, что проверяемый объект может быть не­ верным, и возможные последствия для случая, когда про­ верка не будет выполнена. Оставшимся шагом является определение минимального объема защитного кода, кото­ рый будет обеспечивать защиту от возможных ошибок.

Использование языков высокого уровня. Языки прог­ раммирования оказывают существенное влияние на на­ дежность программного обеспечения. Аргументы против кодирования на ассемблерном (машинном) языке настоль­ ко убедительны, что трудно найти обоснованное решение на применение машинного языка, за исключением двух ситуаций: первоначальный компилятор для новой вычис­ лительной системы и небольшое ядро каждой операцион­ ной системы. Первое — в силу того, что просто иет инстру­ мента трансляции. Второе — не для эффективности, а вследствие того, что некоторые особенности архитектуры, свойственные машине, не будут отражены в используемых языках программирования.

Аргументы против использования машинного языка могут быть усилены следующими замечаниями:

1)при использовании машинного языка программист проводит значительное время не над решением проблемы,

анад освоением деталей эффективной реализации прог­ раммы;

2)значительное количество ошибок кодирования при замене машинного языка языком программирования вы сокого уровня исключается из рассмотрения;

145

3) производительность труда программиста при ис пользовании языков программирования высокого уровня значительно выше, чем в случае использования машинного языка, так как любая функция кодируется несколькими операторами языка программирования высокого уровня.

Программы иа языках высокого уровня обычно более просты для понимания, легко изменяются, при наличии соответствующего транслятора могут самодокументироваться, и, кроме того, они обладают лучшей адаптацией

ксовместимости и переносимости программ. Программы на языке высокого уровня имеют меньшую стоимость, несмотря на то, что работа выполняется применительно

ктарифной ставке для этого языка.

Вопрос эффективности разрабатываемых программ ие решается однозначно в пользу кодирования на машинном языке, так как имеет место достаточно сложная зависи­ мость от того, как «обучен» оптимизации компилятор с языка высокого уровня, каков опыт программиста в ко­ дировании программ, степень знания им языка и другие факторы. Кроме того, необходмо помнить, что эффектиВ' ность достигается посредством рационального выбора ал­ горитмов и структур данных, а не за счет микроэффектив­ ности, получаемой простым использованием машинного языка.

Вне зависимости от применяемого языка программи­ рования проблемы надежности продукта ПО определяют­ ся отношением программиста к выполняемой работе. Здесь прежде всего необходимо акцентировать внимание на точ­ ности выполнения работы. Действительно, программиро­ вание не обладает понятием допуска, известного в техни­ ческих системах: здесь программа модуля либо верна, либо не верна. Опытный программист должен стремиться к тому, чтобы его программа компилировалась верно с пер­ вого раза. Этого положения можно достичь при условии, что для устранения синтаксических ошибок написанная программа будет внимательно прочитана другим програм­ мистом до компиляции. Здесь существенны следующие утверждения:

1)одной из причин появления синтаксических оши­ бок в программе является недопонимание синтаксиса языка. Если иет четкого понимания синтаксиса, то ве­ роятно, что и семантика языка не усвоена в достаточной мере;

2)причиной появления синтаксических ошибок явля­ ется недостаточная аккуратность при написании прог-

146

рамм Это отрицательное свойство в равной степени влияет на синтаксис и на семантику программы,

3) высока вероятность того, что компилятор может не обнаружить отдельные синтаксические и особенно семан­ тические ошибки,

' 4) перед компиляцией программист проводит значи­ тельное время, размышляя над каждым изменением и усовершенствованием. После первой компиляции (как правило, неудачной) программист стремится быстрее при­ вести в порядок программу с тем, чтобы она работала (компилировалась). Изменения, сделанные в спешке, приводят к множеству новых ошибок.

Проверка. Заключительным шагом в процессе проек­ тирования и кодирования модуля является проверка его логики. Проверка перед действительным выполнением программ на машине выполняется людьми. Очередной раздел будет посвяшеи тестированию, т. е. проверке логи­ ки при помощи выполнения программы на машине.

Проверка без машины основывается на различных формах чтения кода. Чтение кода программы может произ­ водиться несколькими людьми, один из которых является ответственным за выполняемую работу. Цель этой рабо­ ты — обнаружение ошибок в модуле, но не их корректи­ ровка. Достаточно сложно представить список специфи­ ческих элементов для проверки любой программы. Све­ дения, представленные в гл. 3, могут в значительной мере помочь выполнению этой работы.

* Другим подходом к проверке программы модуля без машины является прогон тестовых вариантов с прослежи­ ванием хода «выполненняэ программы. Тестовые варианты должны быть простыми и в ограниченном количестве, с тем чтобы избежать громоздкого ручного процесса про­ крутки. Подборку тестовых вариантов целесообразно по­ ручить разработчику модуля (или модулей), вызывающего данный тестируемый модуль. Разработчик тестируемого модуля в процессе прокрутки дает разъяснения логики модуля. Этот контакт позволяет не только проверить ло­ гику модуля, но и получить список дополнительных проб­ лем, включающих плохую практику программирования, неэффективность приемов реализации отдельных частей или алгоритма в целом и другие рекомендации.

После завершения прокрутки разработчик модуля вно­ сит все необходимые коррективы. Если при подобной проверке внесены существенные замечания, то модуль должен быть снова проверен по тестовым вариантам.

U 7

Изложенный подход к проверке разработанного модуля оказывает большое влияние на общую подготовку членов проверочной группы, обеспечивая «подтягивание» не имеющих достаточного опыта программирования до уров­ ня передовых разработчиков.

Третья разновидность чтения кода программы должна проводиться после компиляции на основе полученной до­ кументации. Характер этой проверки определяется в основ­ ном набором и содержанием документации, выдаваемой компилятором. Особенно важной является проверка пра­ вильности интерпретации компилятором намерений прог­ раммиста по объявляемым данным.

Наиболее совершенной формой чтения кода модуля является доказательство корректировки программ на базе средств автоматизации. Существующее состояние теории доказательства программ обеспечивает выполнение такой работы только применительно к небольшим по объему программам

7.3. СТИЛЬ ПРОГРАММИРОВАНИЯ

Проектирование программы должно ответить на вопро­ сы стиля программирования, эффективности, тестирова­ ния, отладки и сопровождения. Попытки решения этих вопросов являются важной частью разработки любого продукта ПО. Однако в проектировании ПО это как раз именно та область, где рекомендации для одного проекта или программиста не всегда подходят для других прог­ раммистов или проектов. Один из очевидных принципов лучшего построения программы состоит в том, чтобы «проектировать до программирования».

Небольшие программы не предполагают таких слож­ ностей построения, как большие программы, т. е. неболь­ шие программы могут быть легко поняты и составлены одним человеком. Однако вряд ли можно рассчитывать на разумное составление программы, которая так сложна, что не может быть понята и «охвачена» одним человеком. Этой проблеме посвящено достаточно большое количество работ, однако в то время, когда необходимо строить большие программы, все мы оказываемся в определенной мере начинающими.

Простота и ясность программы. Простота и ясность в построении программ являются первым фактором, обеспе­ чивающим написание легко читаемой программы. Прог­ раммирование должно быть простым, так как сложная

148

логика или мудрое программирование станут очень доро­ гостоящими при отладке и модификации. Необычные прие­ мы программирования, использующие преимущества скрытых машинно-зависимых возможностей, чаще ме­ шают организации тестирования программ и являются ромехой при последующей эксплуатации программы опе­ ратором

Целью программиста должно быть написание прог­ раммы для людей, а не для машин. Это требование должно выполняться с учетом факторов ясности, простоты и чи­ таемости программы даже в ущерб таким показателям, как длина программы и ее машинная эффективность. Приводимый ниже перечень советов оказывает значитель­ ное влияние на ясность и простоту программы:

1)используйте значащие имена переменных;

2)не пользуйтесь в качестве идентификаторов ключе­ выми словами языка программирования;

3)избегайте использования промежуточных перемен­

ных там, где без них можно без ущерба обойтись;

4)применяйте круглые скобки для устранения дву­ смысленностей в программе;

5)записывайте только один оператор в строке бланка;

6)не изменяйте значения управляющей переменной в теле цикла, так как это способствует усложнению читае­

мости программы; 7) избегайте меток операторов, если в этом нет необ­

ходимости.

Другим подходом к улучшению простоты и ясности программы является использование одних и тех же мето­ дов программирования не только применительно к одному программисту, но и к группе программирования в целом.

Еще одним важным фактором стиля программиро­ вания является то, как программист пользуется особен­ ностями языка программирования. Главное здесь заклю­ чается в понимании и использовании всех возможностей языка, исключении неудачных конструкций, особенностей и машинно-зависимых элементов. Можно привести ряд полезных, на наш взгляд, советов:

1) изучайте и используйте прямые возможности языка программирования;

2)избегайте неясности и противоречий, допускаемых

вязыке;

3)изучайте и используйте библиотечные и встроенные функции;

4)не игнорируйте предупредительные сообщения транслятора;

149

5)перед началом программирования прочитайте ещ

раз главу, посвященную ошибкам программирования. В ы б о р а л г о р и т м а . Одним из худших нарушений стиля программирования является желание «улучшить програм­ му с целью повышения эффективности» до завершения ее отладки. Эти попытки не только снижают простоту, ясность, надежность н читаемость программы, но и не дают, как правило, серьезных результатов увеличения

эффективности.

Любой программист, серьезно занимающийся систем­ ными вопросами, знает, что эффективность обработки ин­ формации достигается тщательным выбором алгоритма реализации, структур обрабатываемых данных и исполь- ,зуемых ресурсов, а не исключением отдельных вычисле­ ний индекса, заменой возведения в степень на умножение

И т. д.

Можно сформулировать ряд советов, способствующих получению удовлетворительных характеристик программ,:

1) не улучшайте программу, пока она не будет окон­ чательно проверена. Нецелесообразно заботиться о ско­ рости работы программы в то время, когда она еще не функционирует должным образом. Быстрая, но неправиль­ ная программа бесполезна, правильной, но медленно работающей программой можно пользоваться;

2)не приносите читаемость программы в жертву эф­ фективности, так как впереди еще тестирование и сопро­ вождение неудачно разработанного продукта,

3)не оптимизируйте без необходимости. Однако если уже это так необходимо, то поручите эту работу опти­ мизирующему компилятору;

4)наиболее значительные результаты по эффектив­ ности могут быть получены за счет выбора алгоритма, структур данных и используемых ресурсов, т. е. за сче]-

средств макроэффективности,

,

5) в сложных системах простейшие алгоритмы обычно

оказываются самыми быстрыми.

не должен

Практика показывает, что программист

бояться переделывать (переписывать) программу Напи­ сание программы рассматривается как творческая задача и, как и другие формы творчества, часто не заканчивает­ ся одним наброском. Чаще только в ходе написания программы становится ясным, что выбранный путь не является лучшим. Своевременно и обоснованно принятое решение на переписывание поможет создать ясную и эффективную программу, допускающую проведение от­

ладки

1

4 5 0

Соседние файлы в папке книги