Басни на ночь — Хорошие имена как залог успеха

image
Доброго времени суток всем.

«Как лодку назовешь, так она и поплывет» — довольная известная фраза, которая вполне подходит к функциям, переменным, классам.

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

Например
enum Dates{
  GET_FIRST,
  GET_SECOND,
  GET_BOTH,
  None
}



Вот и думай, что этот енам делает эдакого в вашем коде. Вроде из названия перечисления понятно, что дело связано с датами. Может методу передаются две даты, и нужно выбрать какую то из них. А может в расчет нужно брать только определенные даты, первую и вторую из списка. А может что еще, может быть много мнений.
В этом случае ищем место, где этот код используется
Boolean IsInRange(DateTime date1, DateTime date2, Dates pDates) {
  if (pDates==Dates.GET_BOTH) {
    return date1 <= myDate && myDate <= date2;
  }
  if(pDates==Dates.GET_FIRST) {
    return date1 <= myDate && myDate < date2;
  }
  if(pDates==Dates.GET_SECOND) {
    return date1 < myDate && myDate <= date2;
  }
  return date1 < myDate && myDate < date2;
}



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

enum DateIntervals{
  Open,
  Closed,
  OpenLeft,
  OpenRight
}


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

Комментарии.
Комментарии — это признак того, что имя не отражает суть. Скорее всего, следует поразмыслить над именем функции, нежели над хорошим комментарием.
Помнится был у меня бзик, писать комментарии ко всему и вся — было лениво, но я старался. Даже там где было понятно. Но по большей части это не приносило пользы, а вот эээ, гемороя — о дааа. Так что, пишите хорошие названия — это реально экономит время. Совершенству нет предела. Бывает по 5 раз переименовываешь, пока не скажешь — «вот, это оно»

Произносимые имена.
  • GetPCF
  • GetYYYY
  • GetNN

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

Префиксы
Во времена отсутствия мощных сред разработок (все относительно конечно), были приняты префиксы.

имена переменных в начале должны содержать префикс, который соответствует определенному типу данных и записывается маленькими буквами. Само имя начинается с большой буквы.
Префикс Тип данных
  • a массив (array)
  • b BOOL булево целое (один байт)
  • by BYTE беззнаковое целое
  • c символ (один байт)
  • C класс
  • d число с двойной точностью (double)
  • ....
Даже сейчас встречается подобное. Не стоит этим баловаться. Лучше назовите логично переменную, нежели таскать префиксы.

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

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

Резюмируя:
  • Используете имена, которые выражают ваше намерение. Тогда это сэкономит много времени.
  • Следите за комментариями, как правило это признак необходимости небольшого рефакторинга.
  • Имя должно быть легко произносимым.
  • Не вставляйте суффиксы и префиксы понятные только вам.
  • Для функций используйте имена образованные из глагола + существительное/прилагательное
  • Избегайте дезинформации — функция должна делать то, что исходит из ее названия, иначе это превращается в постоянное воспоминание того, что же делает функция.
  • Базовые классы должны иметь короткие имена. Каждый наследник как правило добавляет по слову. А наследники наследников еще. Вот и может получится длинный паравоз


