Как написать легкий код

Как написать легко описываемый код

Время на прочтение
2 мин

Количество просмотров 4.3K

Привет, Хабр!

Часто ли у вас было, что вы или ваши коллеги не могли описать свой собственный код парочкой фраз?

Предлагаю вашему вниманию перевод статьи «How to write easily describable code» автора Cedd Burge, в которой он делится советом, как избежать таких ситуаций.

image

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

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

Пример неописуемого кода

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

Ниже приведенное общее решение определяет, является ли год високосным.

(divisibleBy(4) and not divisibleBy(100)) or divisibleBy(400)

Это простой код. Он вызывает функции 3 раза, имеет 3 оператора (и, или, нет) и два уровня вложенности.

Но я думаю, что, если вам дадут одну секунду для описания этого алгоритма с помощью слов, у вас появятся трудности.

Может быть, «год является високосным, если он делится на 4 и не делится на 100, или делится на 400»?

Проблема состоит в том, что в словах, в отличии от кода, нет скобок. Поэтому словами сложно адекватно описать условие и то, относится ли «или делится на 400» к «делится на 4» или к «не делится на 400». Для обхода этой проблемы, вы можете указывать скобки рукой или делать более продолжительные паузы между условиями, но вероятность ошибки всё равно останется большой.

Рефакторинг описываемого кода

Мы можем сперва описать условия словами и уже потом сокращать их, сделав как можно яснее и лаконичнее. Начнем так:

«400 лет — это уникальный случай. Если год делится на 400, то это високосный год. 100 лет — это тоже уникальный случай. Если год делится на 100, то это не високосный год, если только он не делится на 400, таким образом, приоритет у особого случая «400 лет». Если особые случаи отсутствуют, то этот год — високосный, при условии, что он делится на 4».

Звучит понятно, но не лаконично, поэтому мы немного сократим текст:
«Если год делится на 400, то он високосный. Если же он делится на 100, то это обычный год, но при делении на 4, это високосный год».

Если превратим эти слова в код, мы получим что-то следующее:

if divisbleBy(400):
        return LeapYear
    elif divisbleBy(100)
        return NormalYear
    elif divisbleBy(4):
        return LeapYear
    else:
        return NormalYear

Выводы

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

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


Загрузить PDF


Загрузить PDF

