У меня есть некоторые обсуждения с моими новыми коллегами относительно комментариев. Нам обоим нравится чистый код , и я прекрасно согласен с тем, что следует избегать комментариев встроенного кода и что имена классов и методов должны использоваться для выражения того, что они делают.
Тем не менее, я большой поклонник добавления кратких резюме классов, которые пытаются объяснить назначение класса и то, что на самом деле представляет, в первую очередь, чтобы было легко поддерживать шаблон принципа единой ответственности . Я также привык добавлять однострочные сводки к методам, которые объясняют, что метод должен делать. Типичным примером является простой метод
public Product GetById(int productId) {...}
Я добавляю следующую сводку метода
/// <summary>
/// Retrieves a product by its id, returns null if no product was found.
/// </summary
Я считаю, что тот факт, что метод возвращает ноль, должен быть задокументирован. Разработчик, который хочет вызвать метод, не должен открывать мой код, чтобы увидеть, возвращает ли метод значение null или выдает исключение. Иногда это часть интерфейса, поэтому разработчик даже не знает, какой основной код выполняется?
Однако мои коллеги считают, что такого рода комментарии являются « запахом кода » и что «комментарии всегда являются ошибками» ( Роберт К. Мартин ).
Есть ли способ выразить и передать эти типы знаний без добавления комментариев? Поскольку я большой поклонник Роберта С. Мартина, я немного запутался. Являются ли резюме такими же, как комментарии, и, следовательно, всегда неудачи?
Это не вопрос о встроенных комментариях.
источник
Ответы:
Как уже говорили другие, есть разница между комментариями, документирующими API, и встроенными комментариями. С моей точки зрения, основное отличие состоит в том, что встроенный комментарий читается вместе с кодом , тогда как комментарий к документации читается вместе с подписью того, что вы комментируете.
Учитывая это, мы можем применить тот же принцип СУХОЙ . Означает ли комментарий то же самое, что и подпись? Давайте посмотрим на ваш пример:
Эта часть просто повторяет то, что мы уже видим из имени
GetById
и типа возвратаProduct
. Также возникает вопрос, в чем разница между «получением» и «извлечением», и какое отношение имеет код к комментарию в отношении этого различия. Так что это ненужно и немного сбивает с толку. Во всяком случае, это мешает действительно полезной второй части комментария:Ах! Это то, что мы определенно не можем знать наверняка только из подписи, и предоставляет полезную информацию.
Теперь сделайте этот шаг дальше. Когда люди говорят о комментариях как о запахах кода, вопрос не в том, нуждается ли код в комментариях, а в том, указывает ли комментарий, что код можно было бы написать лучше, чтобы выразить информацию в комментарии. Вот что означает «запах кода» - это не значит «не делай этого!», Это означает «если ты делаешь это, это может быть признаком того, что есть проблема».
Поэтому, если ваши коллеги говорят вам, что этот комментарий о нуле - это запах кода, вы должны просто спросить их: «Хорошо, как мне тогда выразить это?» Если у них есть выполнимый ответ, вы чему-то научились. Если нет, это, вероятно, убьет их жалобы мертвыми.
Что касается этого конкретного случая, то, как правило, нулевая проблема, как известно, является сложной. Есть основания кода причины, изобилующие защитными предложениями, почему проверки нуля являются популярным предварительным условием для контрактов кода, почему существование нуля было названо «ошибкой в миллиард долларов». Не так много жизнеспособных вариантов. Тем не менее, одним из самых популярных в C # является
Try...
соглашение:В других языках может быть идиоматично использовать тип (часто называемый чем-то вроде
Optional
илиMaybe
), чтобы указать результат, который может или не может быть:Таким образом, эта позиция антикомментариев куда-то нас привела: мы, по крайней мере, подумали о том, представляет ли этот комментарий запах и какие альтернативы могут существовать для нас.
Должны ли мы на самом деле отдавать предпочтение этим по сравнению с оригинальной подписью - это совсем другая дискуссия, но у нас, по крайней мере, есть варианты выражения через код, а не комментарии, что происходит, когда продукт не найден. Вам следует обсудить со своими коллегами, какой из этих вариантов они считают лучшим и почему, и, надеюсь, помочь выйти за рамки общих догматических заявлений о комментариях.
источник
Linq
-эквивалентно изTry...
,...OrDefault
, возвращающихdefault(T)
если предложение приведет к пустому результату.TryGetValue
Картина является разумным способом сделать это в C #, но большинство функциональных языков есть лучший способ представления недостающего значения. Подробнее здесьT TryGetValue(params, ref bool success)
для любого типа T, либоT TryGetValue(params)
с нулевым значением, указывающим на сбой, для типа T с ограниченным классом, ноTryGetXX
возвращаемый шаблонbool
несовместим с ковариацией.Optional<Product>
чтобы указать, что не может быть Продукт, возвращенный из метода.Цитата Роберта К. Мартина вырвана из контекста. Вот цитата с немного большим контекстом:
(Скопировано здесь , но оригинальная цитата взята из Чистого кода: Справочник по мастерству гибкого программного обеспечения )
То, как эта цитата превращается в «Комментарии - всегда неудачи», является хорошим примером того, как некоторые люди вычтут разумную цитату из контекста и превратят ее в глупую догму.
Документация API (например, javadoc) должна документировать API, чтобы пользователь мог использовать его, не читая исходный код . Таким образом, в этом случае документация должна объяснить, что делает метод. Теперь вы можете утверждать, что «Извлечение продукта по его идентификатору» является избыточным, потому что оно уже указано именем метода, но информация, которая
null
может быть возвращена, определенно важна для документа, поскольку это ни в коей мере не очевидно.Если вы хотите избежать необходимости комментария, вы должны устранить основную проблему (которая является использованием
null
в качестве допустимого возвращаемого значения), сделав API более явным. Например, вы можете вернуть какой-тоOption<Product>
тип, так что сигнатура типа сама четко сообщает, что будет возвращено в случае, если продукт не найден.Но в любом случае нереально полностью документировать API только через имена методов и сигнатуры типов. Используйте doc-комментарии для любой дополнительной неочевидной информации, которую должен знать пользователь. Взять хотя бы документацию по API из
DateTime.AddMonths()
BCL:Невозможно выразить это, используя только имя метода и подпись! Конечно, документация вашего класса может не требовать такого уровня детализации, это всего лишь пример.
Встроенные комментарии тоже неплохие.
Плохие комментарии - это плохо. Например, комментарии, которые только объясняют, что можно тривиально увидеть из кода, классический пример:
Комментарии, которые объясняют что-то, что можно прояснить, переименовывая переменную или метод или иным образом реструктурируя код, являются запахом кода:
Вот такие комментарии Мартин против. Комментарий является признаком невозможности написания понятного кода - в этом случае использовать понятные имена для переменных и методов. Сам комментарий, конечно, не проблема, проблема в том, что нам нужен комментарий, чтобы понять код.
Но комментарии должны использоваться для объяснения всего, что не очевидно из кода, например, почему код написан определенным неочевидным способом:
Комментарии, которые объясняют, что делает излишне запутанный фрагмент кода, также являются запахом, но исправление не должно запрещать комментарии, исправление - это исправление кода! На самом деле, запутанный код действительно существует (надеюсь, только временно, пока не произойдет рефакторинг), но ни один обычный разработчик не пишет идеально чистый код с первого раза. Когда происходит запутанный код, гораздо лучше написать комментарий, объясняющий, что он делает, чем не писать комментарий. Этот комментарий также облегчит рефакторинг позже.
Иногда код неизбежно сложен. Это может быть сложный алгоритм или код, жертвующий ясностью по соображениям производительности. Снова комментарии необходимы.
источник
x++
может быть хорошим, если это что-то вроде «увеличение х на единицу, обтекание, если это UINT32_MAX»; Любой, кто знает спецификацию языка, знал бы, что инкрементuint32_t
заворачивания будет увеличиваться , но без комментария можно не знать, была ли такая упаковка ожидаемой частью реализуемого алгоритма.Существует разница между комментированием вашего кода и документированием вашего кода .
Комментарии необходимы для поддержки кода позже, то есть для изменения самого кода.
Комментарии действительно могут восприниматься как проблемные. Крайняя точка зрения - сказать, что они всегда указывают на проблему, либо внутри вашего кода (код слишком сложен для понимания), либо внутри языка (язык не может быть достаточно выразительным; например, тот факт, что метод никогда не возвращает,
null
может быть выражен через контракты кода в C #, но нет способа выразить это через код, скажем, в PHP).Документация необходима для использования разработанных вами объектов (классов, интерфейсов). Целевая аудитория другая: мы говорим здесь не о тех, кто будет поддерживать ваш код и изменять его, а о тех, кому его нужно использовать .
Удаление документации, потому что код достаточно ясен, безумие, поскольку документация предназначена специально для того, чтобы можно было использовать классы и интерфейсы без необходимости читать тысячи строк кода .
источник
Что ж, кажется, ваш коллега читает книги, понимает, что они говорят, и применяет то, что он изучил, не задумываясь и не принимая во внимание контекст.
Ваш комментарий о том, что делает функция, должен быть таким, чтобы вы могли выбросить код реализации, я прочитал комментарий функции и смогу написать замену для реализации, без каких-либо ошибок.
Если комментарий не говорит мне, было ли выброшено исключение или возвращается nil, я не могу этого сделать. Кроме того, если комментарий не сообщает вам, было ли выброшено исключение или возвращается nil, то, где бы вы ни вызывали функцию, вы должны убедиться, что ваш код работает правильно, независимо от того, сгенерировано исключение или возвращается nil.
Так что твой коллега совершенно не прав. И продолжайте читать все книги, но подумайте сами.
PS. Я видел вашу фразу: «иногда это часть интерфейса, поэтому вы даже не знаете, какой код выполняется». Даже с помощью виртуальных функций вы не знаете, какой код выполняется. Хуже того, если вы написали абстрактный класс, даже кода не будет! Поэтому, если у вас есть абстрактный класс с абстрактной функцией, комментарии, которые вы добавляете к абстрактной функции, являются единственной вещью, которой должен руководствоваться разработчик конкретного класса. Эти комментарии также могут быть единственной вещью, которой могут руководствоваться пользователи класса, например, если у вас есть только абстрактный класс и фабрика, возвращающая конкретную реализацию, но вы никогда не увидите никакого исходного кода реализации. (И, конечно, я не должен смотреть на исходный код одной реализации).
источник
Есть два типа комментариев: те, которые видны людям с кодом, и те, которые используются для создания документации.
Тип комментария, на который ссылается дядя Боб, - это вид, который виден только людям с кодом. То, что он защищает, является формой СУХОГО . Для человека, который просматривает исходный код, исходный код должен быть необходимой документацией. Даже в случае, когда люди имеют доступ к исходному коду, комментарии не всегда плохие. Иногда алгоритмы сложны, или вам нужно понять, почему вы используете неочевидный подход, чтобы другие не заканчивали тем, что ломали ваш код, если пытались исправить ошибку или добавить новую функцию.
Комментарии, которые вы описываете, являются документацией API. Это вещи, которые видны людям, использующим вашу реализацию, но которые могут не иметь доступа к вашему исходному коду. Даже если они имеют доступ к вашему исходному коду, они могут работать с другими модулями и не смотреть на ваш исходный код. Этим людям было бы полезно иметь эту документацию доступной в их IDE, когда они пишут свой код.
источник
Ценность комментария измеряется значением информации, которую он дает, за вычетом усилий, необходимых для его прочтения и / или игнорирования. Так что, если мы проанализируем комментарий
По стоимости и стоимости мы видим три вещи:
Retrieves a product by its id
повторяет то, что говорит название функции, так что это стоимость без значения. Это должно быть удалено.returns null if no product was found
это очень ценная информация. Это, вероятно, уменьшает время, которое другие кодировщики должны будут смотреть на реализацию функции. Я совершенно уверен, что это экономит больше чтения, чем стоимость чтения, которую он представляет сам. Это должно остаться.Линии
не несите никакой информации вообще. Они являются чистой ценой для читателя комментария. Они могут быть оправданы, если они нужны вашему генератору документации, но в этом случае вам, вероятно, следует подумать о другом генераторе документации.
Это причина, почему использование документации генераторов спорная идея: Как правило, они требуют много дополнительных комментариев, которые не несут никакой информации или повторять очевидные вещи, только ради полированного документа.
Наблюдение, которое я не нашел ни в одном из других ответов:
Даже комментарии, которые не нужны для понимания / использования кода, могут быть очень ценными. Вот один из таких примеров:
Скорее всего, много текста, совершенно ненужного для понимания / использования кода. Однако это объясняет причину, по которой код выглядит так, как он выглядит. Это остановит людей, которые смотрят на код, удивятся, почему это не сделано очевидным образом, и начнет рефакторинг кода, пока они не поймут, почему код был написан именно так. И даже если читатель достаточно умен, чтобы не переходить к рефакторингу напрямую, ему все равно нужно выяснить, почему код выглядит так, как он это делает, прежде чем он поймет, что лучше всего оставаться таким, какой он есть. Этот комментарий может буквально сэкономить часы работы. Таким образом, стоимость выше, чем стоимость.
Кроме того, комментарии могут сообщать о намерениях кода, а не только о том, как он работает. И они могут нарисовать большую картину, которая обычно теряется в мельчайших деталях самого кода. Таким образом, вы правы, выступая в пользу комментариев класса. Я больше всего ценю комментарии класса, если они объясняют намерение класса, как он взаимодействует с другими классами, как его следует использовать и т. Д. Увы, я не большой автор таких комментариев ...
источник
GetById
ставит вопрос, что такое id, а также, что и откуда. Комментарии к документации должны позволять среде разработки отображать ответы на эти вопросы. Если это не будет объяснено где-либо еще в комментариях к документу модуля, это также будет одно место, чтобы сказать, почему в любом случае можно получить идентификатор.Некомментированный код - это плохой код. Это широко распространенный (если не универсальный) миф о том, что код может читаться почти так же, как, скажем, английский. Это нужно интерпретировать и для любого, кроме самого тривиального кода, который требует времени и усилий. Плюс ко всему, у каждого разный уровень возможностей как в чтении, так и в написании любого языка. Различия между авторским и читательским стилями и возможностями кодирования являются сильными барьерами для точной интерпретации. Это также миф, что вы можете извлечь намерение автора из реализации кода. По моему опыту, это редко неправильно добавлять дополнительные комментарии.
Роберт Мартин и соавт. Считайте это "неприятным запахом", потому что может быть, что код изменен, а комментарии - нет. Я говорю, что это хорошо (точно так же, как «плохой запах» добавляется к бытовому газу, чтобы предупредить пользователя об утечках). Чтение комментариев даст вам полезную отправную точку для интерпретации реального кода. Если они совпадают, у вас будет повышенная уверенность в коде. Если они различаются, значит, вы обнаружили предупреждающий запах, и вам необходимо провести дальнейшее расследование. Лекарство от "неприятного запаха" - это не устранение запаха, а устранение утечки.
источник
i++; // Adjust for leap years.
В некоторых языках (например, F #) весь этот комментарий / документация фактически может быть выражена в сигнатуре метода. Это связано с тем, что в F # значение null, как правило, не является допустимым значением, если оно не разрешено специально.
В F # (а также в некоторых других функциональных языках) распространено то, что вместо нуля вы используете тип опции,
Option<T>
который может бытьNone
либоSome(T)
. Затем язык поймет это и заставит вас (или предупредит вас, если вы этого не сделаете) в обоих случаях, когда вы попытаетесь его использовать.Так, например, в F # вы можете иметь подпись, которая выглядит следующим образом
И тогда это будет функция, которая принимает один параметр (int) и затем либо возвращает продукт, либо значение None.
И тогда вы могли бы использовать это так
И вы получите предупреждения компилятора, если вы не соответствуете обоим возможным случаям. Таким образом, нет никакого способа получить исключения с нулевой ссылкой (если вы, конечно, не игнорируете предупреждения компилятора), и вы знаете все, что вам нужно, просто взглянув на сигнатуру функции.
Конечно, это требует, чтобы ваш язык был спроектирован таким образом, чего нет у многих распространенных языков, таких как C #, Java, C ++ и т. Д. Так что это может быть не полезно для вас в вашей текущей ситуации. Но (надеюсь) приятно знать, что существуют языки, которые позволяют вам выражать такую информацию статически, без комментариев и т. Д. :)
источник
Здесь есть несколько отличных ответов, и я не хочу повторять то, что они говорят. Но позвольте мне добавить несколько комментариев. (Не каламбур предназначен.)
Есть много утверждений, которые умные люди делают - о разработке программного обеспечения и многих других предметах, я полагаю - которые являются очень хорошим советом, если их понимать в контексте, но которые глупые люди, которые вырывают из контекста или применяют в неподходящих ситуациях или принимают нелепые крайности.
Идея о том, что код должен самодокументироваться, является одной из таких прекрасных идей. Но в реальной жизни существуют ограничения на практичность этой идеи.
Одна из них заключается в том, что язык может не предоставлять возможности для документирования того, что должно быть задокументировано четко и кратко. Поскольку компьютерные языки улучшаются, это становится все меньше и меньше проблемой. Но я не думаю, что это полностью прошло. В те дни, когда я писал на ассемблере, имело смысл добавить такой комментарий, как «общая цена = цена необлагаемых налогом предметов плюс цена облагаемых налогом предметов плюс цена облагаемых налогом предметов * ставка налога». То, что было в реестре в любой момент, не обязательно было очевидно. Потребовалось много шагов, чтобы сделать простую операцию. И т.д. Но если вы пишете на современном языке, такой комментарий будет просто повторением одной строки кода.
Я всегда раздражаюсь, когда вижу комментарии типа «x = x + 7; // добавляем 7 к x». Как, вау, спасибо, если бы я забыл, что означает знак плюс, который мог бы быть очень полезным. В действительности я могу запутаться, зная, что такое «х» или почему нужно было добавить 7 к нему в это конкретное время. Этот код можно сделать самодокументированным, если дать более значимое имя для «x» и использовать символическую константу вместо 7. Как если бы вы написали «total_price = total_price + MEMBERSHIP_FEE;», тогда комментарий, вероятно, вообще не нужен ,
Звучит замечательно сказать, что имя функции должно точно сказать, что делает функция, чтобы любые дополнительные комментарии были излишними. У меня остались приятные воспоминания о том времени, когда я написал функцию, которая проверяла, был ли номер элемента в таблице элементов нашей базы данных, возвращая true или false, и который я назвал «ValidateItemNumber». Выглядело как приличное имя. Затем кто-то другой пришел и изменил эту функцию, чтобы также создать заказ для элемента и обновить базу данных, но никогда не менял имя. Теперь имя было очень обманчивым. Это звучало так, как будто это сделало одну маленькую вещь, хотя на самом деле это сделало намного больше. Позже кто-то даже взял часть о проверке номера изделия и сделал это где-то еще, но все еще не изменил название. Таким образом, хотя функция теперь не имеет ничего общего с проверкой номеров позиций,
Но на практике для имени функции часто невозможно полностью описать, что делает функция, без имени функции, если код, составляющий эту функцию. Собирается ли название сообщить нам, какие проверки выполняются по всем параметрам? Что происходит при исключительных условиях? Разобрать все возможные двусмысленности? В какой-то момент имя станет настолько длинным, что станет просто запутанным. Я бы принял String BuildFullName (имя строки, имя строки) в качестве приличной сигнатуры функции. Несмотря на то, что в нем не указано, указано ли имя «первый последний» или «последний, первый» или какой-либо другой вариант, что он делает, если одна или обе части имени не заполнены, если оно накладывает ограничения на общую длину и что он делает, если это превышено,
источник