19 Венгерская нотация

      Комментарии к записи 19 Венгерская нотация отключены

В этой теме 0 ответов, 1 участник, последнее обновление  Васильев Владимир Сергеевич 2 мес., 2 нед. назад.

  • Автор
    Сообщения
  • #3597

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

    Лишняя информация

    Информация о точном типе переменной в ее имени — это лишняя информация. Любая лишняя информация сбивает пользователя с толку, и он начинает думать не о том, о чем должен. Конечно же, некоторая информация о типе пользователю все же может понадобиться, однако речь идет далеко не о том, является ли тип указателем на void, или же это строка в стиле Plain C.

    Во-первых, пользователю может понадобиться информация о размере переменной или объекта. С этой задачей прекрасно справится оператор sizeof.

    Во-вторых, пользователю нужно знать, является ли языковая сущность единичным объектом, или же это множество объектов, то есть контейнер. И не просто контейнер, а контейнер в человеческом понимании. То есть, std::string — единичный объект.

    Вся остальная информация является лишней. Если же для понимания программы пользователю все-таки требуется какая-то дополнительная информация, касающаяся типа той или иной сущности, то это просто означает, что система, с которой работает пользователь, плохо спроектирована, а предоставляемые сущности имеют плохие имена, не соответствующие формуле «Именование = назначение = использование».

    Замена типа

    Венгерская нотация создает массу проблем после любой замены типа. Любая замена типа в лучшем случае приведет к куче ошибок времени компиляции. В худшем — неочевидных и не всегда легко выявляемых ошибок времени выполнения.

    Во-первых, может просто возникнуть необходимость заменить один тип данных на другой. В этом случае придется ползать по всем исходникам и вносить исправления во все места, где успел «засветиться» объект или переменная.

    Во-вторых, если проект компилируется под несколько платформ, то тип данных, который на одной платформе является DWORD-ом, на другой платформе может оказаться уже QWORD-ом, а следовательно информация, содержащаяся в имени переменной уже не будет соответствовать действительности.

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

    Отсутствие контроля

    Однако главный недостаток венгерской нотации заключается в том, что она является искусственным правилом, а не частью языковой грамматики. Например, никто не мешает взять переменную типа BYTE и назвать ее ulSize. Компилятору совершенно все равно — проверка этих соответствий не входит в круг его полномочий. В этом случае информация о типе будет являться не только лишней, но еще и не соответствующей действительности. По сути, разработчики, использующие венгерскую нотацию, переносят работу по контролю типов с плеч компилятора на свои собственные. Это один из ярких примеров того, как человек из-за сиюминутного удобства берется решать задачи, с которыми может прекрасно справиться машина. Использование всяческих префиксов и суффиксов по сути не дает никаких гарантий, при этом притупляя бдительность пользователя и давая ему еще один шанс допустить ошибку.

    Мотивация

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

    class safe_byte
    {
        // Реализация (в том числе всей арифметики)
        // со всевозможными проверками
    };
    
    #if defined (debug)
    typedef safe_byte byte;
    #else
    typedef unsigned char byte;
    #endif // debug

    Завязка на размер

    Существуют ситуации, когда нужны четкие гарантии относительно размера типа. Чаще всего такие ситуации возникают, когда возникает необходимость обмениваться данными внутри системы, в которой каждый участник общения может быть скомпилирован под разные платформы. Например — сетевой маршрутизатор, общающийся по своему внутреннему протоколу с себе подобными, скомпилированный как под 32, так и под 64 бита.

    В таком случае стоит ввести специальный набор типов данных, из которых будут собираться пакеты для общения с себе подобными и для которых имеется некоторый механизм или гарантия обеспечения целостности размера данных, независимо от платформы. Этот набор типов нужно использовать как можно более локально и только по назначению, не позволяя им выбираться куда-то на поверхность и решать какие-то задачи, не связанные с cross-application взаимодействием. Набор данных, о которых идет речь, мог бы выглядеть следующим образом:

    namespace network
    {
    
    namespace detail
    {
    
    #if defined (platform_16)
    typedef unsigned char      byte;
    typedef unsigned int       word;
    typedef unsigned long int  dword;
    #elif defined (platform_32)
    typedef unsigned char      byte;
    typedef unsigned short int word;
    typedef unsigned int       dword;
    #elif defined (platform_64)
    typedef unsigned char      byte;
    typedef unsigned short int word;
    typedef unsigned int       dword;
    #else
    #error Unknown platform
    #endif
    
    } // namespace detail
    
    } // namespace network

    Заключение

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

Для ответа в этой теме необходимо авторизоваться.