86. Две ошибки могут гасить одна другую (и тогда их трудно исправлять)





Скачать 242.08 Kb.
Название86. Две ошибки могут гасить одна другую (и тогда их трудно исправлять)
Дата публикации20.08.2013
Размер242.08 Kb.
ТипВопрос
100-bal.ru > Философия > Вопрос

86. Две ошибки могут гасить одна другую (и тогда их трудно исправлять)


Аллан Келли (Allan Kelly)

Код никогда не лжет, но он может противоречить сам себе. Иногда противоречия вызывают недоумение: как это вообще может работать?

В своем интервью1 главный разработчик программного обеспечения для лунного модуля Apollo 11 Аллан Клампп рассказал, что ПО управления двигателями содержало дефект, из-за которого спускаемый модуль должен был вести себя неустойчиво. Однако в программе была еще одна ошибка, компенсировавшая первую, и это ПО успешно использовалось при посадке Apollo 11 и 12 на Луну, прежде чем ошибки были обнаружены или исправлены.

Рассмотрим функцию, которая возвращает статус своего выполнения. Допустим, что она возвращает false, когда должна была бы возвратить true. Теперь представим, что вызывающая функция не заботится о проверке возвращаемого значения. Все работает прекрасно, пока в один прекрасный день кто-то не обнаружит отсутствие проверки и не вставит ее.

Или рассмотрим приложение, которое хранит состояние в виде документа XML. Допустим, что один из узлов некорректно записывается как TimeToLive (время жизни) вместо TimeToDie (время смерти), как следовало бы из документации. Все будет хорошо, пока код записи и код чтения содержат одну и ту же ошибку. Но исправьте ее в одном месте или добавьте новое приложение, читающее тот же документ, и симметрия рухнет, как и весь код.

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

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

Такое случается не только с кодом: проблема бывает с документами, содержащими технические требования. И она может распространяться из одного места в другое, как вирус. Ошибка в коде компенсирует ошибку в письменном описании.

Это может коснуться и человека: пользователи обнаруживают, что когда программа говорит «левый», она имеет в виду «правый», и подстраивают под нее свою работу. Они даже сообщают о проблеме новым пользователям: «Запомни, когда приложение говорит, что нужно щелкнуть левой кнопкой, это значит, что нужно щелкнуть правой». Стоит исправить ошибку, и пользователям придется переучиваться.

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

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

87. Кодирование в духе Убунту для своих друзей


Аслам Хан (Aslam Khan)

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

Можно написать код высокого качества в отрыве от реальности, полностью уйдя в себя. С одной точки зрения, это эгоцентричный подход (эго не в смысле высокомерия, а в смысле личного). Это также точка зрения Дзен, и речь идет о вас в момент создания программы. Я всегда пытаюсь жить в конкретный момент, поскольку это помогает добиться лучшего качества, но при это я живу в своем моменте. А как же быть с моментом моей команды? Мой момент и момент моей команды – они совпадают?

На языке зулу философия Убунту определяется как «Умунту нгумунту нгабанту», что приблизительно значит «Личность – это личность через (другие) личности». Я становлюсь лучше, поскольку ты делаешь меня лучше своими добрыми поступками. Но с другой стороны, вы хуже делаете свое дело, если я плох в делаю свое. Для разработчиков это сводится к тому, что «разработчик – это разработчик через (других) разработчиков». Если опуститься до «железа», то «код – это код через (другой) код».

Качество кода, который пишу я, влияет на качество кода, который пишете вы. А что если мой код низкого качества? Даже если вы напишите очень «чистый» код, там, где вы будете пользоваться моим кодом, качество вашего кода упадет примерно до уровня моего кода. Можно применять множество шаблонов и приемов, чтобы ограничить ущерб, но полностью от него уже не избавиться. Я заставил вас делать больше, чем требовалось, просто потому, что не думал о вас, когда жил в свое моменте.

Я могу считать свой код «чистым», но все же можно сделать его еще лучше, придерживаясь духа Убунту. Как выглядит код в духе Убунту? Он выглядит как хороший, ясный код. И речь идет даже не о коде как артефакте. Все дело в акте создания этого артефакта. Программирование для своих друзей с в духе Убунту поможет вашей команде жить, согласуясь с собственными ценностями, и укрепить свои принципы. Следующий человек, который каким-либо образом прикоснется к вашему коду, станет лучше как личность и как разработчик.

