• Абстракция. Этот способ предполагает сокрытие деталей, которые не являются необходимыми для работы с программой (детали реализации) за удобным и универсальным интерфейсом. Например, вместо изучения деталей сортировки телефонной книги (о методах сортировки написано множество толстых книг), мы можем просто вызвать алгоритм сортировки из стандартной библиотеки языка С++. Все, что нам нужно для сортировки, — знать, как вызывается этот алгоритм, так что мы можем написать инструкцию
sort(b, e)
, где
b
и
e
— начало и конец телефонной книги соответственно. Другой пример связан с использованием памяти компьютера. Непосредственное использование памяти может быть довольно сложным, поэтому чаще к участкам памяти обращаются через переменные, имеющие тип и имя (раздел 3.2), объекты класса
vector
из стандартной библиотеки (раздел 4.6, главы 17–19), объекты класса
map
(глава 21) и т.п.
• “Разделяй и властвуй”. Этот способ подразумевает разделение большой задачи на несколько меньших задач. Например, если требуется создать словарь, то работу можно разделить на три части: чтение, сортировка и вывод данных. Каждая из новых задач намного меньше исходной.
Чем это может помочь? Помимо всего прочего, программа, созданная из частей, обычно немного больше, чем программа, в которой все фрагменты оптимально согласованы друг с другом. Причина заключается в том, что мы плохо справляемся в большими задачами. Как правило, как в программировании, так и в жизни, — мы разбиваем их на меньшие части, полученные части разделяем на еще более мелкие, пока не получим достаточно простую задачу, которую легко понять и решить. Возвращаясь к программированию, легко понять, что программа, состоящая из 1000 строк, содержит намного больше ошибок, чем программа, состоящая из 100 строк, поэтому стоит разделить большую программу на части, размер которых меньше 100 строк. Для более крупных программ, скажем, длиной более 10 тыс. строк, применение абстракции и метода “разделяй и властвуй” является даже не пожеланием, а настоятельным требованием.
Мы просто не в состоянии писать и поддерживать работу крупных монолитных программ. Оставшуюся часть книги можно рассматривать как длинный ряд примеров задач, которые необходимо разбить на более мелкие части, а также методов и способов, используемых для этого.
Рассматривая процесс разбиения программ, мы всегда учитываем, какие инструменты помогают выделить эти части и обеспечить взаимодействие между ними. Хорошая библиотека, содержащая полезные средства для выражения идей, может существенно повлиять на распределение функциональных свойств между разными частями программы. Мы не можем просто сидеть и фантазировать, как получше разбить программу на части; мы должны учитывать, какие библиотеки находятся в нашем распоряжении и как их можно использовать. Пока вы находитесь в начале пути, но вскоре увидите, что использование существующих библиотек, таких как стандартная библиотека языка С++, позволяет сэкономить много сил не только на этапе программирования, но и на этапах тестирования и документации. Например, потоки ввода-вывода позволяют нам не вникать в детали устройства аппаратных портов ввода-вывода. Это первый пример разделения программы на части с помощью абстракции. В следующих главах мы приведем новые примеры.
Обратите внимание на то, какое значение мы придаем структуре и организации программы: вы не сможете написать хорошую программу, просто перечислив множество инструкций. Почему мы упоминаем об этом сейчас? На текущем этапе вы (или, по крайней мере, многие читатели) слабо представляете себе, что такое программа, и лишь через несколько месяцев будете готовы написать программу, от которой может зависеть жизнь или благосостояние других людей. Мы упоминаем об этом, чтобы помочь вам правильно спланировать свое обучение. Существует большой соблазн набросать примерный план курса по программированию — похожего на изложенный в оставшейся части книги, — выделив темы, которые имеют очевидное полезное применение и проигнорировав более “тонкие” вопросы разработки программного обеспечения. Однако хорошие программисты и проектировщики систем знают (и это знание часто приобретается тяжелой ценой), что вопросы структуры лежат в основе хорошего программного обеспечения и пренебрежение ими порождает массу проблем. Не обеспечив хорошей структуры программы, вы, образно говоря, лепите ее из глины. Это вполне возможно, но таким образом никогда нельзя построить пятиэтажный дом (глина просто не выдержит). Если хотите построить не времянку, а солидное здание, то следует уделить внимание структуре и правильной организации кода, а не возвращаться к этим вопросам, совершив множество ошибок.
4.3. Выражения
Основными строительными конструкциями программ являются выражения. Выражение вычисляет некое значение на основе определенного количества операндов. Простейшее выражение представляет собой обычную литеральную константу, например '
a
',
3.14
или
"Norah"
.
Имена переменных также являются выражениями. Переменная — это объект, имеющий имя. Рассмотрим пример.
// вычисление площади:
int length = 20; // литеральное целое значение
// (используется для инициализации переменной)
int width = 40;
int area = length*width; // умножение
Здесь литералы
20
и
40
используются для инициализации переменных, соответствующих длине и ширине. После этого длина и ширина перемножаются; иначе говоря, мы перемножаем значения
length
и
width
. Здесь выражение “значение
length
” представляет собой сокращение выражения “значение, хранящееся в объекте с именем
length
”. Рассмотрим еще один пример.
length = 99; // присваиваем length значение 99
Здесь слово
length
, обозначающее левый операнд оператора присваивания, означает “объект с именем
length
”, поэтому это выражение читается так: “записать число 99 в объект с именем
length
”. Следует различать имя
length
, стоящее в левой части оператора присваивания или инициализации (оно называется “
lvalue
переменной
length
”) и в правой части этих операторов (в этом случае оно называется “
rvalue
переменной
length
”, “значением объекта с именем
length
”, или просто “значением
length
”). В этом контексте полезно представить переменную в виде ящика, помеченного именем.