По мере того как технология становится все более и более доступной широкой публике, растет и потребность в программистах. Написание компьютерных кодов и программ, оно же кодинг (от английского «сoding») — это навык, который приобретается и совершенствуется на протяжении долгого времени, но даже самый опытный программист когда-то был новичком. Существует большое разнообразие языков программирования, которые великолепно подходят для начинающих программистов, вне зависимости от того, в какой сфере деятельности вы хотите применять ваши навыки (например, JavaScript довольно сложен, так что лучше начать с HTML или CSS). Узнайте, как научиться писать компьютерные программы, прочитав эту статью.

  1. Изображение с названием Code Step 1

    1

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

    • Выбирая язык, сконцентрируйтесь на том, в каких целях вы хотите создавать программные коды, и уже потом выбирайте начальный язык. К примеру, если вы хотите заниматься разработкой веб-сайтов, то вам следует начать с изучения HTML5, а затем дополнить его языками CSS, JavaScript и PHP. Если вы хотите создавать компьютерные программы, то начните изучать C++ или любой другой основной язык программирования.
    • Если вы станете профессиональным программистом, то вы можете обнаружить, что никогда не используете язык, который вы изначально выучили, для своей работы. Вместо этого вы будете все время продолжать учить новые языки через документацию и эксперименты.
  2. Изображение с названием Code Step 2

    2

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

    • Вот только несколько популярных сайтов: Bento, CodeAcademy, Code.org, html.net, Khan Academy, Udacity, W3Schools и многие другие.
    • На wikiHow тоже можно найти разнообразные инструкции для начинающих изучать языки программирования.
    • Вы можете найти обучающие видео практически для любого языка на YouTube.
    • Stack Exchange — это один из самых популярных форумов, на котором профессиональные программисты отвечают на любые вопросы пользователей.

    СОВЕТ СПЕЦИАЛИСТА

    Арчана Рамамурти — технический директор Workday (Северная Америка). Высококлассный специалист по продуктам, поборница безопасности, сторонница большего распространения интеграции на равных для всех условиях в индустрии технологий. Получила степень бакалавра в Университете SRM и магистра в Университете Дьюка. Работает в области продакт-менеджмента более восьми лет.

    Archana Ramamoorthy, MS

    Наш специалист делится своей историей:: «Я пришла к написанию кодов, не зная ничего ни о компьютерном дизайне, ни о программировании. Когда я захотела научиться писать программы, я начала с чтения книг по языку и с использования информации из интернета. Сегодня в мире доступно так много ресурсов, что научиться новым навыкам очень легко!»

  3. Изображение с названием Code Step 3

    3

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

    • К популярным программам относятся Notepad++ (Windows), TextWrangler (OS X) и JEdit (любая система).
  4. Изображение с названием Code Step 4

    4

    Скачайте любые необходимые компиляторы. Некоторые языки программирования требуют компилятор для отображения исходного кода, который вы написали. Компиляторы транслируют исходный код в эквивалентную программу на языке низкого уровня, которая затем обрабатывается компьютером. Многие компиляторы бесплатны и находятся в открытом доступе. Языки, которые требуют использования компиляторов, включают:

    • C;
    • C++;
    • C#;
    • Java;
    • BASIC;
    • Fortran.
  5. Изображение с названием Code Step 5

    5

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

  6. Изображение с названием Code Step 6

    6

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

    • Функцию комментирования можно использовать для того, чтобы быстро убрать часть кода из программы в целях тестирования. Поставьте теги как для комментария в начале и в конце кода, который вы хотите временно исключить из программы, а затем удалите эти теги, чтобы вернуть код.
  7. Изображение с названием Code Step 7

    7

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

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

    Реклама

  1. Изображение с названием Code Step 8

    1

    Запишитесь на курсы. Университеты, колледжи и интернет-программы предлагают программы и курсы, которые не только научат вас программированию, но и помогут вам найти работу. Хотя университетский диплом по специальности программиста не всегда нужен, он может помочь вам найти постоянную работу в качестве программиста.[2]

    • Неоспорима и польза от прямого общения с учителем или специалистом в области программирования, которое не всегда доступно через онлайн-курсы.
    • Обучение на программиста может обойтись вам довольно дорого, поэтому внимательно подумайте, стоит ли оно того. Если вы увлекаетесь написанием компьютерных программ только в качестве хобби, то вам не стоит тратить время и деньги на профессиональные курсы. Если же вы хотите построит карьеру в этой области, то получение профессионального образования может значительно вам в этом помочь (но, опять же, оно необязательно, если у вас есть талант).
  2. Изображение с названием Code Step 9

    2

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

    • Программирование, включающее физические расчеты и моделирование, требует хорошего понимания алгоритмов и моделей.
    • Логика — фундаментальная основа программирования, так что понимание логики и процессов поможет вам решать проблемы при написании кода.
    • Знание высшей математики чаще всего не требуется для программирования, однако его можно использовать для оптимизации и других преимуществ.
  3. Изображение с названием Code Step 10

    3

    Выучите больше языков. Когда вы хорошо разобрались с вашим первым языком, вы можете начать учить и другие языки программирования. Найдите язык, который дополняет тот, который вы уже знаете, или же выберите язык, который необходим для определенного проекта. Дополнительные языки, вроде HTML и CSS, как правило, легче всего выучить.

    • Java — один из самых популярных языков, и Java-разработчики всегда очень востребованы. Java используется в самых разных системах и имеет бесчисленное множество вариантов применения. На Java пишут приложения для Android — один из самых быстрорастущих рынков.
    • C++ очень рекомендуется, если вы хотите влиться в ряды разработчиков видеоигр. Если вы научитесь программировать на Unity (широко используемый и дешевый игровой движок) и UDK (код для популярного движка Unreal), это откроет перед вами некоторые двери, однако они не так полезны за пределами игровой индустрии.
    • Если вы хотите создавать приложения для iPhone, Xcode и Objective-C будут вашими первейшими инструментами. Вам понадобится компьютер Mac, посколько компилировать Xcode можно только на Mac.
    • Python — язык для написания серверного кода, один из наиболее легких в изучении. Python используется для таких интернет-сервисов, как Pinterest и Instagram, и он достаточно прост, чтобы изучить основы всего за несколько дней.
  4. Изображение с названием Code Step 11

    4

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

  5. Изображение с названием Code Step 12

    5

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

  6. Изображение с названием Code Step 13

    6

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

  7. Изображение с названием Code Step 14

    7

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

  8. Изображение с названием Code Step 15

    8

    Практикуйтесь, практикуйтесь и практикуйтесь. По некоторым оценкам, нужно заниматься программированием около 15 000 часов, прежде чем вас можно будет считать настооящим мастером.[3]
    Это означает годы постоянной практики. Вы по-настоящему овладеете мастерством программирования, только когда вложите много времени в практическую работу и станете знатоком своего дела..

    • Старайтесь заниматься программированием каждый день, даже в выходные. Программирование в свободное время может способствовать прорывам и возникновению новых идей.

    Реклама

Об этой статье

Эту страницу просматривали 91 019 раз.

Была ли эта статья полезной?

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

Почему важна читаемость кода

Сначала учимся читать и писать код, потом читать и переписывать написанный другими

Сначала учимся читать и писать код, потом читать и переписывать написанный другими

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

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

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

<a href="https://artsandculture.google.com/asset/the-index-connections-in-the-computer-centre/oQFmHS7CiJvayg" target="_blank" rel="noopener noreferrer nofollow">В компьютерном центре ЦЕРН, 1983 г.</a>

В компьютерном центре ЦЕРН, 1983 г.

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

Стиль кода определяет его будущее. Стиль и дисциплина продолжают жить в коде, даже если в нем не осталось ни одной исходной строки.