Дзен – это дело личное. Убунту – это Дзен, но для группы людей. Крайне редко мы пишем программы исключительно для самих себя.

88. Утилиты Unix – ваши друзья


Диомидис Спинеллис (Diomidis Spinellis)

Если бы, отправляясь на необитаемый остров, я должен был выбирать между IDE и набором инструментов Unix, я бы, не колеблясь, выбрал утилиты Unix. Вот причины, по которым следует овладеть искусством работы с утилитами Unix.

Во-первых, IDE ориентированы на конкретные языки, а утилиты Unix могут работать с любым материалом в текстовом виде. В современных условиях разработки, когда новые языки и понятия появляются каждый год, затраты на изучение работы с утилитами Unix окупятся многократно.

Кроме того, IDE предлагают только те команды, которые решили реализовать их создатели, тогда как средствами Unix можно выполнить любую мыслимую задачу. Их можно представить себе как классические (до появления Bionicle) блоки Lego: вы создаете собственные команды, просто комбинируя маленькие, но универсальные утилиты Unix. Например, следующая последовательность представляет собой текстовую реализацию анализа сигнатуры по Каннингему – последовательность использования точки с запятой, фигурных скобок и кавычек в любом файле может многое сказать о его содержимом:

for i in *.java; do

echo -n "$i: "

sed 's/[^"{};]//g' $i | tr -d '\n'

echo

done

Далее, каждая изученная вами операция IDE специфична для данной задачи – например, добавление нового шага в конфигурацию отладочной сборки проекта. Напротив, более глубокое изучение утилит Unix повышает вашу эффективность при решении любых задач. Например, я применил утилиту sed, использованную в приведенной выше последовательности команд, чтобы преобразовать процедуру сборки проекта для кросс-компиляции на многопроцессорных архитектурах.

Утилиты Unix были разработаны в ту эпоху, когда многопользовательские компьютеры располагали ОЗУ размером в 128KB. При их создании была проявлена такая изобретательность, что сейчас они могут очень эффективно обрабатывать огромные наборы данных. Большинство утилит работает как фильтры, обрабатывая одновременно всего одну строку, а потому нет верхней границы объема данных, которые они могут обработать. Вы хотите узнать, сколько редакций хранится в дампе английской Википедии, имеющем размер полтерабайта? Простой вызов команды

grep '' | wc –l

без труда даст вам ответ. Если какая-то последовательность команд покажется вам очень полезной, можно поместить ее в сценарий оболочки, применив в нем исключительно мощные программные конструкции, например, направив данные в циклы и условные операторы. Еще более впечатляет, что при выполнении команд Unix в конвейере, как в предыдущем примере, нагрузка естественным образом распределяется по нескольким блокам обработки современных многоядерных ЦП.

Принцип «малое прекрасно» и реализации open source утилит Unix делают их повсеместно доступными, даже на платформах с ограниченными ресурсами, таким как настольный медиа-плеер или маршрутизатор DSL. Такие устройства едва ли располагают мощным графическим интерфейсом пользователя, но часто содержат приложение BusyBox, предоставляющее наиболее часто используемые утилиты. Если вы занимаетесь разработкой под Windows, то среда Cygwin предложит вам все мыслимые утилиты Unix, как в виде исполняемых файлов, так и в виде исходного кода.

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

Параметры, влияющие на функционирование утилиты, задаются в командной строке. Следуйте этим правилам, и тогда «Земля – твое, мой мальчик, достоянье».

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


Ян Кристиан ван Винкель (Jan Christiaan “JC” van Winkel)

Крупный банк с большим числом отделений пожаловался, что купленные им для кассиров новые компьютеры работают слишком медленно. Это было еще до повсеместного использования электронного банка, и банкоматы тоже не были так широко распространены, как сейчас. Люди ходили в банк гораздо чаще, а из-за медленных компьютеров выстраивались очереди. Банк угрожал разорвать контракт с поставщиком.

Поставщик послал своего специалиста по настройке и анализу производительности, чтобы выяснить, в чем причина задержек. Тот быстро нашел выполняющуюся на терминале программу, которая съедала почти всю мощность ЦП. С помощью инструмента профилирования он нашел в этой программе функцию, которая и была виновником. Ее исходный код выглядел так:
for (i=0; i
if (... s[i] ...) ...

}

