Принципы программирования

Вопросы

Принципы программирования.

Стиль программирования.

Принципы программирования

Принципы программирования:

Модифицируемость

Функции обозначают модификацию программ.

Именованные константы облегчают модификацию программ.

int scores[202];

for (index = 0 through 201)

Обработка оценок.

const int NUMBER_OF_MAJORS = 202;

int scores[NUMBER_OF_MAJORS];

for (index = 0 through NUMBER_OF_MAJORS - 1)

Обработка оценок.

Операторы — «typedef» — облегчают модификацию программ

typedef float RealType;

typedef long double RealType;

Приглашение к вводу данных.

Эхо ввода.

Разметка вывода.

Разметка вывода

18:00 б 1

Джонс, К. 223 2234.00 1088.19 Н, О Смит, Т. 111

110.23 3, Харрис, У. 44 44000.000 22222.22

Счета вкладчиков по состоянию на 18:00 1 июня

Состояние счёта: Н - новый, О- общий, 3 - закрыт

Имя Номер Снятие Вклады Состояние
Джонс, К. 223 $2234.00 $1088.19 Н, 0
Смит, Т. 111 $110.23 ------------ 3
Харрис, У. 44 $44000.00 $22222.22 ------------

Надёжное программирование

Хороший пользовательский интерфейс имеет большое значение.

Проверка ошибок при вводе данных.

Проверка логики программы.

Предотвращение неверного ввода.

Ненадёжная функция

const mt LOW_END = 10;

// Нижняя граница доходов.

const int HIGH_END = 10; // Верхняя граница доходов .

const int TABLE_SIZE = HIGH_END - LOW__END + 1;

typedef int TableType[TABLE_SIZE];

int index(int group)

// Возвращает индекс массива, соответствующий номеру группы.

{

return group - LOW_END;

} // Конец функции — «index».

void readData(TableType incomeData)

// Считывает и организовывает статистические данные о доходах. Предусловие: вызываемый модуль выдает инструкции и предлагает пользователю ввести данные. Входные данные не должны содержать ошибки. Каждая строка имеет вид — «G» (количество), «N» (количество людей, чей годовой доход равен тысячам долларов со значением — «G» (LOW_END <= G <= HIGH_END)). Ввод данных завершается после считывания строки, в которой числа — «G», «N» — равны нулю. Постусловие: число — «incomeData[G-LOW__END]» — равно общему количеству людей, чей доход равен значению — «G» — тысяч долларов для каждого данного считанного значения. Считанные значения выводятся на экран.

{

int group, number; // Входные значения.

// Очищаем массив.

for (group = LOW__END; group <= HIGH_END; ++group)

incomeData[index(group)] = 0;

for (cin >> group >> number; (group != 0) II (number 1= 0))

cin >> group >> number;

{ // Инвариант: переменные — «group», «member» — не равны нулю.

cout << "Количество людей в группе" << group << " равно " << number << ".\n";

incomeData[index(group)] += number;

} // Конец цикла — «for».

}

// Конец функции — «readData».

Надёжная функция

bool readData(TableType incomeData)

// Считывает и организовывает статистические данные. Предусловие: вызываемый модуль выдает инструкции и предлагает пользователю ввести данные. Каждая строка содержит два целых числа в виде — «G» (количество), «N» (количество людей, чей годовой доход равен значению — «G» — тысяч долларов). LOW_END <= G <= HIGH_END. Ввод данных завершается после считывания строки, в которой данные числа равны нулю. Постусловие: число — «incomeData[G-LOW_END]» — равно общему количеству людей, чей доход равен значению — «G» — тысяч долларов для каждого данного считанного значения. Считанные значения выводятся на экран. Числа — «G» «N» — неверны (не равны нулю и G < LOW_END, G > HIGH_END или N < 0) — функция игнорирует строку ввода, задаёт возвращаемое значение, равным значению — «false» — и продолжает работу. Решение о продолжении выполнения программы принимает вызывающий модуль. Если входные данные не содержат ошибок, функция возвращает значение — «true».

{

int group, number; // Входные значения.

bool dataCorrect = true; // Ошибок пока нет.

for (group = LOW_END; group <= HIGH_END; ++group)

incomeData[index(group)] = 0;

for (сin >> group >> number; (group != 0) II (number != 0))

сin >> group >> number;

{ // Инвариант: переменные — «group», «number» — не равны нулю

cout << "Количество людей в группе" << group << " равно " << number << ".\n";

if ((group >= LOW_end) && (group <= HIGH_END) && (number >= 0))

// Входные данные корректны — добавляем их в счётчик.

incomeData[index(group)] += number;

else

// Ошибка при вводе данных: устанавливаем признак ошибки, игнорируя строку.

dataCorrect = false;

} // Конец цикла — «for».

return dataCorrect;

} // Конец функции — «readData».