С чего начать: документация по стилю оформления кода

Все дороги программиста ведут к документации. В каждом языке существует свой стандарт оформления кода. Для Python используется документ PEP-8, для PHP – стандартные рекомендации PSR-1 и PSR-2, для Java – Java Coding Conventions, для JavaScript – Airbnb JavaScript Style Guide или Google JavaScript Style Guide. Документ для вашего языка вы найдете по поисковому запросу <Название языка> Code Style.

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

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

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

Роберт Мартин «Чистый код. Создание, анализ и рефакторинг»

Как Библиотека программиста, мы не могли обойтись без упоминания замечательной книги Роберта Мартина о чистом коде и анализе программ. В книге приводятся примеры для языка Java, но большинство идей справедливы для любых языков.

Если вы видели эту книгу ранее с другим оформлением, не удивляйтесь – это новая версия обложки книги «Чистый код»

Если вы видели эту книгу ранее с другим оформлением, не удивляйтесь – это новая версия обложки книги «Чистый код»

Книга в сообществе Книги для программистов

Книга на Ozon

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

Главное правило чистого кода: выразительные имена

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

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

Сравните. До:

        public List < int[] > getThem() {
 List < int[] > list1 = new ArrayList < int[] > ();
 for (int[] x: theList)
  if (x[0] == 4)
   list1.add(x);
 return list1;
}
    

После:

        public List < int[] > getFlaggedCells() {
 List < int[] > flaggedCells = new ArrayList < int[] > ();
 for (int[] cell: gameBoard)
  if (cell[STATUS_VALUE] == FLAGGED)
   flaggedCells.add(cell);
 return flaggedCells;
}
    

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

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

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

Путаница также возникает, если несколько синонимичных слов и выражений используются для обозначениях разных сущностей, например, controller, manager и driver.

Имя должно легко произноситься. Используйте для названий слова. Если названия состоят из сокращений, каждый начинает произносить их по-своему, что затрудняет взаимопонимание. А при чтении кода каждый раз «спотыкаешься» о такое название.

Имя должно быть удобным для поиска. Слишком короткие имена трудно искать в большом объеме текста. Однобуквенные имена можно использовать только для локальных переменных в коротких методах и для счетчиков циклов (i, j, k). Обычно называя объект одной буквой, вы всего лишь создаете временный заменитель. Но не бывает ничего более постоянного, чем что-то «временное». Проверяйте грамотность написания выбранных слов.

Правильно выбирайте часть речи. Классы и объекты желательно называть существительными и их комбинациями: Account, WikiPage, HTMLParser. Имена функций и методов лучше представлять глаголами или глагольными словосочетаниями: delete_page, writeField(name). Для методов чтения/записи и предикатов используйте стандартные префиксы get, set, is.

Заменяйте «магические» числа именованными константами. Одно из самых древних правил разработки. Магическими называют числа, о которых сходу нельзя сказать, что они означают. Например: 100, 1.1, 42, 1000000. Выделяйте такие числа в соответствующие константы с конкретным названиями. Например, вместо числа 86400 в теле кода приятнее встретить константу SECONDS_PER_DAY.

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

Одно слово для каждой концепции. Для одной и той же идеи, реализующей одну механику, используйте одно слово. Например, для добавления элементов одинаковым образом – метод add. Однако, если механика и семантика изменились, потребуется и другое слово (например, insert, append), описывающее новую концепцию.

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

Помещайте имена в соответствующий контекст. Например, имена street, house_number, city понятнее смотрятся внутри класса Address.

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

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

Функции

Компактность. Уже в 80-е годы считалось, что функция должна занимать не более одного экрана. Экраны VT100 состояли из 24 строк и 80 столбцов. В наши дни на экране можно разместить гораздо больше инфорфмации, но лучше ограничиться тем же объемом. Самоограничение позволяет видеть точку объявления каждой используемой переменной и держать в уме всю «историю», которую рассказывает функция.

Внешний вид текстового компьютерного терминала <a href="https://ru.wikipedia.org/wiki/VT100" target="_blank" rel="noopener noreferrer nofollow">VT100</a>

Внешний вид текстового компьютерного терминала VT100

Вполне вероятно, что тот, кто будет сопровождать ваш код, не будет иметь возможности работать на большом мониторе. Например, ему необходимо одновременно разместить на одном рабочем столе экрана ноутбука несколько окон. Среды разработки позволяют установить ограничение, «верхнюю планку» (то есть правую 😉 ).