Happy codding.

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 34

    +1
    5 за пересказ Мартина по памяти, 2 за копирование. Ну действительно, чуть ли не дословно переписано!
      0
      А у меня очень хорошая память, Так что двойку прошу не ставить ;)
      Новичкам будет полезно.
      А то что чуть ли не дословно — мхм, можно было на древнерусском языке, но смысл. Ниже приведены слова из книги Питера Гудлифа. Книги хоть и разные, контент очень похож.
        0
        P.S. при написании не использовал книгу как источник. Написал основные принципы хорошего стиля.
      +2
      Настоятельно рекомендую прочитать и автору статьи и тем, кто добавляет её в избранное:
      Питер Гудлиф — Ремесло программиста. Практика написания хорошего кода
        0
        «Золотые правила» из книги, описывающие данную статью:

        Учитесь давать объектам прозрачные имена — они должны ясно описывать то, что за ними скрывается.

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

        Ясность имени предпочтительнее его краткости.

        Предпочтительней такое правило именования, которое различает имена переменных и имена типов.

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

        Качества хорошего имени:
        1. Описательность
        2. Техническая корректность
        3. Идиоматичность
        4. Тактичность (длина, стиль)

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

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

        (с) Питер Гудлиф
        0
        … а еще у МакКоннела про это есть существенно больше одной главы. Ну то есть как минимум The Power of Variable Names + Good Routine Names.
          0
          Спасибо, возьму на заметку
          0
          Функция IsInRange() в посте — вообще непонятный шлак, да ещё и с ошибкой (одинаковый код в первых двух if). Что-то мне подсказывает, что так будет понятнее:

          enum IntervalFlags
          {
            Interval_NotStrict = 0,
            Interval_StrictLeft = 1 << 0,
            Interval_StrictRight = 1 << 1,
            Interval_Strict = Interval_StrictLeft | Interval_StrictRight
          };
          
          bool Date::IsInRange(const Date &left, const Date &right, IntervalFlags interval_flags)
          {
            return (range_flags & Interval_StrictLeft ? *this > left : *this >= left) &&
                   (range_flags & Interval_StrictRight ? *this < right : *this <= right);
          }
          

          C# не знаю, поэтому написал на C++. Поведение функции поменялось
            0
            Прошу прощения, отправил слишком рано.
            Поведение функции поменялось немного, на первый взгляд, но никто не мешает прототип объявить так:

            bool IsInRange(const Date &left, const Date &right, IntervalFlags interval_flags = Interval_Strict);
            

            Тогда по умолчанию будет использоваться строгое неравенство, как и в оригинале. Кстати, возможно, название IntervalFlags не самое удачное, и должно быть что-то, связанное с relations.
              0
              считаю приведенную функцию — мало понятной. С таким же успехом можно наваять множество функций из одной строки но в сотни символов в длину, и что? это не самоцель. Код должен быть понятным, а не показывать какой автор молодец, что все в одну строку запихал. Считаю это кандидатом на рефакторинг.
                0
                P.S. спасибо за указание ошибки. поправил статью
                  0
                  А можете полному нубу в плюсах объяснить смысл строчек:
                    Interval_StrictLeft = 1 << 0,
                    Interval_StrictRight = 1 << 1,
                  

                  Зачем использовать сдвиг в объявлении енума?
                    0
                    Мне кажется, что битовые флаги так выглядят понятнее. Обычно их записывают как-то так:

                    Interval_StrictLeft = 0x01,
                    Interval_StrictRight = 0x02,
                    

                    Но как по мне, проще понять сдвиг единицы в позицию соответствующего бита, чем переводить из шестнадцатеричной формы в двоичную в уме.
                  0
                  Спасибо 1с за
                  ТипПроекцииГеографическойСхемы.ПсевдоцилиндрическаяПлоскоПолярнаяПроекцияЧетвертогоПорядкаМакБрайдаТомаса
                  Ну и всякие ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначенийИмениБорисаГеоргиевичаНуралиева

                  Это я к чему? Везде важен баланс…
                    0
                    А почему вы не говорите о том, что все должно быть задокументировано?
                    enum Dates{
                      /// <summary>
                      /// Дата первая
                      /// </summary>
                      GET_FIRST,
                      /// <summary>
                      /// Дата вторая
                      /// </summary>
                      GET_SECOND,
                      /// <summary>
                      /// Дата еще какая-то
                      /// </summary>
                      GET_BOTH,
                      /// <summary>
                      /// Дата никакая
                      /// </summary>
                      None
                    }
                    

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

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

                      P.S. между кодом с комментами и чистым кодом без комментов я выберу второе. Код может быть обновлен, а комменты будут за ленью/или по др. причине забыты. В итоге комменты оторваны от кода. А это зло.
                        0
                        А ещё лучше чистый код с каментами:)
                        Всё-таки каменты нужно — не всё можно объяснить именами переменных/функций.
                          0
                          понятно, что не все. Это не должно становится самоцелью. Но плодить коммента как документацию — увольте.
                          К вам вопрос — много комментво пишите? не надоедает? Я лично устаю быстро их писать.

                          /// ///Находится в состоянии загрузки
                          ///
                          private bool isLoading;

                          /// /// Применить изменения
                          ///

                          В таком
                          private void ApplyChanges(){

                          }

                          Не является ли это самоцелью?
                            0
                            >К вам вопрос — много комментво пишите?
                            достаточно много, у нас это регламинтируется стандартом — каждый член класса обязательно должен иметь комментарии…

                            >Не является ли это самоцелью?
                            тут конечно согласен, в таких ситуациях каменты излишни.
                            сам уже устал генерировать коменты в стиле Капитана О.
                              0
                              Если у вас это регламент такой — то понятно. Но смысла особого не вижу в этом. Сочувствую вам если честно. Меня бы надолго не хватило.

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

                          0
                          Комментарии позволяют указать требования для вызова функции без необходимости понимать, как она работает.
                            0
                            Хорошее имя функции сделает тоже самое. Понимаю что не всегда это достижимо, но как правило более чем возможно.
                              0
                              и еще вот к примеру. Есть класс с функциями. У функций есть комменты. Решили сделать рефакторинг дабы привести в порядок содержимое самих функций. В итоге некоторые функции разбились на несколько новых, пара функций была смержена, и т.д.
                              В случае цели писать комменты, придется для всех модифицированных функций проверять соответствие комментария с назначением функций. А если вы упустили одну из функций из вида, тогда комментарий будет вводить в заблуждение. После пары таких случаев программист начнет сомневаться в комментах. Дабл чекинг выходит.
                                0
                                Писать ли комменты доки к приватным функциям ваших классов — это ваше дело. А вот описание публичных методов — будьте любезны предоставить, читать сотни строк кода ради того, чтобы понять, какие аргументы у функции допустимы и какие ошибки она генерит — недопустимо.
                                  0
                                  Тогда вопрос.

                                  Допустим есть задача начисления зароботной платы.
                                  есть функция
                                  Check GetCheck(int empId){
                                      ....
                                      var taxes=GetTaxes(empId, paymentType); // эта функция генерит свои ошибки
                                      var fines=GetFines(empId); // эта тоже
                                  
                                      return Calculate(taxes, fines, period) //и эта
                                  }
                                  


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

                                  если меняется логика функции M2 вы легко можете забыть про какой нибудь коммент для функции M1 (где M1 вызывает M2).

                                  Для Api безусловно нужны комментарии, а так, я не считаю, что они являются обязательным фактором.

                                  P.S. всё вышесказанное является ИМХО и не хочу сказать что сказанное является абс. истинной. Кто то без комментов жить не может, а кто то легко обходится.
                                    0
                                    Метод API — это черный ящик, что у него творится внутри — не важно. Поэтому и описывать внутреннее строение в комментах не нужно. А вот возможные возвращаемые значения и требования к аргументам — необходимо, на мой взгляд. Во-первых, это помогает понять, что делает функция, не заглядывая в код. Во-вторых, при разработке, комментирование помогает понять, что вообще требуется от такой функции. :)

                                    В приведенном примере важно только, что такое Check, что возвращает функция GetCheck и что такое empId.
                                      0
                                      >В приведенном примере важно только, что такое Check, что возвращает функция GetCheck и что такое empId.

                                      В приведенном примере понятно что такое чек из названия, что возвращает GetCheck тоже, и суть параметра так же. Комменты излишни.
                                        0
                                        А что возвратит функция в случае отрицательного/невалидного айдишника?
                                          0
                                          ArgumentException
                                            0
                                            Ну тогда это вот и надо задокументировать, по-хорошему. Потому что это совсем не очевидно. Я бы, например ожидал OutOfRangeException
                                              0
                                              у каждой команды своё видение того, как их система работает. Не хочу сказать, что у тебя неправильная логика, но как по мне, я бы скорее обошелся без документации, нежели с ней.

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

                                          Ну или я дурак. В любом случае, мне не очень ясно, что такое Check (я так понимаю, поля этого класса (структуры?) тоже не задокументированы?). Реальный «чек» — это бумажка с кучей информации и подписью. Наверное, тут что-то другое. Какая информация содержится в этом классе/структуре, сразу не ясно. Так же, как не ясно, почему ф-ция calculate возвращает Check, а не число, но этого я «как бы» ещё не видел.

                                          Исходя из того, что название функции — GetCheck( emp id ), я могу только предполагать, что она возвращает.

                                          Во-первых, не ясно, что произойдет, если id не совпал ни с одним из базы работников. Функция вывалится с исключением? Каким? Вернет какой-нибудь nullobject?

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

                                          Что функция вернет, если я её вызову с валидным id посреди месяца, если за прошлые месяцы зарплату сотруднику выдали и зарплату зачисляют раз в месяц? Чек с нулем в графе «бабло»? Рассчитанную по часам зарплату, начиная начала расчетного периода, заканчивая текущей датой? У меня ещё куча вариантов в голове. Нет, все, надо лезть в код метода, и в код функций, которых он вызывает.

                                          Я бы сказал, что функция названа неудачно, а отсутствие комментариев усугубило ситуацию.

                          Only users with full accounts can post comments. Log in, please.