Программирование — страница 17 из 57

Чего мы ждем от наших программ вообще? Чего хотим от конкретной программы в частности? Мы хотим, чтобы программа работала правильно и надежно. Если программа не делает то, что от нее требуется, или работает ненадежно, то в лучшем случае это серьезный нюанс, а в худшем — опасность. При этом мы хотим, чтобы программа была хорошо спроектирована, т.е. удовлетворяла наши реальные потребности; на самом деле совершенно неважно, что программа работает правильно, если она делает не то, что задумано, или правильно выполняет задание, но способ, которым она это делает, вызывает тревогу. Кроме того, мы хотим, чтобы программа была экономичной; возможно, я предпочел бы ездить на роллс-ройсе или летать на корпоративном самолете, но пока я не миллиардер, должен учитывать стоимость этого удовольствия.

Именно эти аспекты программного обеспечения (оборудования, систем) могут быть по достоинству оценены непрограммистами. Они должны служить для программистов идеалами, которые следует иметь в виду постоянно, особенно на ранних стадиях проектирования, если мы хотим разработать качественное программное обеспечение. Мы должны также учитывать требования к самому коду: он должен быть удобным в сопровождении; т.е. его структура должна быть такой, что-бы любой другой программист мог понять его и внести свои изменения. Успешная программа “живет” долго (часто десятки лет), постоянно изменяясь. Например, она может быть выполнена на новом аппаратном обеспечении, получить новые возможности, адаптироваться к новым средствам ввода-вывода (экраны, видео, звук), взаимодействовать с пользователями на новых естественных языках и т.д. Только неправильную программу невозможно модифицировать. Для удобства сопровождения программа должна быть относительно простой, а ее код должен непосредственно выражать идеи, лежащие в ее основе. Сложность — враг простоты и удобства — может быть присуща самой проблеме (в этом случае мы должны просто как-то с этим справиться), но она также может быть следствием неудачного выражения идей, заложенных в программе. Мы должны избегать этого, придерживаясь хорошего стиля программирования — стиль имеет значение!

Звучит довольно просто, но это далеко не так. Почему? В программировании по существу нет ничего сложного: просто сообщите компьютеру, что вы от него хотите. Почему же оно может потребовать большого напряжения сил? Ведь в компьютерах тоже нет ничего сложного; они просто выполняют определенные наборы операций, например складывают два числа и выбирают следующую инструкцию в зависимости от результата их сравнения. Проблема заключается в том, что мы не используем компьютеры для решения простых задач. Мы хотим, чтобы они решали задачи, которые нам самим не под силу, но при этом забываем, что вычислительные машины — это придирчивые, ничего не прощающие и безмолвные существа. Более того, мир устроен более сложно, чем мы думаем, поэтому часто не представляем, к каким последствиям могут привести наши запросы.

Мы просто хотим, чтобы программа “делала что-то вроде этого”, и не вникаем в техническое детали. Кроме того, мы часто опираемся на “здравый смысл”. К сожалению, даже среди людей встречаются разные точки зрения на здравый смысл, а уж у компьютеров его вообще нет (хотя некоторые действительно хорошо спроектированные программы могут имитировать здравый смысл в конкретных, подробно изученных ситуациях).

Такой образ мышления приводит к заключению, что “программирование — это понимание”: если вы можете запрограммировать задачу, значит, понимаете ее. И наоборот, если вы глубоко разобрались в задаче, то сможете написать и программу для ее решения. Иначе говоря, программирование можно рассматривать как часть усилий по исследованию проблемы. Программы — это точное представление нашего понимания задачи. Когда вы программируете, то проводите много времени, пытаясь понять задачу, которую хотите автоматизировать.

Процесс разработки программ можно разделить на четыре этапа.

• Анализ. В чем заключается задача? Чего хочет пользователь? Что требуется пользователю? Что может позволить себе пользователь? Какая степень надежности нам необходима?

• Проектирование. Как решить задачу? Какую структуру должна иметь система? Из каких частей она должна состоять? Каким образом эти части будут взаимодействовать? Каким образом система будет взаимодействовать с пользователем?

• Программирование. Выражаем решение задачи (проект) в виде программы. Пишем программу, учитывая все установленные ограничения (по времени, объему, финансам, надежности и т.д.). Убеждаемся, что программа работает правильно и удобна в сопровождении.

• Тестирование. Убеждаемся, что во всех предусмотренных ситуациях система работает правильно.


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

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

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

Аналогично, попав в тупик при реализации программы, оторвитесь от клавиатуры. Думайте о самой задаче, а не о своем неполном решении этой задачи. Поговорите с кем-нибудь: объясните, что вы хотели и почему программа не работает. Просто удивительно, как часто можно найти решение, просто подробно объяснив задачу кому-то еще. Не занимайтесь отладкой программ (поиском ошибок) в одиночку, если есть такая возможность!

В центре внимания нашей книги лежит реализация и особенно программирование. Мы не учим решать задачи, заваливая вас грудой примеров и решений. Часто новую задачу можно свести к уже известной и применить традиционный метод ее решения. Только после того, как большая часть подзадач будет обработана таким образом, можно позволить себе увлекательное “свободное творчество”. Итак, сосредоточимся на методах выражения идей в виде программ.

Непосредственное выражение идей в виде программ — это основная цель программирования. Это совершенно очевидно, но до сих пор мы еще не привели достаточно ярких примеров. Мы еще не раз будем возвращаться к этому. Если в нашей программе необходимо целое число, мы храним его в виде переменной типа int, предусматривающего основные операции с целыми числами. Если мы хотим работать со строками символов, то храним их в виде переменных типа string, обеспечивающего основные операции по манипуляции с текстом. В идеале, если у нас есть идея, концепция, сущность или какая-то “вещь”, которую можно изобразить на доске и сослаться на нее в ходе дискуссии, про которую написано в учебнике (по некомпьютерным наукам), то мы хотим, чтобы это нечто существовало в нашей программе в виде именованной сущности (типа), предусматривающей требуемые операции. Если мы собираемся проводить математические вычисления, то нам потребуется тип complex для комплексных чисел и тип Matrix для матриц. Если хотим рисовать, то потребуются типы Shape (Фигура), Circle (Круг), Color (Цвет) и Dialog_box (Диалоговое окно). Если хотим работать с потоками данных, скажем, поступающих от датчика температуры, то нам понадобится тип istream (буква “i” означает ввод (input)). Очевидно, что каждый такой тип должен обеспечивать совершенно конкретный набор предусмотренных операций. Мы привели лишь несколько примеров из книги. Кроме них, мы опишем инструменты и методы, позволяющие создавать собственные типы, описывающие любые концепции, необходимые для вашей программы.

Программирование носит частично практический, частично теоретический характер. Если вы ограничитесь ее практическими аспектами, то будете создавать немасштабируемые и трудные для сопровождения поделки. Если же захотите остаться теоретиком, то будете разрабатывать непрактичные (и не экономичные) игрушки. Различные точки зрения на идеалы программирования и биографии людей, внесших значительный вклад в создание языков программирования, изложены в главе 22 “Идеалы и история”.


Контрольные вопросы

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

1. Что такое программное обеспечение?

2. Чем объясняется важность программного обеспечения?