Блоки if, else, while должны иметь минимальный размер, чтобы информацию о них можно было держать в уме. Старайтесь избегать отрицательных условий – на их восприятие обычно уходит чуть больше времени, чем на положительные. То есть запись if (buffer.shouldCompact()) предпочтительнее записи if (!buffer.shouldNotCompact().

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

Функция должна выполнять только одну операцию, выполнять ее хорошо, и ничего другого она делать не должна.

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

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

Исключения вместо кодов ошибок. Используйте исключения (try-catch, try-except) вместо возвращения кодов ошибок. Возвращение кодов приводит к слишком глубокой вложенности.

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

Соблюдайте уровни абстракции. Одна функция – один уровень абстракции. Смешение уровней абстракции создает путаницу, функция обрастает слишком большим количеством второстепенных подробностей. Старайтесь соблюдать ясную иерархию.

Код читается сверху вниз. По мере чтения уровни абстракции должны меняться равномерно. Каждая функция должна быть окружена функциями единого уровня абстракции.

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

Комментарии

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

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

Однако есть несколько видов комментариев, которые выглядят достаточно оправданными.

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

Юридические комментарии. Корпоративные стандарты могут принуждать вставлять комментарии по юридическим соображениям. Ограничьтесь в таком комментарии описанием лицензии и ссылкой на внешний документ.

Предупреждения о последствиях. Иногда бывает полезно предупредить других программистов о нежелательных последствиях:

        // Не запускайте, если только не располагаете
// излишками свободного времени.
    

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

По-настоящему плохие комментарии

Бывают такие типы комментариев, которые лучше никогда не делать.

Закомментированный программный код. «Когда-нибудь в будущем раскомментирую этот код, приведу всё в порядок. Или вдруг эта идея кому-то поможет». Любой закомментированный код только ухудшает ситуацию. Все изменения хранятся в контроле версий – удаляйте такой код на корню. Это просто мусор: «потом» равносильно «никогда». Если что-то действительно нужно сделать, создайте краткий TODO-комментарий и задачу.

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

Избыточные комментарии. Задайте себе вопрос: стал ли код понятнее после прочтения комментария? Часто комментарии просто загромождают код и скрывают его смысл, излагая очевидные вещи. Иногда в комментарии включаются описания не относящихся к делу подробностей. Но профессионал бережет не только свое, но и чужое время, и не отвлекает читающего без повода.

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

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

        // Классы //////////////////////////////////
    

Качественно организованный код способен внятно рассказать историю без балластных заголовков.

Уровень файлов программ

Минималистичность. Чем меньше кода, тем лучше. Имя файла должно быть простым, но содержательным. Маленькие файлы обычно более понятны, чем большие. Но размер файла, конечно, не должен быть самоцелью.

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

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

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

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

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

Некоторые замечания по поводу архитектуры и тестов

<a href="https://artsandculture.google.com/asset/protection-for-radio-computer-monitor/ugGMqStdU_OSRw" target="_blank" rel="noopener noreferrer nofollow">Компьютер в центре по контролю воды, 1980 г.</a>

Компьютер в центре по контролю воды, 1980 г.

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

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

  1. Не пишете код продукта, пока не напишете отказной модульный тест.
  2. Не пишите модульный тест в объеме большем, чем необходимо для отказа.
  3. Не пишите код продукта в объеме большем, чем необходимо для прохождения текущего отказного теста.

F.I.R.S.T. Качественные тесты должны обладать пятью характеристиками, первые буквы которых образуют указанный акроним:

  • Fast. Тесты должны выполняться быстро.
  • Independent. Тесты не должны зависеть друг от друга и выполняться в любом порядке.
  • Repeatable. Тесты должны давать воспроизводимые в любой среде результаты.
  • Self-validating. Результат выполнения теста – логический признак: тест пройден или нет. Иначе результаты приобретают субъективный характер.
  • Timely. Тест должен создаваться своевременно. Тесты нужно писать непосредственно перед написанием кода.

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

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

Несколько языков в одном исходном файле. Современные среды программирования позволяют объединять в одном файле код, написанный на разных языках. Результат получается запутанным, неаккуратным и ненадежным. Чтобы четко разграничить ответственность, в файле должен преобладать один язык. Сведите к минимуму количество и объем кода на дополнительных языках.

Не нужно бездумно следовать догмам. Не переусердствуйте с сокращением кода функций и классов. Всегда руководствуйтесь здравым смыслом.

Заключение

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

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

Расскажите нам о правилах, которые вы применяете для написания своего программного кода. Какие open source программы, на ваш взгляд, имеют лучшее качество кода?

В этой главе мы напишем первую программу на C++ и научимся печатать и считывать с клавиатуры строки и числа.

Функция main

Пожалуй, самая простая и короткая программа на C++ — это программа, которая ничего не делает. Она выглядит так:

int main() {
    return 0;
}

Здесь определяется функция с именем main, которая не принимает никаких аргументов (внутри круглых скобок ничего нет) и не выполняет никаких содержательных команд. В каждой программе на C++ должна быть ровно одна функция main — с неё начинается выполнение программы.

У функции указан тип возвращаемого значения int (целое число), и она возвращает 0 — в данном случае это сообщение для операционной системы, что программа выполнилась успешно. И наоборот, ненулевой код возврата означает, что при выполнении возникла ошибка (например, программа получила некорректные входные данные).

Для функции main разрешается не писать завершающий return 0, чем мы и будем пользоваться далее для краткости. Поэтому самую короткую программу можно было бы написать вот так:

int main() {
}

Hello, world!

Соблюдая традиции, напишем простейшую программу на C++ — она выведет приветствие в консоль:

#include <iostream>

int main() {
    std::cout << "Hello, world!n";
    return 0;
}

Разберём её подробнее.

Директива #include <iostream> подключает стандартный библиотечный заголовочный файл для работы с потоками ввода-вывода (input-output streams). Для печати мы используем поток вывода std::cout, где cout расшифровывается как character output, то есть «символьный вывод».

В теле функции main мы передаём в std::cout строку Hello, world! с завершающим переводом строки n. В зависимости от операционной системы n будет преобразован в один или в два управляющих байта с кодами 0A или 0D 0A соответственно.

Инструкции внутри тела функции завершаются точками с запятой.

Компиляция из командной строки

Вы можете запустить эту программу из какой-нибудь IDE. Мы же покажем, как собрать её в консоли Linux с помощью компилятора clang++.

Пусть файл с программой называется hello.cpp. Запустим компилятор:

$ clang++ hello.cpp -o hello

В результате мы получим исполняемый файл с именем hello, который теперь можно просто запустить. Он напечатает на экране ожидаемую фразу:

$ ./hello
Hello, world!

Если опцию -o не указать, то сгенерированный исполняемый файл будет по умолчанию назван a.out. В дальнейшем для простых примеров мы будем использовать краткую форму записи команды:

$ clang++ hello.cpp && ./a.out
Hello, world!

С её помощью мы компилируем программу и в случае успеха компиляции сразу же запускаем.

Комментарии

Комментарии — это фрагменты программы, которые игнорируются компилятором и предназначены для программиста. В C++ есть два вида комментариев — однострочные и многострочные:


int main() {  // однострочный комментарий продолжается до конца строки

/* Пример
   многострочного
   комментария */
}

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

Хорошо: комментировать, что делает библиотека, функция или класс или почему этот код написан именно так.

Плохо: комментировать, что происходит на отдельной строчке. Это признак того, что код можно написать лучше.

Библиотеки и заголовочные файлы

Библиотека — это код, который можно переиспользовать в разных программах. В стандарт языка C++ входит спецификация так называемой стандартной библиотеки, которая поставляется вместе с компилятором. Она содержит различные структуры данных (контейнеры), типовые алгоритмы, средства ввода-вывода и т. д. Конструкции из этой библиотеки предваряются префиксом std::, который обозначает пространство имён.

Чтобы воспользоваться теми или иными библиотечными конструкциями, в начале программы надо подключить нужные заголовочные файлы. Так, в программе, которая печатала Hello, world!, нам уже встречался заголовочный файл iostream и конструкция std::cout из стандартной библиотеки.

Для C++ существует также множество сторонних библиотек. Наиболее известной коллекцией сторонних библиотек для C++ является Boost.

Ошибки компиляции

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

Рассмотрим пример такой программы:

#include <iostream>

int main() {
    cout << "Hello, worldn"

Компилятор может выдать такие сообщения:

$ clang++ hello.cpp
hello.cpp:4:5: error: use of undeclared identifier 'cout'; did you mean 'std::cout'?
    cout << "Hello, world!n"
    ^~~~
    std::cout

hello.cpp:4:30: error: expected ';' after expression
    cout << "Hello, world!n"
                             ^
                             ;

hello.cpp:5:1: error: expected '}'
^
a.cpp:3:12: note: to match this '{'
int main() {
           ^
3 errors generated.

Первая ошибка — вместо std::cout мы написали cout. Вторая ошибка — не поставили точку запятой после "Hello, world!n". Наконец, третья – не закрыли фигурную скобку с телом функции.

Ошибки компиляции (compile errors) следует отличать от возможных ошибок времени выполнения (runtime errors), которые происходят после запуска программы и, как правило, зависят от входных данных, неизвестных во время компиляции.

Отступы и оформление кода

Фрагменты программы на C++ могут быть иерархически вложены друг в друга. На верхнем уровне находятся функции, внутри них написаны их тела, в теле могут быть составные операторы, и так далее.

Среди программистов есть соглашение — писать внутренние блоки кода с отступами вправо: компилятор полностью игнорирует эти отступы, а код читать удобнее. Мы будем использовать отступы в четыре пробела. Также мы будем придерживаться стиля оформления кода, принятого в Яндексе. Имена переменных мы будем писать с маленькой буквы, имена функций и классов — с большой (если речь не идёт о конструкциях стандартной библиотеки, где действуют другие соглашения).

Переменные

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

Type name;

где вместо Type — конкретный тип данных (например, строка или число), а вместо name — имя переменной. Имена переменных должны состоять из латинских букв, цифр и знаков подчёркивания и не должны начинаться с цифры. Также можно в одной строке определить несколько переменных одного типа:

Type name1 = value1, name2 = value2, name3 = value3;

Например:

#include <string>  // библиотека, в которой определён тип std::string

int main() {
    // Определяем переменную value целочисленного типа int
    int value;

    // Определяем переменные name и surname типа std::string (текстовая строка)
    std::string name, surname;
}

В этом примере мы используем встроенный в язык тип int (от слова integer — целое число) и поставляемый со стандартной библиотекой тип std::string. (Можно было бы использовать для строк встроенный тип с массивом символов, но это неудобно.)

Тип переменной должен быть известен компилятору во время компиляции.

От типа зависит:

  • сколько байтов памяти потребуется для хранения данных;
  • как интерпретировать эти байты;
  • какие операции с этой переменной возможны.

Например, переменной типа int можно присваивать значения и с ней можно производить арифметические операции. Подробнее про разные типы данных и их размер в памяти мы поговорим ниже.

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

int main() {
    int value;
    value = 42;  // OK
    value = "Hello!";  // ошибка компиляции!
}

Переменные можно сразу проинициализировать значением. В С++ есть много разных способов инициализации. Нам пока будет достаточно способа, который называется copy initialization:

#include <string>

int main() {
    int value = 42;
    std::string title = "Bjarne Stroustrup";
}

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

Потоковый ввод и вывод

Поток — это абстракция для чтения и записи последовательности данных в форматированном виде.

Записывать данные можно на экран консоли, в файл, буфер в памяти или в строку. Считывать их можно с клавиатуры, из файла, из памяти. Причём с каждым таким «устройством» можно связать свой поток.

Важно, что потоки не просто пересылают байты памяти, а применяют форматированный человекочитаемый ввод-вывод. Например, числа печатаются и считываются в десятичной нотации, хотя в памяти компьютера они хранятся в двоичном виде.

В программе Hello, world! нам уже встречался поток вывода std::cout, по умолчанию связанный с экраном консоли. Познакомимся с потоком ввода std::cin, связанным с клавиатурой. Для его использования нужен тот же заголовочный файл iostream.

Рассмотрим программу, которая спрашивает имя пользователя и печатает персональное приветствие:

#include <iostream>
#include <string>

int main() {
    std::string name;  // объявляем переменную name
    std::cout << "What is your name?n";
    std::cin >> name;  // считываем её значение с клавиатуры
    std::cout << "Hello, " << name << "!n";
}

Обратите внимание на направление угловых скобок в этом примере — они условно показывают направление потока данных. При печати данные выводятся на экран, и стрелки направлены от текста к cout. При вводе данные поступают с клавиатуры, и стрелки направлены от cin к переменной.

В нашем примере в переменную name считается одно слово, которое будет выведено в ответном сообщении. Пример работы программы:

What is your name?
Alice
Hello, Alice!

Однако если ввести строку из нескольких слов с пробелами, то в name запишется только первое слово:

$ ./a.out
What is your name?
Alice Liddell
Hello, Alice!

Дело в том, что cin читает поток данных до ближайшего пробельного разделителя (пробела, табуляции, перевода строки или просто конца файла). Чтобы считать в строковую переменную всю строчку целиком (не включая завершающий символ перевода строки), нужно использовать функцию std::getline из заголовочного файла string:

#include <iostream>
#include <string>

int main() {
    std::string name;
    std::getline(std::cin, name);
    std::cout << "Hello, " << name << "!n";
}

В этом примере мы печатаем в одном выражении друг за другом несколько строк ("Hello, ", name и "!n"), разделённых угловыми скобками <<. Таким образом, cin и cout позволяют кратко считывать и печатать несколько объектов одной командой.

Например, считывание нескольких чисел целого типа, набранных через пробельные разделители, может выглядеть так:

int main() {
    int a;
    int b;
    int c;
    std::cin >> a >> b >> c;
}

Напечатать их значения можно следующим образом:

std::cout << a << " " << b << " " << c << "n";

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

How to write easily describable code

When code is not describable using words, most people have to do some mental mapping to turn it in to words. This wastes mental energy, and you run the risk of getting the mapping wrong. Different people will map to different words, which leads to confusion when discussing the code.

This is usually a fertile breeding ground for bugs born out of miscommunication / misunderstanding, and fixing these bugs often introduces new ones, for the same reasons. In the end it becomes code that no one really understands or wants to touch.

Example of undescribable code

It is easy to think that code is already a written language. If it looks simple, it should be easy to read, speak and listen to. However, this is not always the case.

Below is a common solution to deciding whether a year is a leap year.

(divisibleBy(4) and not divisibleBy(100)) or divisibleBy(400)

This is not overly complicated code. It calls a functions 3 times, has 3 operators (and, or, not), and has two levels of nesting.

However, if you take a second to try and describe the algorithm in words I think you will find it to be a struggle.

Maybe “A year is leap year if it is divisible by 4 and not divisible by 100, or divisible by 400”?

The trouble with this is that the code has brackets, but the words do not. So they cannot adequately describe the condition, and whether “or divisible by 400” applies to “divisible by 4” or “not divisible by 400”. You could try some hand waving and gesturing to get around this, or vary the length of pause between the statements, but hopefully it’s obvious that there is a lot of potential for error.

Refactoring to describable code

Instead we can start by describing the condition with words, and then make the words as clear and concise as possible. We might start with this:

“400 years is a special case. If a year is divisible by 400, then it is a leap  year. 100 years is also a special case. If a year is divisible by 100 then it isn’t a leap year, unless it is also divisble by 400, the 400 year special case takes priority. If there are no special cases, then the year is a leap year if it is divisible by 4.”

This is clear, but isn’t concise, so we would probably want to shrink it a bit:

“If a year is divisible by 400, then it is a leap year. Otherwise if it is divisible by 100 then it is a normal year, otherwise it is a leap year if it is divisible by 4.”

If we turn these words in to code, we probably get something like the following:

	if divisbleBy(400):
		return LeapYear
	elif divisbleBy(100)
		return NormalYear
	elif divisbleBy(4):
		return LeapYear
	else:
		return NormalYear

Conclusions

Hard to understand code is a daily occurrence for virtually all programmers. We can help ourselves and our co-workers by writing code that is easy to describe in words.

And the great thing is that doing so is actually easier than writing code any other way, as there is no mental mapping / wasted mental effort. The only “trick” is to describe the algorithm in words, and then write code to match the words.

In many organisations, the algorithm will already be described in words, as part of acceptance tests or user stories, which will improve productivity even further.



Learn to code for free. freeCodeCamp’s open source curriculum has helped more than 40,000 people get jobs as developers. Get started

How to write easily describable code

When code is not describable using words, most people have to do some mental mapping to turn it in to words. This wastes mental energy, and you run the risk of getting the mapping wrong. Different people will map to different words, which leads to confusion when discussing the code.

This is usually a fertile breeding ground for bugs born out of miscommunication / misunderstanding, and fixing these bugs often introduces new ones, for the same reasons. In the end it becomes code that no one really understands or wants to touch.

Example of undescribable code

It is easy to think that code is already a written language. If it looks simple, it should be easy to read, speak and listen to. However, this is not always the case.

Below is a common solution to deciding whether a year is a leap year.

(divisibleBy(4) and not divisibleBy(100)) or divisibleBy(400)

This is not overly complicated code. It calls a functions 3 times, has 3 operators (and, or, not), and has two levels of nesting.

However, if you take a second to try and describe the algorithm in words I think you will find it to be a struggle.

Maybe “A year is leap year if it is divisible by 4 and not divisible by 100, or divisible by 400”?

The trouble with this is that the code has brackets, but the words do not. So they cannot adequately describe the condition, and whether “or divisible by 400” applies to “divisible by 4” or “not divisible by 400”. You could try some hand waving and gesturing to get around this, or vary the length of pause between the statements, but hopefully it’s obvious that there is a lot of potential for error.

Refactoring to describable code

Instead we can start by describing the condition with words, and then make the words as clear and concise as possible. We might start with this:

“400 years is a special case. If a year is divisible by 400, then it is a leap  year. 100 years is also a special case. If a year is divisible by 100 then it isn’t a leap year, unless it is also divisble by 400, the 400 year special case takes priority. If there are no special cases, then the year is a leap year if it is divisible by 4.”

This is clear, but isn’t concise, so we would probably want to shrink it a bit:

“If a year is divisible by 400, then it is a leap year. Otherwise if it is divisible by 100 then it is a normal year, otherwise it is a leap year if it is divisible by 4.”

If we turn these words in to code, we probably get something like the following:

	if divisbleBy(400):
		return LeapYear
	elif divisbleBy(100)
		return NormalYear
	elif divisbleBy(4):
		return LeapYear
	else:
		return NormalYear

Conclusions

Hard to understand code is a daily occurrence for virtually all programmers. We can help ourselves and our co-workers by writing code that is easy to describe in words.

And the great thing is that doing so is actually easier than writing code any other way, as there is no mental mapping / wasted mental effort. The only “trick” is to describe the algorithm in words, and then write code to match the words.

In many organisations, the algorithm will already be described in words, as part of acceptance tests or user stories, which will improve productivity even further.



Learn to code for free. freeCodeCamp’s open source curriculum has helped more than 40,000 people get jobs as developers. Get started

[Эта статья была написана Дэвидом Старром]

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

Есть некоторые принципы, с которыми разработчики склонны соглашаться — особенно, как выразить свое намерение в коде Попросите программиста назвать некоторые из них, и вы, вероятно, услышите что-то вроде этого:

  • Хорошо названные переменные, методы и класс
  • Последовательное форматирование
  • Маленькие методы
  • Минимальная вложенность и кодовые ветви

Давайте возьмем некоторые из этих идей и применим их на практике, начиная с функционального, если не совсем понятно, кусочка кода и улучшая его.

Улучшение вашего кода: боулинг

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

Модель ниже показывает игру в боулинг и как она забивается. Забастовки показаны как X, а запасные части показаны как /.

Боулинг

Загружаемый zip-файл содержит 2 рабочих примера решений ( до  и послеката для боулинга Боба Мартина ; если вы не слышали об этом, это отличное упражнение для изучения. Класс Game является предметом этой статьи. Метод Roll () класса Game принимает количество выбитых кеглей, когда мяч катится по переулку, и предоставляет метод GetScore () для определения итоговой оценки боулера.

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

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

int[] _rolls = new int[21];

int _rollIndex = 0;

internal void Roll(int i)

{

_rolls[_rollIndex++] = i;

}

Следующее, что может улучшить ситуацию, — это имя переменной, переданной в метод Roll. Чего ожидает этот метод? Очевидно, целое число, но что это целое представляет? Вот что показывает моя IDE, когда я смотрю на этот метод с точки зрения вызывающего.

game1

Не самый интуитивно понятный метод подписи, не так ли? Если я хочу узнать, что представляет собой «i», мне, вероятно, нужно пойти в этот класс и посмотреть на детали, чтобы выяснить намерение. Изображение ниже — то, что я вижу после переименования переменной, чтобы лучше соответствовать цели метода Roll (). Это более интуитивный метод подписи. Я вижу, что параметр представляет количество пинов, сбитых за один бросок.

Game2

Теперь давайте обратим внимание на имена переменных в методе GetScore () в  ранее примере, который является сердцем этого класса. Глядя на метод GetScore (), вы можете увидеть, что есть некоторые переменные с менее интуитивными именами. Вот как выглядит метод после переименования для удобства чтения.

int rollIndex = 0;

int score = 0;

for (int frameIndex = 0; frameIndex < 10; frameIndex++)

{

. . .

}

Это немного лучше. Теперь мы видим, что алгоритм работает, хотя массив _rolls по одному кадру за раз, а переменная rollIndex отслеживает броски в каждом кадре. Мы также можем сразу увидеть, что результат, который мы вычисляем, на самом деле является результатом, хотя это, как правило, подразумевается именем метода GetScore (), это все еще добавляет немного ясности.

Пойдя дальше, я приведу некоторые условные коды к методу (называемому «Рефакторинг метода Exrtact Method»). Например, я изменю if (_rolls [rollIndex] == 10) на if (ThisFrameIsAStrike (rollIndex). Теперь код начинает читать как простой разговорный язык. Эти изменения чуть-чуть замедляют запись, но ускоряют читатель очень много — и мы знаем, что большая часть обслуживания читает, а не пишет.

Но как насчет фактической оценки? Требуется немного концентрации, чтобы пробраться сквозь эти элементы массива и понять, что происходит. Давайте выполним еще немного рефакторинга Extract Method, чтобы немного их очистить. Полученный класс содержит больше кода из-за частных методов разъяснения, но логику основного алгоритма GetScore () теперь легко читать. Проверьте  решение после, чтобы увидеть окончательную реализацию класса Game.cs.

for (int frameIndex = 0; frameIndex < 10; frameIndex++)

{

if (FrameIsStrike(rollIndex))

{

. . .

}

else if (FrameIsSpare(rollIndex))

{

. . .

}

else

{

. . .

}

}

Через простое переименование и извлечения в метод мы значительно очистили этот класс, чтобы сделать его более читабельным. Обратите внимание, что код читается почти как предложения.

Давайте поговорим еще об одном примере, прежде чем называть это днем.

В поисках простых чисел

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

Метод GetPrimes () принимает целое число и возвращает его простые множители. Переменные уже имеют правильные имена, и если вы потратите некоторое время на чтение кода, вы, вероятно, поймете его намерения.

public int[] Factor(int numToFactor)

{

var factors = new List<int>();

var possiblePrime = 2;



while (numToFactor > 1)

{

while (numToFactor%possiblePrime == 0)

{

factors.Add(possiblePrime);

numToFactor /= possiblePrime;

}

possiblePrime++;

}

return factors.ToArray();

}

Хотя это полное и работающее решение для ката Prime Factors, многие практикующие ката используют этот код, чтобы сделать его еще более коротким и кратким. Фактически, основная логика может быть выражена в 2 коротких строках кода.

public int[] Factor(int numToFactor)

{

var factors = new List<int>();



for (int candidate = 2; numToFactor > 1; candidate++)

for (; numToFactor % candidate == 0; numToFactor /= candidate)

factors.Add(candidate);





return factors.ToArray();

}

Хотя в этой реализации код действительно короче, его труднее читать. Эта реализация берет стандартные структуры цикла for и использует их способами, которые мы не привыкли видеть. Внешний цикл сравнивает numToFactor в слоте, который мы обычно ожидаем увидеть сравниваемым индексатором. Внутренний цикл выглядит немного странно, поскольку он даже не объявляет индексатор. Это вполне допустимо в C #, но это не та конструкция, которую часто можно увидеть.

Найдите минутку, чтобы попытаться найти решение в 2 строки. Чтение заняло больше или меньше времени, чем первое решение, которое использует больше строк кода? Как читатель, я ценю более подробное решение с циклами while.

Ответственность программиста

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

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

Понравилась статья? Поделить с друзьями:
  • Как написать латинскими цифрами на клавиатуре
  • Как написать кубометр на клавиатуре
  • Как написать кубический метр на клавиатуре
  • Как написать кубический корень словами
  • Как написать кубический корень на клавиатуре