При этом строка s обычно содержала несколько тысяч символов. Код (который был написан в банке) быстро изменили, после чего банковские кассиры зажили счастливо.

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

n=strlen(s);

for (i=0; i
if (... s[i] ...) ...

}

Всем известен принцип «сначала заставь его хоть как-то работать, а потом заставь работать быстро», направленный против микрооптимизаций. Но по приведенному примеру можно решить, что программист исполнил макиавеллиевское адажио «сначала заставь его работать медленно».

С подобным недомыслием можно часто столкнуться. И дело не только в том, что не нужно «изобретать заново колесо» Иногда молодые программисты начинают, не думая, набирать код и вдруг «изобретают» пузырьковую сортировку. При этом они, бывает, еще и гордятся этим.

Обратной стороной выбора правильного алгоритма является выбор структуры данных. Есть большая разница между тем, что использовать для хранения совокупности в миллион объектов, в которой нужно выполнять поиск: связанный список или структуру с хешированием данных либо двоичное дерево. Ваш выбор окажет большое влияние на оценку пользователем вашего умения программировать.

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

Хороший программист должен также знать, когда стоит использовать дрянной алгоритм. Например, если предметная область такова, что в ней не может быть больше пяти элементов (скажем, количество костей в покере на костях), то вы знаете, что вам не придется сортировать больше пяти элементов. В таком случае, пузырьковая сортировка действительно может оказаться наиболее эффективной. У каждой собаки бывает свой праздник.

Поэтому прочтите несколько хороших книг – и хорошенько в них разберитесь. А если вы глубоко изучите «Искусство программирования» Дональда Кнута, вам может даже повезти: найдите у автора ошибку, и вы получите от него чек на один шестнадцатеричный доллар ($2.56).

90. Подробный журнал лишит вас сна


Йоханнес Бродуолл (Johannes Brodwall)

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

Если ваши системы похожи на мои, то когда кончается ваша работа, начинается работа кого-то другого. После завершения разработки система должна долго и успешно обслуживать клиентов (если вам повезет). Как вы узнаете об ошибках, если система в эксплуатации, и что вы будете с ними делать?

Возможно, кто-то осуществляет мониторинг системы вместо вас, а возможно, вы делаете это сами. В любом случае, мониторинг, вероятно, включает в себя просмотр журналов. Если что-то случилось и вас из-за этого должны были разбудить, желательно, чтобы причина была веской. Если моя система при смерти, я должен знать об этом. Но если она просто икнула, я бы предпочел, чтобы мой сладкий сон не нарушали.

Во многих системах первым признаком неприятностей служит запись сообщения в какой-нибудь журнал. Обычно это журнал регистрации ошибок. Будьте добры: с первого же дня, как только что-то появится в журнале ошибок, пусть вам будут звонить и будить среди ночи. Если вы сможете смоделировать нагрузку на своей системе во время системного тестирования, то чистый журнал ошибок указывает на вероятную надежность вашей системы, а грязный – служит первым сигналом тревоги.

Распределенные системы создают еще один уровень сложности. Вы должны решить, как действовать в случае отказа внешних зависимостей. Если система сильно распределена, такие случаи могут быть частыми. Учтите это обстоятельство при выборе политики журналирования.

В целом, лучшим свидетельством того, что все в порядке, служит регулярное появление сообщений низкого приоритета. Меня устраивает, когда на каждое существенное событие в приложении появляется примерно одно сообщение уровня INFO.

Слишком подробный журнал говорит о том, что систему будет трудно контролировать во время эксплуатации. Если вы рассчитываете, что в журнал ошибок ничего не будет выводиться, вам будет гораздо легче решить, что нужно делать, когда в нем что-то все-таки появится.

91. WET размазывает узкие места производительности


Кирк Пеппердин (Kirk Pepperdine)

Значение принципа DRY (не повторяй себя) в том, что он кодифицирует идею о том, что каждый элемент знаний в системе должен иметь единственное представление. Иными словами, знание должно содержаться в единственной реализации. Полную противоположность DRY представляет WET (пиши каждый раз). Наш код соответствует WET, когда знание кодифицируется в нескольких разных реализациях. Сравнительное влияние DRY и WET на функциональность становится понятным после рассмотрения их многочисленных эффектов на профиль функциональности.

