97 этюдов для программистов. Опыт ведущих экспертов — страница 34 из 41

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

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

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

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

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

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

Пишите маленькие функции на основе примеровКейт Брэйтуэйт

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

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

boolean atari(int libertyCount)

   libertyCount < 2

Здесь спрятано больше, чем кажется на первый взгляд. Математическую функцию можно рассматривать как множество — некоторое подмножество декартова произведения области определения (здесь это int) и области принимаемых значений (здесь — boolean). Будь эти множества одинакового размера, как в Java, в множестве int х boolean было бы 2L*(Integer.MAX_VALUE+(—1L*Integer.MIN_VALUE)+1L), или 8589934592 элементов. Половина из них принадлежит подмножеству, являющемуся нашей функцией, поэтому для полного доказательства корректности нашей функции нужно проверить около 4,3 х 109 случаев.

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

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

LibertyCount = {1,2,3,4}

boolean atari(LibertyCount libertyCount)

   libertyCount == 1

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

Тесты пишутся для людейДжерард Мезарос

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

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

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

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

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

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

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

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

Нужно заботиться о кодеПит Гудлиф

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

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

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