| 23. Венгерская нотация | |
| Самое идиотское изобретение человечества после фонарика на солнечных батарейках, это, конечно же, венгерская нотация. Однако, к сожалению, этот факт до сих пор не является очевидным для многих разработчиков. Поговорим о венгерской нотации и создаваемых ею проблем более детально. | |
| | |
| Лишняя информация | |
| Информация о точном типе переменной в ее имени — это лишняя информация. Любая лишняя информация сбивает пользователя с толку, и он начинает думать не о том, о чем должен. Конечно же, некоторая информация о типе пользователю все же может понадобиться, однако речь идет далеко не о том, является ли тип указателем на void, или же это строка в стиле Plain C. | |
| Во-первых, пользователю может понадобиться информация о размере переменной или объекта. С этой задачей прекрасно справится оператор sizeof. | |
| Во-вторых, пользователю нужно знать, является ли языковая сущность единичным объектом, или же это множество объектов, то есть контейнер. И не просто контейнер, а контейнер в человеческом понимании. То есть, std::string — единичный объект. | |
| Вся остальная информация является лишней. Если же для понимания программы пользователю все-таки требуется какая-то дополнительная информация, касающаяся типа той или иной сущности, то это просто означает, что система, с которой работает пользователь, плохо спроектированна, а предоставляемые сущности имеют плохие имена, не соответствующие формуле «Именование = назначение = использование». | |
| | |
| Замена типа | |
| Венгерская нотация создает массу проблем после любой замены типа. Любая замена типа в лучшем случае приведет к куче ошибок времени компиляции. В худшем — неочевидных и не всегда легко выявляемых ошибок времени выполнения. | |
| Во-первых, может просто возникнуть необходимость заменить один тип данных на другой. В этом случае придется ползать по всем исходникам и вносить исправления во все места, где успел «засветиться» объект или переменная. | |
| Во-вторых, если проект компилируется под несколько платформ, то тип данных, который на одной платформе является DWORD-ом, на другой платформе может оказаться уже QWORD-ом, а следовательно информация, содержащаяся в имени переменной уже не будет соответствовать действительности. | |
| Помимо всего прочего, попробуйте отвлечься от языка C++ и подумайте, каковы будут последствия замены типа в языках, не имеющих статической типизации. | |
| | |
| Отсутствие контроля | |
| Однако главный недостаток венгерской нотации заключается в том, что она является искусственным правилом, а не частью языковой грамматики. Например, никто не мешает взять переменную типа BYTE и назвать ее ulSize. Компилятору совершенно все равно — проверка этих соответствий не входит в круг его полномочий. В этом случае информация о типе будет являться не только лишней, но еще и не соответствующей действительности. По сути, разработчики, использующие венгерскую нотацию, переносят работу по контролю типов с плечь компилятора на свои собственные. Это один из ярких примеров того, как человек из-за сиюминутного удобства берется решать задачи, с которыми может прекрасно справиться машина. Использование всяческих префиксов и суффиксов по сути не дает никаких гарантий, по сути претупляя бдительность пользователя и давая ему еще один шанс допустить ошибку. | |
| | |
| Мотивация | |
| В реальной жизни все же существуют задачи, когда пользователю действительно нужна полная информация о типе. Например, передача данных на внешнее устройство — мы хотим точно быть уверены в том, что данные передаются именно по байтам. Или же — вычисление контрольной суммы — нам нужна четкая гарантия того, что работа с числами идет по правилам беззнаковой арифметики. В таких случаях нужно создать всяческие защитные механизмы, как уровня компиляции, так и уровня выполнения, которые позволят дать гарантиют того, что работа с объектами и переменными идет согласно с предъявляемыми требованиями. Как вы понимаете, простое добавление префиксов в имена не дает абсолютно никаких гарантий. Вопрос производительности защитных механизмов решается элементарно. Для механизмов уровня компиляции, очевидно, этот вопрос не стоит вообще; механизмы же уровня выполнения реализуются, как вариант, следующим образом: | |
| | |
| Завязка на размер | |
| Существуют ситуации, когда нужны четкие гарантии относительно размера типа. Чаще всего такие ситуации возникают, когда возникает необходимость обмениваться данными внутри системы, в которой каждый участник общения может быть скомпилирован под разные платформы. Например — сетевой маршрутизатор, общающийся по своему внутреннему протоколу с себеподобными, скомпилированный как под 32, так и под 64 бита. | |
| В таком случае стоит ввести специальный набор типов данных, из которых будут собираться пакеты для общения с себеподобными и для которых имеется некоторый механизм или гарантия обеспечения целостности размера данных, независимо от платформы. Этот набор типов нужно использовать как можно более локально и только по назначению, не позволяя им выбираться куда-то на поверхность и решать какие-то задачи, не связанные с cross-application взаимодействием. Набор данных, о которых идет речь, мог бы выглядеть следующим образом: | |