Рассмотрим некоторую функцию, скажем, X, нашей системы, являющуюся узким местом для ЦП. Допустим, что функция X потребляет 30% мощности ЦП. Теперь предположим, что у функции X есть 10 различных реализаций. В среднем, каждая реализация потребляет 3% ЦП. Поскольку такой уровень использования ЦП не вызывает беспокойства, то при беглом анализе можно не заметить, что эта функция создает узкое место. Но допустим, что мы как-то выяснили, что функция X – узкое место. Тогда встает проблема найти и исправить каждую реализацию. Для WET у нас есть 10 разных реализаций, которые нужно найти и исправить. Для DRY мы сразу увидим использование ЦП на 30%, а размер исправляемого кода будет в 10 раз меньше. К тому же, не нужно выискивать все многочисленные реализации.

Есть одна ситуация, в которой мы часто виновны в нарушении DRY: при работе с коллекциями. Стандартный прием реализации запроса заключается в обходе коллекции и применении запроса к каждому ее элементу:

public class UsageExample {

private ArrayList allCustomers = new ArrayList();

// ...

public ArrayList findCustomersThatSpendAtLeast(Money amount) {

ArrayList customersOfInterest = new ArrayList();

for (Customer customer: allCustomers) {

if (customer.spendsAtLeast(amount))

customersOfInterest.add(customer);

}

return customersOfInterest;

}

}

Открыв саму коллекцию клиентам, мы нарушили принцип инкапсуляции. Это не только ухудшает возможности рефакторинга, но и заставляет пользователей кода нарушать DRY, поскольку, возможно, им придется заново реализовывать тот же запрос. Такой ситуации легко избежать, если убрать открытые коллекции из API. В данном примере можно ввести новый проблемно-ориентированный тип коллекции с именем CustomerList. Этот класс семантически лучше согласован с предметной областью. Он будет играть естественную роль хранилища всех наших запросов.

Наличие этого нового типа коллекции позволит также легко выяснить, являются ли эти запросы узким местом производительности. Включив запросы в класс, мы устраняем необходимость открывать клиентам варианты представления, такие как ArrayList. Это дает нам свободу для последующего изменения реализаций без нарушения контрактов с клиентами:

public class CustomerList {

private ArrayList customers = new ArrayList();

private SortedList customersSortedBySpendingLevel =

new SortedList
// ...

public CustomerList findCustomersThatSpendAtLeast(Money amount) {

return new CustomerList(

customersSortedBySpendingLevel.elementsLargerThan(amount));

}

}

public class UsageExample {

public static void main(String[] args) {

CustomerList customers = new CustomerList();

// ...

CustomerList customersOfInterest =

customers.findCustomersThatSpendAtLeast(someMinimalAmount);

// ...

}

}

В этом примере следование DRY позволило нам ввести другую систему индексирования для SortedList с ключом по объему трат наших покупателей. Еще важнее, чем конкретные детали данного примера, то, что следование DRY помогло найти и исправить узкое место, что было бы труднее сделать, если бы код был написан по правилам WET.

92. Когда программисты и тестеры начинают сотрудничать между собой


Джанет Грегори (Janet Gregory)

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

Тестеры могут помочь заказчикам написать приемочные тесты, используя язык их предметной области, с помощью таких инструментов, как Fit (Framework for Integrated Test). Если передать эти тесты программистам перед тем, как они начнут кодирование, те смогут применить практику разработки на основе приемочного тестирования (acceptance test–driven development – ATDD). Программисты пишут приспособления для прогона тестов, а потом кодируют с проверкой на прохождение этих тестов. Эти тесты становятся затем частью набора регрессивных тестов. При такой организации сотрудничества функциональное тестирование проходит быстро, и остается больше времени пробного тестирования граничных условий или в рамках более широкой картины.

