Почему я не должен использовать "Венгерскую нотацию"?
Я знаю, на что ссылается венгерский - давая информацию о переменной, параметре или типе в качестве префикса к ее имени. Все, кажется, бешено против этого, хотя в некоторых случаях это кажется хорошей идеей. Если я чувствую, что передается полезная информация, почему бы мне не поместить ее туда, где она доступна?
Смотрите также: Используют ли люди венгерские соглашения об именах в реальном мире?
37 ответов
Большинство людей используют венгерские обозначения неправильно и получают неправильные результаты.
Прочитайте эту замечательную статью Джоэла Спольски: "Неправильный код выглядит неправильно".
Короче говоря, венгерская нотация, где вы начинаете префикс ваших имен переменных с их type
(строка) (Венгерские системы) плохо, потому что это бесполезно.
Венгерская нотация в том виде, как она была задумана ее автором, где вы префикс имени переменной kind
(используя пример Джоэла: безопасная строка или небезопасная строка), так называемый Apps Hungarian имеет свое применение и по-прежнему полезен.
v Используя прилагательное Hungarian, делает чтение ncode приличным.
Джоэл не прав, и вот почему.
Информация о "приложении", о которой он говорит, должна быть закодирована в системе типов. Вы не должны зависеть от переключения имен переменных, чтобы убедиться, что вы не передаете небезопасные данные в функции, требующие безопасных данных. Вы должны сделать это ошибкой типа, чтобы это было невозможно. Любые небезопасные данные должны иметь тип, помеченный как небезопасный, чтобы их просто нельзя было передать в безопасную функцию. Для преобразования из небезопасного в безопасное состояние требуется обработка с какой-либо функцией очистки.
Многие вещи, о которых Джоэл говорит как о "видах", не являются видами; на самом деле они являются типами.
Однако большинству языков не хватает системы типов, которая достаточно выразительна, чтобы обеспечить такие различия. Например, если бы C имел своего рода "сильный typedef" (где имя typedef имело все операции базового типа, но не было преобразовано в него), то многие из этих проблем исчезли бы. Например, если бы вы могли сказать, strong typedef std::string unsafe_string;
ввести новый тип unsafe_string
это не может быть преобразовано в std::string (и поэтому может участвовать в разрешении перегрузки и т. д. и т. д.), тогда нам не понадобятся глупые префиксы.
Таким образом, центральное утверждение, что венгерский язык относится к вещам, которые не являются типами, неверно. Он используется для информации о типе. Более подробная информация о типе, чем традиционная информация о типе C, безусловно; это информация о типе, которая кодирует какую-то семантическую деталь, чтобы указать назначение объектов. Но это все еще информация о типах, и правильное решение всегда заключалось в том, чтобы закодировать ее в систему типов. Кодирование его в систему типов - это, несомненно, лучший способ получить надлежащую проверку и применение правил. Имена переменных просто не срезают горчицу.
Другими словами, цель не должна заключаться в том, чтобы "сделать неправильный код неправильным для разработчика". Это должно быть "сделать неправильный код неправильным для компилятора".
Я думаю, что это массово загромождает исходный код.
Это также не приносит вам большого успеха в строго типизированном языке. Если вы делаете какую-либо форму несоответствия типов tomfoolery, компилятор сообщит вам об этом.
Венгерская нотация имеет смысл только в языках без пользовательских типов. В современном функциональном или OO-языке вы бы закодировали информацию о "виде" значения в тип данных или класс, а не в имя переменной.
Несколько ответов ссылаются на статью Джоэлса. Однако обратите внимание, что его пример написан на VBScript, который не поддерживает пользовательские классы (по крайней мере, долгое время). На языке с пользовательскими типами вы решите ту же проблему, создав тип HtmlEncodedString, а затем разрешите методу Write принимать только это. В статически типизированном языке компилятор будет отлавливать любые ошибки кодирования, в динамически типизированном вы получите исключение времени выполнения - но в любом случае вы защищены от написания некодированных строк. Венгерские нотации просто превращают программиста в человека, проверяющего тип, с такой работой, как правило, лучше выполняемой программным обеспечением.
Джоэл различает "системный венгерский" и "венгерский приложения", где "системный венгерский" кодирует встроенные типы, такие как int, float и т. Д., А "венгерский приложения" кодирует "виды", что является метаинформацией более высокого уровня. о переменной за типом машины. В ОО или современном функциональном языке вы можете создавать определяемые пользователем типы, поэтому нет различия между типом и "видом" в этом смысле - оба могут быть представлены системой типов - и "приложениями" Венгерский язык так же избыточен, как и "системный" венгерский.
Итак, чтобы ответить на ваш вопрос: системный венгерский язык будет полезен только на небезопасном, слабо типизированном языке, где, например, присвоение значения с плавающей запятой переменной int приведет к сбою системы. Венгерская нотация была специально придумана в шестидесятых для использования в BCPL, довольно низкоуровневом языке, который вообще не делал никакой проверки типов. Я не думаю, что какой-либо язык общего пользования сегодня имеет эту проблему, но обозначения жили как своего рода культовое программирование груза.
Венгерский приложения будет иметь смысл, если вы работаете с языком без пользовательских типов, таких как устаревший VBScript или ранние версии VB. Возможно также ранние версии Perl и PHP. Опять же, использование его в современном языке - культ чистого груза.
На любом другом языке венгерский просто уродлив, излишен и хрупок. Он повторяет информацию, уже известную из системы типов, и вы не должны повторяться. Используйте описательное имя для переменной, которая описывает намерение этого конкретного экземпляра типа. Используйте систему типов для кодирования инвариантов и метаинформации о "видах" или "классах" переменных - т.е. типы.
Общий смысл статьи Джоэлса - неправильный код выглядит неправильно - очень хороший принцип. Однако еще лучшая защита от ошибок - когда это вообще возможно - иметь неправильный код, который будет автоматически обнаруживаться компилятором.
Я всегда использую венгерскую нотацию для всех своих проектов. Я нахожу это действительно полезным, когда я имею дело с сотнями разных имен идентификаторов.
Например, когда я вызываю функцию, требующую строку, я могу набрать 's' и нажать control-space, и моя IDE покажет мне именно имена переменных с префиксом 's' .
Еще одно преимущество, когда я ставлю префикс u для неподписанных и i для подписанных целых, я сразу вижу, где я смешиваю подписанные и неподписанные потенциально опасными способами.
Я не могу вспомнить, сколько раз, когда в огромной 75000-строчной кодовой базе ошибки (и я, и другие тоже) вызывали ошибки из-за именования локальных переменных так же, как существующие переменные-члены этого класса. С тех пор я всегда префикс членов с "м_"
Это вопрос вкуса и опыта. Не стучите, пока не попробуете.
Вы забываете причину номер один для включения этой информации. Это не имеет никакого отношения к вам, программист. Это имеет отношение к человеку, идущему по дороге через 2 или 3 года после того, как ты уйдешь из компании, который должен прочесть это.
Да, IDE быстро определит типы для вас. Однако, когда вы читаете несколько длинных пакетов кода "бизнес-правил", хорошо, что вам не нужно останавливаться на каждой переменной, чтобы выяснить, к какому типу она относится. Когда я вижу такие вещи, как strUserID, intProduct или guiProductID, это значительно облегчает процесс ускорения.
Я согласен, что MS зашла слишком далеко с некоторыми из их соглашений об именах - я классифицирую это в куче "слишком много хорошего".
Соглашения об именах - это хорошие вещи, при условии, что вы их придерживаетесь. Я просмотрел достаточно старый код, который заставлял меня постоянно возвращаться, чтобы посмотреть определения для стольких переменных с одинаковыми именами, что я нажимаю "верблюжий корпус" (как его называли в предыдущей работе). Прямо сейчас я нахожусь на работе, у которой есть много тысяч строк совершенно некомментированного классического ASP-кода с VBScript, и это - кошмар, пытающийся разобраться.
Привязка к загадочным символам в начале каждого имени переменной не является необходимой и показывает, что само имя переменной не является достаточно описательным. В любом случае для большинства языков требуется тип переменной при объявлении, так что информация уже доступна.
Также существует ситуация, когда во время обслуживания необходимо изменить тип переменной. Пример: если переменная, объявленная как "uint_16 u16foo", должна стать 64-битной беззнаковой, произойдет одно из двух:
- Вы пройдете и измените имя каждой переменной (убедитесь, что все переменные с таким же именем не связаны), или
- Просто поменяйте тип и не меняйте название, что только вызовет путаницу.
Джоэл Спольски написал хороший пост в блоге об этом. http://www.joelonsoftware.com/articles/Wrong.html В основном все сводится к тому, чтобы не усложнять чтение кода, когда приличная среда IDE скажет, что вы хотите ввести переменную, если вы ее не помните. Кроме того, если вы сделаете свой код достаточно разделенным, вам не нужно будет помнить, какая переменная была объявлена на три страницы выше.
Разве объем не важнее, чем печатать в эти дни, например
* l for local
* a for argument
* m for member
* g for global
* etc
С современными методами рефакторинга старого кода, поиска и замены символа, потому что вы изменили его тип, утомительно, компилятор будет ловить изменения типа, но часто не будет ловить неправильное использование области видимости, здесь помогают разумные соглашения об именах.
Нет причин, по которым вы не должны правильно использовать венгерскую нотацию. Это непопулярность из-за длительной обратной реакции против неправильного использования венгерской нотации, особенно в Windows API.
В старые добрые времена, до того, как для DOS существовало что-то похожее на IDE (скорее всего, у вас не было достаточно свободной памяти для запуска компилятора под Windows, поэтому ваша разработка была сделана в DOS), вы не получали никакой помощи от наведите курсор мыши на имя переменной. (Предполагая, что у вас есть мышь.) То, с чем вам приходилось иметь дело, это функции обратного вызова событий, в которых все передавалось вам как 16-битное int (WORD) или 32-битное int (LONG WORD). Затем вам пришлось преобразовать эти параметры в соответствующие типы для данного типа события. По сути, большая часть API была практически без типов.
В результате получается API с такими именами параметров:
LRESULT CALLBACK WindowProc(HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam);
Обратите внимание, что имена wParam и lParam, хотя и довольно ужасны, на самом деле ничуть не хуже, чем называть их param1 и param2.
Что еще хуже, в Window 3.0/3.1 было два типа указателей: ближний и дальний. Так, например, возвращаемое значение из функции управления памятью LocalLock было PVOID, а возвращаемое значение из GlobalLock было LPVOID (с длинным 'L'). Затем эта ужасная запись расширилась, так что строка l lg p ointer была префиксом lp, чтобы отличить ее от строки, которая была просто malloc'd.
Неудивительно, что против такого рода вещей произошла обратная реакция.
Как программист на Python, венгерская нотация разваливается довольно быстро. В Python меня не волнует, является ли что-то строкой, мне важно, может ли она действовать как строка (т. Е. Имеет ли она ___str___()
метод, который возвращает строку).
Например, скажем, у нас есть целое число foo, 12
foo = 12
Венгерская нотация говорит нам, что мы должны называть это iFoo или чем-то еще, чтобы обозначать это целое число, чтобы позже мы знали, что это такое. За исключением Python, это не работает, или, скорее, это не имеет смысла. В Python я решаю, какой тип мне нужен, когда я его использую. Хочу ли я строку? хорошо, если я сделаю что-то вроде этого:
print "The current value of foo is %s" % foo
Обратите внимание %s
строка Фу не строка, но %
оператор позвонит foo.___str___()
и использовать результат (при условии, что он существует). foo
все еще целое число, но мы рассматриваем его как строку, если мы хотим строку. Если мы хотим поплавок, то мы рассматриваем его как поплавок. В динамически типизированных языках, таких как Python, венгерская нотация не имеет смысла, потому что не имеет значения, что это за тип, пока вы его не используете, и если вам нужен определенный тип, просто убедитесь, что он приведен к этому типу (например, float(foo)
) когда вы используете его.
Обратите внимание, что динамические языки, такие как PHP, не имеют этого преимущества - PHP пытается делать "правильные вещи" в фоновом режиме на основе неясного набора правил, который почти никто не запомнил, что часто приводит к неожиданным катастрофическим беспорядкам. В этом случае, какой-то механизм именования, например, $files_count
или же $file_name
, может быть удобно.
На мой взгляд, венгерская нотация похожа на пиявки. Возможно, в прошлом они были полезны, или, по крайней мере, они казались полезными, но в настоящее время это просто много дополнительной печати, но не много пользы.
Венгерская нотация может быть полезна в языках без проверки типов во время компиляции, поскольку она позволит разработчику быстро напомнить себе о том, как используется конкретная переменная. Это ничего не делает для производительности или поведения. Предполагается, что он улучшает читабельность кода и в основном зависит от вкуса и стиля кодирования. По этой самой причине его критикуют многие разработчики - не у всех одинаковая разводка в мозге.
Для языков проверки типов во время компиляции это в основном бесполезно - прокрутка вверх на несколько строк должна показать объявление и, следовательно, тип. Если вы используете глобальные переменные или кодовый блок для более чем одного экрана, у вас серьезные проблемы с дизайном и возможностью повторного использования. Таким образом, одна из критических замечаний заключается в том, что венгерская нотация позволяет разработчикам иметь плохой дизайн и легко сойти с рук. Это, наверное, одна из причин ненависти.
С другой стороны, могут быть случаи, когда даже языки проверки типов во время компиляции выиграют от венгерской нотации - пустых указателей или HANDLE в win32 API. Это запутывает фактический тип данных, и может быть целесообразным использовать там венгерскую нотацию. Тем не менее, если можно знать тип данных во время сборки, почему бы не использовать соответствующий тип данных.
В общем, нет веских причин не использовать венгерскую нотацию. Это вопрос лайков, политик и стиля кодирования.
Apps венгерский для меня греческий - в хорошем смысле
Будучи инженером, а не программистом, я сразу же обратился к статье Джоэла о преимуществах Apps Hungarian: "Создание неправильного кода, выглядящего неправильным". Мне нравится Apps Hungarian, потому что он имитирует то, как инженерия, наука и математика представляют уравнения и формулы, используя символы суб- и суперскриптов (например, греческие буквы, математические операторы и т. Д.). Возьмите конкретный пример закона универсальной гравитации Ньютона: сначала в стандартной математической записи, а затем в приложениях венгерский псевдокод:
frcGravityEarthMars = G * massEarth * massMars / norm(posEarth - posMars)
В математической записи наиболее заметными символами являются символы, представляющие тип информации, хранящейся в переменной: сила, масса, вектор положения и т. Д. Индексы играют вторую скрипку, чтобы уточнить: позиция чего? Это именно то, что делает Apps Hungarian; он рассказывает вам о том, что вначале хранится в переменной, а затем углубляется в специфику - о ближайшем коде можно добраться до математической записи.
Ясно, что строгая типизация может разрешить пример безопасной и небезопасной строки из эссе Джоэла, но вы не определите отдельные типы для векторов положения и скорости; оба представляют собой двойные массивы третьего размера, и все, что вы можете сделать с одним, может относиться к другому. Кроме того, имеет смысл объединить положение и скорость (чтобы создать вектор состояния) или взять их точечное произведение, но, вероятно, не добавлять их. Как печатать позволят первые два и запретить второе, и как такая система будет распространяться на все возможные операции, которые вы, возможно, захотите защитить? Если только вы не захотели закодировать всю математику и физику в своей системе ввода.
Вдобавок ко всему, большая часть разработки выполняется на слабо типизированных языках высокого уровня, таких как Matlab, или старых, таких как Fortran 77 или Ada.
Так что, если у вас есть модный язык и IDE и приложения венгерский не помогут вам, то забудьте об этом - очевидно, многие люди. Но для меня, хуже начинающего программиста, работающего на языках со слабой или динамической типизацией, я могу быстрее писать лучший код с помощью венгерских приложений, чем без.
IDE должна передать эту полезную информацию. Венгерский мог иметь какой-то (не много, а какой-то) смысл, когда IDE были гораздо менее продвинутыми.
Это невероятно избыточно и бесполезно в большинстве современных IDE, где они делают хорошую работу, чтобы сделать тип очевидным.
Плюс - для меня - просто раздражает видеть intI, strUserName и т. Д.:)
Я всегда думал, что префикс или два в нужном месте не повредит. Я думаю, что если я смогу передать что-то полезное, например, "Эй, это интерфейс, не рассчитывайте на конкретное поведение", как в IEnumerable, я должен это сделать. Комментарий может загромождать вещи намного больше, чем просто символ из одного или двух символов.
Это полезное соглашение для именования элементов управления в форме (btnOK, txtLastName и т. Д.), Если список элементов управления отображается в раскрывающемся списке в алфавитном порядке в вашей среде IDE.
Я склонен использовать венгерскую нотацию только с серверными серверными элементами управления ASP.NET, в противном случае мне сложно определить, какие элементы управления находятся в форме.
Возьмите этот фрагмент кода:
<asp:Label ID="lblFirstName" runat="server" Text="First Name" />
<asp:TextBox ID="txtFirstName" runat="server" />
<asp:RequiredFieldValidator ID="rfvFirstName" runat="server" ... />
Если кто-то может показать лучший способ иметь этот набор контрольных имен без венгерского языка, у меня возникнет соблазн перейти на него.
В книге Джоэла Спольски "Создание неправильного кода выглядит неправильно" он объясняет, что то, что все считают венгерской нотацией (которую он называет Systems Hungarian), не является тем, чем на самом деле было задумано (то, что он называет Apps Hungarian). Прокрутите вниз до заголовка " Я Венгрия", чтобы увидеть это обсуждение.
По сути, венгерские системы ничего не стоят. Он просто говорит вам то же, что скажет ваш компилятор и / или IDE.
Apps венгерский говорит вам, что переменная должна означать, и может быть полезной.
Разоблачение преимуществ венгерской нотации
- Это обеспечивает способ различения переменных.
Если тип - это все, что отличает одно значение от другого, то это может быть только для преобразования одного типа в другой. Если у вас одинаковое значение, которое конвертируется между типами, скорее всего, вы должны делать это в функции, предназначенной для преобразования. (Я видел, что венгерские остатки VB6 используют строки во всех параметрах их методов просто потому, что они не могли понять, как десериализовать объект JSON, или правильно понять, как объявлять или использовать типы, допускающие значения NULL). Если у вас есть две переменные, отличающиеся только Венгерские префиксы, и они не являются переходом от одного к другому, тогда вам нужно уточнить свое намерение с ними.
- Это делает код более читабельным.
Я обнаружил, что венгерская запись делает людей ленивыми с их именами переменных. У них есть что-то, чем можно отличить это, и они не чувствуют необходимости уточнять его назначение. Это то, что вы обычно найдете в венгерском нотном коде против современного: sSQL против groupSelectSql (или вообще никакого sSQL вообще, потому что предполагается, что они используют ORM, который был введен более ранними разработчиками.), SValue против formCollectionValue (или обычно не sValue либо, потому что они оказываются в MVC и должны использовать его функции привязки модели), sType против publishSource и т. д.
Это не может быть читаемостью. Я вижу больше sTemp1, sTemp2... sTempN из любого данного венгерского остатка VB6, чем все остальные вместе взятые.
- Это предотвращает ошибки.
Это было бы в силу числа 2, которое является ложным.
Если я чувствую, что передается полезная информация, почему бы мне не поместить ее туда, где она доступна?
Тогда кого это волнует, что думает кто-то еще? Если вы найдете это полезным, используйте обозначения.
Я мой опыт, это плохо, потому что:
1 - затем вы нарушаете весь код, если вам нужно изменить тип переменной (то есть, если вам нужно расширить 32-битное целое до 64-битного целого);
2 - это бесполезная информация, так как тип либо уже есть в объявлении, либо вы используете динамический язык, где фактический тип не должен быть таким важным в первую очередь.
Более того, с языком, принимающим общее программирование (то есть функции, в которых тип некоторых переменных не определяется при написании функции) или с системой динамической типизации (то есть, когда тип даже не определяется во время компиляции), как бы вы назвали свой переменные? И большинство современных языков поддерживают один или другой, даже если в ограниченной форме.
Статья Джоэла великолепна, но, похоже, она пропускает один важный момент:
Венгерский делает конкретную "идею" (вид + имя идентификатора) уникальной или почти уникальной для всей кодовой базы - даже очень большой кодовой базы.
Это огромно для поддержки кода. Это означает, что вы можете использовать хороший однострочный текстовый поиск (grep, findstr, "найти во всех файлах"), чтобы найти КАЖДОЕ упоминание об этой "идее".
Почему это важно, когда у нас есть IDE, которые умеют читать код? Потому что они еще не очень хороши в этом. Это трудно увидеть в небольшой кодовой базе, но очевидно в большой - когда "идея" может быть упомянута в комментариях, файлах XML, скриптах Perl, а также в местах вне контроля исходного кода (документы, вики, базы данных ошибок).
Вы должны быть немного осторожны даже здесь - например, вставка токенов в макросах C/C++ может скрыть упоминания идентификатора. Такие случаи могут быть рассмотрены с использованием соглашений о кодировании, и в любом случае они имеют тенденцию затрагивать только меньшинство идентификаторов в кодовой базе.
PS К вопросу об использовании системы типов против венгерского - лучше всего использовать оба. Вам нужен только неправильный код, чтобы выглядеть неправильно, если компилятор не поймает его за вас. Есть много случаев, когда компилятор не может его перехватить. Но где это возможно - да, пожалуйста, сделайте это вместо этого!
При рассмотрении осуществимости, тем не менее, учитывайте негативные последствия разделения типов. например, в C# обертка int не встроенным типом имеет огромные последствия. Так что это имеет смысл в некоторых ситуациях, но не во всех.
По словам мастера:
http://www.joelonsoftware.com/articles/Wrong.html
Интересное чтение, как обычно.
Экстракты:
"Кто-то где-то читал статью Симони, где он использовал слово" тип ", и думал, что он имел в виду тип, как класс, как в системе типов, как проверка типов, которую выполняет компилятор. Он этого не делал. Он объяснил очень осторожно именно то, что он имел в виду под словом "тип", но это не помогло. Ущерб был нанесен ".
"Но венгерский Apps все еще имеет огромное значение, так как он увеличивает колокейшн в коде, что облегчает чтение, запись, отладку и сопровождение кода, а главное, что неправильный код выглядит неправильно".
Убедитесь, что у вас есть некоторое время, прежде чем читать Joel On Software.:)
Некоторые причины:
- Любая современная IDE даст вам тип переменной, просто наведя указатель мыши на переменную.
- Большинство имен типов очень длинные (думаю, HttpClientRequestProvider), чтобы их можно было разумно использовать в качестве префикса.
- Информация о типе не несет правильной информации, она просто перефразирует объявление переменной, вместо того, чтобы обрисовать в общих чертах назначение переменной (думайте myInteger против pageSize).
Я не думаю, что все без ума от этого. На языках без статических типов это довольно полезно. Я определенно предпочитаю, когда он используется для предоставления информации, которой еще нет в типе. Как и в C, char * szName говорит, что переменная будет ссылаться на строку с нулевым символом в конце - это не подразумевается в char* - конечно, также поможет typedef.
Джоэл написал отличную статью об использовании венгерского языка, чтобы узнать, была ли переменная закодирована в HTML:
http://www.joelonsoftware.com/articles/Wrong.html
В любом случае, мне не нравится венгерский, когда он используется для передачи информации, которую я уже знаю.
Я начал писать код примерно в то время, когда была изобретена венгерская нотация, и в первый раз, когда я был вынужден использовать ее в проекте, я ненавидел ее.
Через некоторое время я понял, что когда все сделано правильно, это действительно помогает, и в эти дни я люблю это.
Но, как и все хорошее, это нужно изучать и понимать, и для того, чтобы сделать это правильно, нужно время.
Конечно, когда 99% программистов согласны с чем-то, что-то не так. Причина, по которой они здесь согласны, заключается в том, что большинство из них никогда не использовали правильно венгерские обозначения.
Для подробного обсуждения я отсылаю вас к сообщению в блоге, которое я сделал на эту тему.
http://codingthriller.blogspot.com/2007/11/rediscovering-hungarian-notation.html
Я думаю, что все дело в эстетическом аспекте преувеличено. Если бы это было самым важным, мы бы назвали себя не разработчиками, а графическими дизайнерами.
Я думаю, что одна важная часть заключается в том, что вы описываете роль ваших объектов, а не ее роль. Вы не называете себя HumanDustman, потому что в другом контексте вы не были бы самым важным человеком.
Для целей рефакторинга это тоже очень важно:
public string stringUniqueKey = "ABC-12345";
Что если вы решите использовать GUID вместо строки, имя вашей переменной будет выглядеть глупо после рефакторинга всего кода реферирования.
Или же:
public int intAge = 20;
Если изменить это на float, у вас возникнет та же проблема. И так далее.