- •ПРЕДИСЛОВИЕ
- •ВВЕДЕНИЕ
- •1.1. Технология программирования и основные этапы ее развития
- •1.2. Проблемы разработки сложных программных систем
- •1.3. Блочно-иерархический подход к созданию сложных систем
- •1.4. Жизненный цикл и этапы разработки программного обеспечения
- •1.5. Эволюция моделей жизненного цикла программного обеспечения
- •1.7. Оценка качества процессов создания программного обеспечения
- •2. ПРИЕМЫ ОБЕСПЕЧЕНИЯ ТЕХНОЛОГИЧНОСТИ ПРОГРАММНЫХ ПРОДУКТОВ
- •2.1. Понятие технологичности программного обеспечения
- •2.2. Модули и их свойства
- •2.3. Нисходящая и восходящая разработка программного обеспечения
- •2.4. Структурное и «неструктурное» программирование. Средства описания структурных алгоритмов
- •2.5. Стиль оформления программы
- •2.6. Эффективность и технологичность
- •2.7. Программирование «с защитой от ошибок»
- •2.8. Сквозной структурный контроль
- •3.1. Классификация программных продуктов по функциональному признаку
- •3.3. Предпроектные исследования предметной области
- •3.4. Разработка технического задания
- •3.5. Принципиальные решения начальных этапов проектирования
- •4. АНАЛИЗ ТРЕБОВАНИЙ И ОПРЕДЕЛЕНИЕ СПЕЦИФИКАЦИЙ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ПРИ СТРУКТУРНОМ ПОДХОДЕ
- •4.1. Спецификации программного обеспечения при структурном подходе
- •4.2. Диаграммы переходов состояний
- •4.3. Функциональные диаграммы
- •4.4. Диаграммы потоков данных
- •4.5. Структуры данных и диаграммы отношений компонентов данных
- •4.6. Математические модели задач, разработка или выбор методов решения
- •5. ПРОЕКТИРОВАНИЕ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ПРИ СТРУКТУРНОМ ПОДХОДЕ
- •5.1. Разработка структурной и функциональной схем
- •5.2. Использование метода пошаговой детализации для проектирования структуры программного обеспечения
- •5.3. Структурные карты Константайна
- •5.4. Проектирование структур данных
- •5.5. Проектирование программного обеспечения, основанное на декомпозиции данных
- •6. АНАЛИЗ ТРЕБОВАНИЙ И ОПРЕДЕЛЕНИЕ СПЕЦИФИКАЦИЙ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ПРИ ОБЪЕКТНОМ ПОДХОДЕ
- •6.1. UML - стандартный язык описания разработки программных продуктов с использованием объектного подхода
- •6.2. Определение «вариантов использования»
- •6.3. Построение концептуальной модели предметной области
- •6.4. Описание поведения. Системные события и операции
- •7. ПРОЕКТИРОВАНИЕ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ПРИ ОБЪЕКТНОМ ПОДХОДЕ
- •7.1. Разработка структуры программного обеспечения при объектом подхода
- •7.2. Определение отношений между объектами
- •7.3. Уточнение отношений классов
- •7.4. Проектирование классов
- •7.5. Компоновка программных компонентов
- •7.6. Проектирование размещения программных компонентов для распределенных программных систем
- •7.7. Особенность спиральной модели разработки. Реорганизация проекта
- •8. РАЗРАБОТКА ПОЛЬЗОВАТЕЛЬСКИХ ИНТЕРФЕЙСОВ
- •8.1. Типы пользовательских интерфейсов и этапы их разработки
- •8.2. Психофизические особенности человека, связанные с восприятием, запоминанием и обработкой информации
- •8.3. Пользовательская в программная модели интерфейса
- •8.4. Классификации диалогов и общие принципы их разработки
- •8.5. Основные компоненты графических пользовательских интерфейсов
- •8.6. Реализация диалогов в графическом пользовательском интерфейсе
- •8.8. Интеллектуальные элементы пользовательских интерфейсов
- •9. ТЕСТИРОВАНИЕ ПРОГРАММНЫХ ПРОДУКТОВ
- •9.1. Виды контроля качества разрабатываемого программного обеспечения
- •9.2. Ручной контроль программного обеспечения
- •9.3. Структурное тестирование
- •9.4. Функциональное тестирование
- •9.5. Тестирования модулей и комплексное тестирование
- •9.6. Оценочное тестирование
- •10. ОТЛАДКА ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ
- •10.1. Классификация ошибок
- •10.2. Методы отладки программного обеспечения
- •10.3. Методы и средства получения дополнительной информации
- •10.4. Общая методика отладки программного обеспечения
- •11. СОСТАВЛЕНИЕ ПРОГРАММНОЙ ДОКУМЕНТАЦИИ
- •11.1. Виды программных документов
- •11.2. Пояснительная записка
- •11.3. Руководство пользователя
- •11.4. Руководство системного программиста
- •11.5. Основные правила оформления программной документации
- •11.6. Правила оформления расчетно-пояснительных записок при курсовом проектировании
- •СПИСОК ЛИТЕРАТУРЫ
2.5.Стиль оформления программы
Сточки зрения технологичности хорошим считают стиль оформления программы, облегчающий ее восприятие как самим автором, так и другими программистами, которым, возможно, придется ее проверять или модифицировать. «Помните, программы читаются людьми», призывал Д. Ван Тассел, автор одной из известных монографий, посвященной проблемам программирования [60].
Именно исходя из того, что любую программу неоднократно придется просматривать, следует придерживаться хорошего стиля написания программ.
Стиль оформления программы включает:
•правила именования объектов программы (переменных, функций, типов, данных и т. п.);
•правила оформления модулей;
•стиль оформления текстов модулей.
Правила именования объектов программы. При выборе имен программных объектов следует придерживаться следующих правил:
• имя объекта должно соответствовать его содержанию, например:
MaxItem - максимальный элемент;
NextItem - следующий элемент;
• если позволяет язык программирования, можно использовать символ «_» для визуального разделения имен, состоящих из нескольких слов, например:
Max_Item, Next_Itetm;
необходимо избегать близких по написанию имен, например:
Index и InDec.
Правила оформления модулей. Каждый модуль должен предваряться заголовком, который, как минимум, содержит:
•название модуля;
•краткое описание его назначения;
•краткое описание входных и выходных параметров с указанием единиц измерения;
•список используемых (вызываемых) модулей;
•краткое описание алгоритма (метода) и/или ограничений;
•ФИО автора программы;
•идентифицирующую информацию (номер версии и/или дату последней корректировки). Например:
Стиль оформления текстов модулей. Стиль оформления текстов модулей определяет использование отступов, пропусков строк и комментариев, облегчающих понимание программы. Как правило, пропуски строк и комментарии используют для визуального разделения частей модуля, например:
{проверка количества отрезков и выход, если отрезки не заданы} ifn<0 then
begin
WrileLn (' Количество отрезков отрицательно'); exit;
end;
{цикл суммирования длин отрезков} S: = 0;
for i: = 0 to n-l do S: = S + Len [i];
Для таких языков, как Pascal, C++ и Java, использование отступов позволяет прояснить структуру программы: обычно дополнительный отступ обозначает вложение операторов языка, например:
аmах: = а[1,1]; for i: = l to n do
for j: = l to т do
ifa[i,j]>amax then amax: = a [i,j];
Несколько сложнее дело обстоит с комментариями. Опыт показывает, что переводить с английского языка каждый оператор программы не нужно: любой программист, знающий язык программирования, на котором написана программа, без труда прочитает тот или иной оператор. Комментировать следует цели выполнения тех или иных действий, а также группы операторов, связанные общим действием, т. е. комментарии должны содержать некоторую дополнительную (неочевидную) информацию, например:
{проверка условия и выход, если условие не выполняется} ifn<0 then
begin
WriteLn('Kojiit4ecmeo отрезков отрицательно'); exit;
end;
Для языков низкого уровня, например, Ассемблера, стиль, облегчающий понимание, предложить труднее. В этом случае может оказаться целесообразным комментировать и блоки операторов, и каждый оператор, например:
;цикл суммирования элементов массива
;установки цикла
mov |
AX, 0 |
; обнуляем сумму |
mov |
CX, п |
; загружаем счетчик цикла |
mov |
BX, 0 |
; смещение первого элемента массива |
;тело цикла
cycle: |
add |
AX, a [BX] |
; добавляем элемент |
|
add |
BX, 2 |
; определяем адрес следующего |
|
loop |
cycle |
; цикл на n повторений |
;выход из цикла при обнулении счетчика
2.6. Эффективность и технологичность
Традиционно эффективными считают программы, требующие минимального времени выполнения и/или минимального объема оперативной памяти. Особые требования к эффективности программного обеспечения предъявляют при наличии ограничений (на время реакции системы, на объем оперативной памяти и т. п.). В случаях, когда обеспечение эффективности не требует серьезных временных и трудовых затрат, а также не приводит к существенному ухудшению технологических свойств, необходимо это требование иметь в виду.
Разумный подход к обеспечению эффективности разрабатываемого программного обеспечения состоит в том, чтобы в первую очередь оптимизировать те фрагменты программы, которые существенно влияют на характеристики эффективности. Для уменьшения времени выполнения некоторой программы в первую очередь следует проанализировать циклические фрагменты с большим количеством повторений: экономия времени выполнения одной итерации цикла будет умножена на количество итераций.
Не следует забывать и о том, что многие способы снижения временных затрат приводят к увеличению емкостных и, наоборот, уменьшение объема памяти может потребовать дополнительного времени на обработку.
И тем более не следует «платить» за увеличение эффективности снижением технологичности разрабатываемого программного обеспечения. Исключения возможны лишь при очень жестких требованиях и наличии соответствующего контроля за качеством.
Частично проблему эффективности программ решают за программиста компиляторы. Средства оптимизации, используемые компиляторами, делят на две группы:
•машинно-зависимые, т. е. ориентированные на конкретный машинный язык, выполняют оптимизацию кодов на уровне машинных команд, например, исключение лишних пересылок, использование более эффективных команд и т. п.;
•машинно-независимые выполняют оптимизацию на уровне входного языка, например, вынесение вычислений константных (независящих от индекса цикла) выражений из циклов и т. п.
Естественно, нельзя вмешаться в работу компилятора, но существует много возможностей оптимизации программы на уровне команд.
Способы экономии памяти. Принятие мер по экономии памяти предполагает, что в каких-то случаях эта память неэкономно использовалась. Учитывая, что анализировать имеет смысл только операции размещения данных, существенно влияющие на характеристику эффективности, следует обращать особое внимание на выделение памяти под данные структурных типов (массивов, записей, объектов и т. п.).
Прежде всего при наличии ограничений на использование памяти следует выбирать алгоритмы обработки, не требующие дублирования исходных данных структурных типов в процессе обработки. Примером могут служить алгоритмы сортировки массивов, выполняющие операцию в заданном массиве, например, хорошо известная сортировка методом «пузырька».
Если в программе необходимы большие массивы, используемые ограниченное время, то их можно размещать в динамической памяти и удалять при завершении обработки.
Также следует помнить, что при передаче структурных данных в подпрограмму «по значению» копии этих данных размещаются в стеке. Избежать копирования иногда удается, если передавать данные «по ссылке», но как неизменяемые (описанные const). В последнем случае в стеке размещается только адрес данных, например:
Type Mas.4iv = array [I.. 100] of real;
function Summa (Const a:Massiv; ...)...
Способы уменьшения времени выполнения. Как уже упоминалось выше, для уменьшения времени выполнения в первую очередь необходимо анализировать циклические участки программы с большим количеством повторений. При их написании необходимо по возможности:
• выносить вычисление константных, т. е. не зависящих от параметров цикла, выражений из
циклов;
•избегать «длинных» операций умножения и деления, заменяя их сложением, вычитанием и сдвигами;
•минимизировать преобразования типов в выражениях;
•оптимизировать запись условных выражений - исключать лишние проверки;
•исключать многократные обращения к элементам массивов по индексам (особенно многомерных, так как при вычислении адреса элемента используются операции умножения на значение индексов) - первый раз прочитав из памяти элемент массива, следует запомнить его в скалярной переменной и использовать в нужных местах;
•избегать использования различных типов в выражении и т. п.
Рассмотрим следующие примеры.
Пример 2.2. Пусть имеется цикл следующей структуры (Pascal):
for y: = 0 to 99 do for x: = 0 to 99 do
a [320*x+y]: = S [k,l];
В этом цикле операции умножения и обращения к элементу S[k] выполняются 10000 раз. Оптимизируем цикл, используя, что 320 = 28 + 26:
skl: =S [k,l]; |
{выносим обращение к элементу массива из цикла} |
for x: = 0 to 99 do |
(меняем циклы местами} |
begin |
|
i:= x shl 8 + x shl 6; {умножение заменяем на сдвиги и выносим из цикла} for y; = 0 to 99 do
a [i+y]: =skl;
end; ...
В результате вместо 10000 операций умножения будут выполняться 200 операций сдвига, а их время приблизительно сравнимо со временем выполнения операции сложения. Обращение к элементу массива S[k] будет выполнено один раз.
Пример 2.3. Пусть имеется цикл, в теле которого реализовано сложное условие:
for k: = 2 to n do begin
ifx[k] > yk then S: = S+y[k]-x [k];
if (x [k]< = yk) and (y[k]<yk) then S: = S+yk-x[k]; end;...
В этом цикле можно убрать лишние проверки:
for k: =2 to n do begin
ifx [k]>yk then S:=S+y[k]-x[k] else
ify[k]<yk then S: =S+yk-x [k];
end;...
Обратите внимание на то, что в примере 2.2 понять, что делает программа, стало сложнее, а в примере 2.3 - практически нет. Следовательно, оптимизация, выполненная в первом случае, может ухудшить технологичность программы, а потому не очень желательна.