Можно пойти еще дальше. Будучи тестером, я могу изложить свои соображения по тестированию еще до того, как программисты начнут кодировать новую функцию. Если я спрошу у программистов, какие у них есть предложения, они почти всегда предоставят мне данные, которые позволят организовать более широкое тестирование или помогут не тратить время на ненужные тесты. Часто нам удавалось предотвратить появление дефектов, потому что тесты проясняют многие первоначальные идеи. Например, в одном из проектов, где я участвовала, я передала программистам Fit-тесты, которые показывали, какие результаты ожидаются при поиске с групповыми символами. Программист до этого был твердо намерен кодировать только поиск конкретных слов. Поговорив с заказчиком, мы смогли договориться о правильной интерпретации поиска до того, как начать кодировать. В результате мы предотвратили возникновение дефекта и смогли сберечь массу времени нам обоим.

Программисты могут также сотрудничать с тестерами в деле автоматизации. Они знакомы с хорошей практикой кодирования и могут помочь тестерам создать надежный комплект автоматизированных тестов, что служит интересам всей команды. Я часто видела неудачные примеры автоматизации тестирования, связанные со слабым проектированием тестов. Либо тесты пытаются проверить слишком многое, либо тестеры недостаточно разбираются в технологии, чтобы сделать тесты независимыми. Тестеры часто создают узкие места, поэтому полезно, когда программисты работают с ними вместе над такими задачами, как автоматизация. Выяснив вместе с тестерами, что можно протестировать на раннем этапе – возможно, с помощью какого-нибудь простого инструмента, программисты получат дополнительный цикл обратной связи, что в конечном итоге позволит им создать лучший код.

Если тестеры перестанут думать лишь о том, как бы им сорвать работу программы и найти ошибки в коде разработчиков, то и программисты перестанут думать, что тестеры стараются только «достать» их, и будут более расположены к сотрудничеству. Когда программисты понимают, что они отвечают за качество своего кода, легкость его тестирования становится естественным дополнительным качеством, и команда может совместно автоматизировать больше регрессивных тестов. Таково чудо успешной групповой работы.

93. Пишите код так, как если бы вам предстояло сопровождать его всю оставшуюся жизнь


Юрий Зубарев (Yuriy Zubarev)

Можно задать вопрос о том, что должен знать и уметь любой программист, 97 разным людям и получить 97 разных ответов. Это может одновременно ошеломить и напугать. Все советы хороши, все принципы здравы, все истории убедительны, но с чего начать? Еще важнее, однажды начав, оставаться на уровне лучших практик, с которыми вы ознакомились, и сделать их составной частью своей практики программирования. Я думаю, что ответ лежит в вашем настроении, или просто в вашем отношении к людям. Если вам безразличны ваши коллеги-разработчики, тестеры, администраторы, сотрудники отделов продаж и маркетинга, а также конечные пользователи, то у вас не возникнет, скажем, побуждения вести разработку на основе тестов или писать понятные комментарии в коде. Думаю, что есть простой способ изменить свое отношение и всегда стремиться выпускать продукты самого лучшего качества:

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

Вот и все. Если вы примете эту мысль, начнут происходить разные удивительные вещи. Если вы согласитесь с тем, что любой из ваших прежних или нынешних работодателей имеет право позвонить вам среди ночи и попросить объяснить, на чем основаны решения, сделанные вами при написании некоего метода fooBar, то вы постепенно станете экспертом в программировании. Вам самим захочется придумывать лучшие имена для переменных и методов. Вы постараетесь не допускать, чтобы блоки кода состояли из сотен строк. Вы будете искать, изучать и применять паттерны проектирования. Вы станете писать комментарии, тестировать код и непрерывно осуществлять его рефакторинг. Поддержка всего написанного вами кода в течение всей оставшейся жизни будет становиться все более грандиозной задачей. У вас просто не останется иного выбора, кроме как работать лучше, изобретательнее и эффективнее.

Если вдуматься, то код, написанный вами много лет назад, продолжает влиять на вашу карьеру, нравится вам это или нет. Каждый метод, класс или модуль, который вы спроектировали или написали, хранит след ваших знаний, позиции, упорства, профессионализма, уровня обязательств и степени удовольствия. Люди формируют о вас свое мнение на основе кода, который они видят. Если это мнение постоянно оказывается отрицательным, ваш карьерный рост не оправдает ваших надежд. Каждая строка вашего кода должна быть на благо вашей карьере, вашим клиентам и вашим пользователям – пишите код так, как если бы вам пришлось сопровождать его всю оставшуюся жизнь.