Предотвращение семантических ошибок

Функции должны проверять свои инварианты.

Функции должны проверять предусловия.

Факториал целого числа

int factorial(int n)

// Вычисляет факториал целого числа. Предусловие: значение — «n» — >= 0. Постусловие: если значение — «n» — > 0, возвращает значение — «n * (n-1) * ... * 1». Если значение — «n» = 0, возвращает число — 1.

{

int fact = 1;

for (int i = n; i > 1; —i)

fact *= i;

return fact;

} // Конец функции — «factorial».

8 вопросов, касающихся стиля программирования

Широкое использование функций.

Использование закрытых данных-членов.

Избегание глобальных переменных в функциях.

Правильное применение аргументов, передаваемых по ссылке.

Правильное применение функций.

Обработка ошибок.

Читабельность.

Документация.

Основные характеристики программной документации

Комментарии в начале программы должны содержать пункты:

В комментариях, помещённых в начале каждого класса, указывается его предназначение и описываются данные, содержащиеся в нём (константы и переменные).

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

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

Резюме

Технология программирования — это область компьютерных наук, изучающая способы разработки компьютерных программ.

Жизненный цикл программного обеспечения состоит из нескольких этапов:

Инвариант цикла — это свойство алгоритма, которое должно выполняться до и после каждой итерации цикла. Инварианты цикла позволяют разрабатывать итерационные алгоритмы и проверять их правильность.

Оценивая качество решения, необходимо учитывать большое количество факторов:

Сочетание объектно-ориентированного подхода и проектирования — «сверху вниз» приводит к модульному решению. Данные и операции над ними инкапсулируются в классах. Классы можно идентифицировать, анализируя имена существительные, употреблённые при постановке задачи. Алгоритмы следует разбивать на независимые подзадачи, постепенно уточняя их. В любом случае применяется абстракция данных — внимание сосредоточивается на том, что делает модуль, а не на том, как он это делает.

Язык — «UML» — это язык моделирования, используемый для описания объектно-ориентированных проектов. Он предоставляет функциональные возможности для описания данных и операций и применяет диаграммы для выявления отношений между классами.

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

Функции должны быть как можно более независимыми и выполнять одну точно поставленную задачу

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

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

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

Вопросы для самопроверки

int index = 0;

int sum = item[0];

while (index < n)

{

++index;

sum += item[index];

} // Конец цикла — «while».

Ответы

0 < index < n

sum = item[0] + ... + item[index]

Ответы: cпецификации включают в себя определения типов, аргументы, а также пред- и постусловия

sum(in anArray: аrrауТуре, in n: integer): elementType

// Вычисляет сумму первых пяти положительных элементов массива — «anArray». Предусловие: массив — «anArray» — состоит из элементов с количеством — «n» — n >= 5, по крайней мере 5 элементов массива являются положительными. Постусловие: возвращает сумму первых пяти положительных элементов массива — «anArray», массив остаётся неизменным.

Другое решение

computeSum(in аnАrrау: аrrауТуре, in n: integer, out sum: elementType, out success: boolean)

Вычисляет сумму первых пяти положительных элементов массива — «anArray». Предусловие: массив — «anArray» — состоит из элементов с количеством — «n». Постусловие: по крайней мере 5 элементов массива являются положительными — параметр — «sum» — равен сумме первых пяти положительных элементов, а параметр — «succes» — имеет значение — «true» — противном случае параметр — «sum» — равен 0, а параметр — «success» — имеет значение — «false» — массив остаётся неизменным.

Упражнения

Стоимость вещи, которую вы хотите купить, выражается в рублях и копейках. Вы платите наличными, отдавая продавцу рубли с количеством — «d» — и копейки — «c».

Напишите спецификацию функции, вычисляющей сдачу, если она вам полагается.

Обязательно укажите ее предназначение, пред- и постусловия, а также приведите описание её аргументов.

Дата состоит из месяца, дня и года. Часто она выражается целым числом — например, для даты — 4 июля 1776 — количество месяцев равно 7, дней — 4 и лет — 1776.

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

Напишите реализацию этой функции на языке — «С++». Включите в неё комментарии, позволяющие эксплуатировать её в будущем.