21

Разве не интересно найти неуместный и бесполезный комментарий в коде?

Как можно написать очень мало комментариев и при этом сохранить код легко понятным?

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

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

  • Именование: используйте имена, чтобы объяснить назначение переменных, функций и т. д.
  • Инкапсуляция функций: инкапсулируйте код с определённой функциональностью в функцию, чтобы прояснить её назначение.
  • Введение переменных: поместите выражения в выделенные переменные.

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

Кроме того, помимо трех вышеперечисленных, есть некоторые более широко применяемые подходы:

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

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

1. Именование

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

1. Переименование функций

Именование функций не очень сложно; можно следовать этим правилам:

  • Избегайте расплывчатых слов, таких как 'handle' или 'manage' — handleLinks, manageObjects.
  • Используйте активные глаголы — cutGrass, sendFile — чтобы показать, что функция активно выполняет действие.
  • Указывайте тип возвращаемого значения — getMagicBullet, READFILE. Строго типизированные языки также могут использовать идентификаторы типов, чтобы указать тип возвращаемого значения функции.

2. Переименование переменных

  • Указывайте единицы измерения — если есть числовые параметры, можно добавить их единицы измерения. Например, используйте widthPx вместо width, чтобы указать, что единица измерения ширины — пиксели.
  • Не используйте сокращения — a и b не должны использоваться в качестве имён параметров.

2. Инкапсуляция функций

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

1. Инкапсулируйте код в функции

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

var width = (value - 0.5) * 16;

Это может быть не очень понятно; конечно, с комментарием было бы совершенно ясно, но мы можем просто инкапсулировать это в функцию, чтобы добиться самодокументирования...

var width = emToPixels(value);
 
function emToPixels(ems) {
    return (ems - 0.5) * 16;
}

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

2. Замените условные выражения функциями

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

if(!el.offsetWidth || !el.offsetHeight) {
}

Вы знаете, для чего предназначен приведённый выше код?

function isVisible(el) {
    return el.offsetWidth && el.offsetHeight;
}
 
if(!isVisible(el)) {
}

3. Введение переменных

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

1. Замена выражений переменными

Посмотрите на этот пример

if(!el.offsetWidth || !el.offsetHeight) {
}

На этот раз вместо инкапсуляции в функцию замените его, введя переменные.

var isVisible = el.offsetWidth && el.offsetHeight;
if(!isVisible) {
}

2. Использование переменных вместо уравнений

Мы также можем использовать это, чтобы наглядно объяснять сложные формулы:

return a * b + (c / d);

Замените их переменными.

var divisor = c / d;
var multiplier = a * b;
return multiplier + divisor;

4. Интерфейсы классов и модулей

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

Посмотрите на следующий пример:

class Box {
    public function setState(state) {
        this.state = state;
    }
 
    public function getState() {
        return this.state;
    }
}

Этот класс мог бы также содержать другой код. Я намеренно выбрал этот пример, чтобы показать, как публичный интерфейс может быть самодокументируемым.

Можете ли вы сказать, как вызывается этот класс? Очевидно, это не очевидно.

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

Но что, если мы изменим это так...

class Box {
    public function open() {
        this.state = open;
    }
 
    public function close() {
        this.state = closed;
    }
 
    public function isOpen() {
        return this.state == open;
    }
}

Разве так не намного яснее? Примечание: мы изменили только публичный интерфейс; его внутреннее представление совпадает с исходным состоянием this.state.

5. Группировка кода

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

Посмотрите на следующий пример:

var foo = 1;
 
blah()
xyz();
 
bar(foo);
baz(1337);
quux(foo);

По сравнению со следующим:

var foo = 1;
bar(foo);
quux(foo);
 
blah()
xyz();
 
baz(1337);

Сгруппируйте все использования foo вместе, и связи будут видны с первого взгляда. Но иногда нам приходится вызывать между ними некоторые другие функции. Поэтому, если возможно, старайтесь использовать группировку кода; если нет — не нужно этого делать.

6. Другие рекомендации

  • Не используйте странные маркеры; следующие два эквивалентны
imTricky && doMagic();
if(imTricky) {
    doMagic();
}

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

  • Именуйте константы: если в коде есть особые значения, лучше всего дать им имена. var PURPOSE_OF_LIFE = 42;
  • Установите правила: лучше всего придерживаться единообразных правил именования. Так читатели смогут правильно угадывать смысл различных вещей, ссылаясь на другой код.

Источник: http://www.ido321.com/1360.html