94. Пишите маленькие функции на основе примеров


Кейт Брэйтуэйт (Keith Braithwaite)

Мы хотим писать правильный код и иметь на руках свидетельство его правильности. Решению обеих задач можно помочь, если принять во внимание «размер» функции. Не в смысле объема кода, который реализует функцию – хотя это интересно, – а как размер математической функции, которую обнаруживает наш код.

Например, в игре го есть положение, называемое «атари», в котором фишки игрока могут быть захвачены противником: фишка с двумя и более свободными соседними клетками (называемыми степенями свободы), не находится в положении атари. Подсчитать количество степеней свободы у фишки бывает нелегко, но когда оно известно, определить атари легко. Можно для начала написать такую функцию:

boolean atari(int libertyCount)

libertyCount < 2

Здесь больше, чем кажется на первый взгляд. Математическую функцию можно рассматривать как множество – некоторое подмножество декартова произведения области определения (здесь это int) и области изменения (здесь boolean). Если бы эти множества были одинакового размера, как в Java, то в множестве int×boolean было бы 2L*(Integer.MAX_VALUE+(–1L*Integer.MIN_VALUE)+1L), или 8,589,934,592 элементов. Половина из них принадлежит подмножеству, являющемуся нашей функцией, поэтому для полного доказательства корректности нашей функции нужно проверить около 4.3×109 случаев.

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

Выход подсказывает предметная область. Природа го такова, что число степеней свободы фишки является не любым целым числом, а одним из чисел {1,2,3,4}. Поэтому можно написать другой вариант кода:

LibertyCount = {1,2,3,4}

boolean atari(LibertyCount libertyCount)

libertyCount == 1

Это уже гораздо легче поддается обработке: вычисляемая функция – это множество, в котором от силы восемь элементов. Фактически, проверка четырех случаев может дать полную уверенность, что функция корректна. Это одна из причин, почему при написании программ лучше использовать типы, тесно связанные с проблемной областью, а не встроенные типы языка. Использование проблемно-ориентированных типов часто позволяет значительно уменьшить размер функций. Один из способов выяснить, какими должны быть эти типы, это найти примеры, регистрирующие термины предметной области, перед тем как писать функцию.

95. Тесты пишутся для людей


Герард Мешарос (Gerard Meszaros)

Вы пишете автоматизированные тесты для всего выходного кода или его части. Поздравляем! Вы пишете сначала тесты, а потом код? Еще лучше! Таким образом, вы одним из первых приняли передовую практику разработки программного обеспечения. Но хороши ли ваши тесты? Как это определить? Можно задаться вопросом: а для кого я пишу эти тесты? Если ответом будет «я пишу их для себя, чтобы сократить затраты на исправление ошибок» или «для компилятора, чтобы их можно было выполнить», то вполне возможно, что вы пишете не самые лучшие тесты. Так для кого же нужно писать тесты? Для того, кто будет пытаться понять ваш код.

Хорошие тесты играют роль документации для тестируемого ими кода. Они описывают, как работает код. Для каждого сценария применения тесты делают следующее:

  • Описывают контекст, начальную точку и предусловия, которые должны быть выполнены

  • Иллюстрируют, как вызывается приложение

  • Описывают предположительные результаты или постусловия, которые должны быть выполнены

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

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

Хорошая идея – протестировать ваши тесты. Можно проверить, смогут ли они обнаружить ошибки, которые должны находить, если ввести эти ошибки в выходной код (разумеется, в ваш личный экземпляр, который вы потом выкинете). Проверьте, чтобы сообщение об ошибке было полезным и содержательным. Нужно также проверить, что ваши тесты говорят языком, понятным тому, кто постарается понять ваш код. Это можно сделать, только если дать прочесть ваши тесты тому, кто не знаком с вашим кодом, и выяснить его впечатления. Если ему будет что-то непонятно, не нужно связывать это с недостатком у него умственных способностей. Более вероятно, что это вы не сумели написать ясный код. (Попробуйте поменяться ролями и почитать его тесты!)

96. Нужно любить код


Пит Гудлиф (Pete Goodliffe)

