97 этюдов для программистов. Опыт ведущих экспертов - Пит Гудлиф
Шрифт:
Интервал:
Закладка:
• При написании модульных тестов вы узнаете, насколько легко проводить модульное тестирование для конкретного модуля кода. Модульное тестирование выявляет присутствие (или отсутствие) качеств, которые желательны для кода, такие как слабая связанность (coupling) и сильная связность (cohesion).
• Прогон модульных тестов демонстрирует, как ведет себя код. Он позволяет обнаружить присутствие (или отсутствие) характеристик времени выполнения, желательных для приложения, например устойчивости и корректности.
• С помощью доски и карточек можно сделать прогресс наглядным и конкретным. Можно увидеть, что задачи находятся в состоянии Не начата, В процессе и Завершена, и при этом не придется заходить в неочевидную систему управления проектом и не придется упрашивать программистов составлять фиктивные отчеты о состоянии проекта.
• Итеративная разработка повышает наглядность прогресса (или его отсутствия), поскольку чаще фиксируются факты того, что разработка ведется. Создание программного обеспечения, готового к выпуску, отражает реальное положение вещей, в отличие от оценок.
Лучше всего разрабатывать программы, имея многочисленные наглядные показатели. Наглядность дает уверенность в том, что прогресс является реальным, а не вымышленным; спланированным, а не непреднамеренным; воспроизводимым, а не случайным.
Передача сообщений улучшает масштабируемость параллельных систем
Рассел Уиндер
Уже на первых лекциях по информатике программистов учат, что конкурентные вычисления — и в особенности параллельные как особый подвид конкурентных — вещь трудная, и что лишь лучшим дается надежда справиться с этой задачей, и что даже лучшие не справляются. При этом неизменно уделяется большое внимание потокам, семафорам, мониторам и трудностям организации потоковой безопасности при одновременном доступе к переменным.
Сложных проблем здесь действительно много, и решать их бывает очень трудно. Но в чем корень проблем? Общая память. Практически все проблемы конкурентных вычислений, о которых постоянно приходится слышать, касаются общей памяти с изменяемыми данными: состояние гонки (race conditions), взаимная блокировка (deadlock), активная блокировка (livelock) и т. п. Кажется, ответ очевиден: забудьте о конкурентности либо держитесь подальше от общей памяти!
Отказ от конкурентности почти наверняка не вариант. Количество ядер в компьютерах возрастает чуть ли не каждый квартал, поэтому достижение настоящего параллелизма становится все важнее. Мы не можем больше полагаться на непрерывный рост тактовой частоты процессоров как основу производительности приложений. Производительность может вырасти только за счет параллелизма. Конечно, можно не заботиться о производительности приложений, но едва ли это понравится пользователям.
Так можно ли отказаться от общей памяти? Определенно, да.
Вместо потоков и общей памяти можно воспользоваться процессами и передачей сообщений. Под процессом здесь понимается защищенное независимое состояние исполняющегося кода, а не обязательно процесс операционной системы. Такие языки, как Erlang (а до него occam), показали, что процессы — весьма удачный механизм программирования конкурентных и параллельных систем. В таких системах меньше проблем синхронизации, чем в многопоточных системах с общей памятью. Кроме того, существует формальная модель взаимодействующих последовательных процессов (Communicating Sequential Processes, CSP), которую можно применять при разработке таких систем.
Можно пойти дальше и организовать вычисления в виде системы, управляемой потоком данных (dataflow system). В такой системе нет явно запрограммированного потока управления. Вместо этого создается направленный граф операторов, соединенных путями передачи данных, а затем в систему подаются данные. Контроль вычислений осуществляется по готовности данных внутри системы. И никаких проблем синхронизации.
При этом для системной разработки применяются главным образом такие языки, как C, C++, Java, Python и Groovy, о которых программистам говорят, что они служат для разработки многопоточных систем с общей памятью. Как же быть? Решение в том, чтобы использовать — или создавать, если их не существует, — библиотеки и среды, которые предлагают схемы процессов и пересылки сообщений, полностью исключающие применение общей изменяемой памяти.
В итоге отказ от общей памяти в пользу передачи сообщений станет, скорее всего, наиболее удачным методом реализации систем, эффективно использующих параллелизм, получивший сегодня повсеместную прописку в компьютерном железе. Занимательно выходит — процессы как единица конкурентного исполнения появились раньше потоков, но в будущем, по-видимому, потоки станут использоваться для реализации процессов.
Послание потомкам
Линда Райзинг
Возможно, дело в том, что в большинстве своем программисты — умные люди, но за многие годы моего преподавания и тесной совместной работы с ними у меня сложилось впечатление, будто сложность задач, над которыми они бьются, оправдывает для них создание решений столь же сложных (возможно, и для них самих спустя несколько месяцев после написания кода) для понимания и сопровождения.
Помню один случай с Джо, слушателем моего курса по структурам данных, который пришел показать мне результат своего труда.
— Держу пари, вы не догадаетесь, что делает этот код! — радостно воскликнул он.
— Ты прав, — согласилась я, не слишком вглядываясь в его текст и думая, как донести до него важную мысль. — Уверена, ты хорошо потрудился над этим примером. Боюсь, правда, ты упустил нечто важное. Скажи, Джо, у тебя есть младший брат?
— Да, конечно! Его зовут Фил, и он слушает ваш вводный курс. Он тоже учится программировать! — гордо объявил Джо.
— Это замечательно, — отвечала я. — Интересно, сможет ли он понять этот код?
— Ни за что, — сказал Джо, — это сложная штука!
— Давай предположим, — продолжила я, — что это реальный рабочий код и что через несколько лет Филу предложат работу по внесению изменений в этот код. Что ты сделал для Фила?
Джо моргал, глядя на меня.
— Мы знаем, что Фил — толковый парень, верно?
Джо кивнул.
— И не хочу хвастаться, но я тоже довольно толковая!
Джо ухмыльнулся.
— Итак, мне нелегко понять, что ты тут сделал, и твоему очень способному младшему брату тоже. Скорее всего, придется поломать над этим голову. В таком случае, что можно сказать о написанном тобой коде?
Как мне показалось, Джо увидел свой код в новом свете.
— Представим себе дело так, — сказала я, стараясь как можно лучше играть роль доброго наставника. — Каждая строка твоего кода — это послание человеку будущего, которым может оказаться твой младший брат. Попробуй объяснить этому умному человеку, как решить эту трудную задачу. Так ли ты видишь это будущее? Что этот умный программист увидит твой код и воскликнет: «Ничего себе! Как здорово! Мне совершенно понятно, что здесь происходит, и я поражен элегантностью — нет, красотой — этого кода. Надо немедленно показать его коллегам по команде. Это же шедевр!»
— Джо, можешь ли ты написать код, который решает эту