Не нужно быть Шерлоком Холмсом, чтобы выяснить, что хорошие программисты пишут хороший код. Ну, а плохие – нет. Они производят уродливые вещи, которые нам, всем остальным, приходится приводить в приличный вид. Вы же хотите писать хороший код, правда? Тогда вам нужно стремиться стать хорошим программистом.

Хороший код не возникает сам по себе из ничего. Его появление не вызвано благоприятным расположением планет. Чтобы сделать код хорошим, нужно над ним поработать, и немало. И хороший код получится у вас только тогда, когда вы действительно к этому стремитесь.

Хорошее программирование – результат не одной лишь технической компетентности. Я встречал очень умных программистов, умеющих создавать сильные и впечатляющие алгоритмы, в совершенстве знающих стандарты своего языка и пишущих совершенно отвратительный код. Его тяжело читать, с ним тяжело работать и его тяжело модифицировать. Я видел программистов с более скромными возможностями, тяготеющих к очень простому коду, но они пишут элегантные и выразительные программы, работать с которыми одно удовольствие.

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

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

Вы хотите писать хороший код. Вы хотите быть хорошим программистом. Тогда вы должны стремиться к следующему:

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

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

  • Вы хорошо ладите с другими программистами. Программист не должен быть отшельником. Редкий программист работает в одиночку: большинство трудится в составе команды программистов в рамках компании или проекта open source. Вы учитываете особенности других программистов и пишете код так, чтобы они могли его прочесть. Вы стремитесь к тому, чтобы команда написала как можно лучший продукт, а не стараетесь выставить себя в лучшем виде.

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

  • Вы любите код и программирование, поэтому вы постоянно изучаете новые языки, идиомы и технологии. Но применяете их только тогда, когда это уместно.

К счастью, вы читаете этот набор советов потому, что действительно любите код. Вам это интересно. Это ваше увлечение. Получайте удовольствие от программирования. Радуйтесь, написав код, решающий сложную задачу. Пишите программы, которыми можно гордиться.

97. Ваши заказчики имеют в виду не то, что говорят


Нэйт Джексон (Nate Jackson)

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

Они опускают важные детали. Они говорят так, будто вы, как и они уже лет 20 проработали в их компании. Это осложняется тем, что на самом деле заказчики часто сами не знают, что им нужно! У одних есть представление об общей картине, но они редко в состоянии умело рассказать о своем представлении. У других общее представление может быть более слабым, но они знают, чего им не нужно. Та как же можно разработать программный проект в интересах того, кто не может правдиво рассказать о том, что ему нужно? Выход достаточно прост. Нужно больше общаться с заказчиком.

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

Обсуждайте со своими заказчиками темы по нескольку раз, пока не решите, что вам понятно, что им требуется. Попробуйте вместе с ними переформулировать задачу два-три раза. Обсуждайте с ними, что происходит непосредственно перед обсуждаемой темой и сразу после нее, чтобы лучше понять контекст. Если есть возможность, обсудите ту же тему с разными людьми в разное время. Рассказ почти всегда будет разным, что выявит отдельные, но связанные факты. Два человека, рассказывающие вам об одном и том же, часто войдут в противоречие между собой. Лучше всего разобраться с этими расхождениями, и только потом начинать создавать свое сверхсложное приложение.

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

В прежнюю эпоху своей жизни я был «мультимедийным программистом» в команде, которая выпускала гламурные продукты. Одна наша заказчица очень подробно описала, как она представляет себе внешний вид проекта. Цветовая схема, обсуждавшаяся на проектных совещаниях, предполагала проведение презентации на черном фоне. Мы думали, что все оговорено. Бригады графических дизайнеров начали выдавать сотни многослойных графических файлов. Куча времени была потрачена на формирование конечного продукта. В тот день, когда мы показали клиентке плоды своего труда, нас ждала ошеломительная новость. Увидев продукт, она произнесла в отношении фона следующую фразу: «Когда я говорила "черный", я имела в виду "белый"». Как видите, "черное" не всегда значит "черное".


1http://www.netjeff.com/humor/item.cgi?file=ApolloComputer


Добавить документ в свой блог или на сайт

Похожие:

86. Две ошибки могут гасить одна другую (и тогда их трудно исправлять) iconОшибки при выборе профессии
Из огромного множества профессий выбрать одну, да еще такую, чтобы не было конфликта между "хочу", "могу", "надо", довольно трудно....
86. Две ошибки могут гасить одна другую (и тогда их трудно исправлять) iconУрока «Математика учит преодолевать трудности и исправлять собственные ошибки»
Повторить и обобщить знания учащихся по теме «Неравенства с одной переменной и их системы»
86. Две ошибки могут гасить одна другую (и тогда их трудно исправлять) iconУрок «Мои биологические исследования. Устройство микроскопа и работа...
Формирование умения работать по плану, сверять свои действия с целью и при необходимости исправлять ошибки самостоятельно
86. Две ошибки могут гасить одна другую (и тогда их трудно исправлять) iconУрок. Тема: «Иррациональные уравнения»
Цель: закрепить умение решать уравнения; способствовать развитию навыков самостоятельного применения знаний при решении уравнений;...
86. Две ошибки могут гасить одна другую (и тогда их трудно исправлять) iconЛистопадова Ольга Викторовна Предмет: Информатика Класс: 2 Тема: Последовательность событий
Цель: Научить определять последовательность событий, находить и исправлять ошибки в последовательности событий
86. Две ошибки могут гасить одна другую (и тогда их трудно исправлять) iconПрограмма по формированию навыков безопасного поведения на дорогах...
Воспитательные задачи: мотивировать учащихся на соблюдения правил здорового образа жизни, развивать умение слушать друг друга, распознавать...
86. Две ошибки могут гасить одна другую (и тогда их трудно исправлять) iconПрограмма по формированию навыков безопасного поведения на дорогах...
Воспитательные задачи: развивать умение работать вместе, развивать умение слушать друг друга, распознавать свои ошибки и уметь их...
86. Две ошибки могут гасить одна другую (и тогда их трудно исправлять) iconПравописание союзов тоже, также, зато, чтобы, оттого
Выставьте друг другу оценки: ни одной ошибки – «5», 1 ошибка – «4», 2 ошибки- «3», 3 ошибки и более- «2»
86. Две ошибки могут гасить одна другую (и тогда их трудно исправлять) iconПрограмма по формированию навыков безопасного поведения на дорогах...
Тема урока: Одна и две н в суффиксах страдательных причастий прошедшего времени. Одна буква н в отглагольных прилагательных. (Слайд...
86. Две ошибки могут гасить одна другую (и тогда их трудно исправлять) iconПрограмма по формированию навыков безопасного поведения на дорогах...
Закрепление и совершенствование умений писать слова с буквами безударных гласных в корне, подбирать проверочные слова, разграничивать...
86. Две ошибки могут гасить одна другую (и тогда их трудно исправлять) iconПрограмма по формированию навыков безопасного поведения на дорогах...
Цель урока: Повторить материал главы Работать над повышением интереса к математике. Вырабатывать навыки планирования действий, умения...
86. Две ошибки могут гасить одна другую (и тогда их трудно исправлять) iconАвторское методическое пособие для учителей физической культуры Учитель...
Во всех упражнения акцентировать внимание на узловых деталях в технике выполнения ранее изученных приемов. Нацеливать занимающихся...
86. Две ошибки могут гасить одна другую (и тогда их трудно исправлять) iconЭссе «Как технологии меняют мою страну и моё будущее»
Трудно назвать другую сферу человеческой деятельности, которая развивалась бы столь стремительно и порождала бы такое разнообразие...
86. Две ошибки могут гасить одна другую (и тогда их трудно исправлять) iconПротокол от 31. 08. 2011г. №1 согласована протокол заседания управляющего...
До революции село Вязовое являлось крупным волостным центром Грайворонского уезда Курской губернии. Здесь до 1917 года действовали...
86. Две ошибки могут гасить одна другую (и тогда их трудно исправлять) iconМетодические рекомендации Обязанности воспитателя могут быть разделены...
Обязанности воспитателя могут быть разделены на две группы: повседневные и эпизодические. К повседневным обязанностям относится организация...
86. Две ошибки могут гасить одна другую (и тогда их трудно исправлять) iconПрограмма по формированию навыков безопасного поведения на дорогах...
Школа снова открыла двери в Страну знаний. Здесь мы будем учиться дружить и говорить правду, учиться исправлять свои ошибки и радоваться...


Школьные материалы


При копировании материала укажите ссылку © 2013
контакты
100-bal.ru
Поиск