среда, 25 ноября 2020 г.

Как названия переменных и методов делают сопровождение кода дороже?

    Да легко.

    Например очень запутывает код с такими элементами:


  • Несоответствие названий переменной и метода/свойства, из которого получают значение переменной:

    • var renderRequired = this._isInitializingRequired();

      Не понятно как необходимость 'initializing' может превратиться в необходимость 'render', под этими словами я понимаю совершенно разные алгоритмы/события/состояния/действия и при чтении кода с переменной renderRequired не буду связывать это условие с состоянием 'initializing', а при изучении метода _isInitializingRequired не буду предполагать его использование для алгоритма render.


    • this._isUpdateAllowed() && this._updateDOMComponent(renderRequired);

      Из названия флажка "разрешено обновление" я никогда не подумаю о его связи с операцией "обновить DOM компонент", ведь он влияет на 'update' всего объекта.


    •   endUpdate: function endUpdate() {
          var renderRequired = this._isInitializingRequired();
          this.callBase();
          this._isUpdateAllowed() && this._updateDOMComponent(renderRequired);
        },

      В функции с названием 'endUpdate' логически не должен использоваться флажок 'isUpdateAllowed' ведь это уже конец операции обновления, только что она вся была завершена и в этот момент она не может быть разрешена или запрещена.


    •   _updateDOMComponent: function _updateDOMComponent(renderRequired) {
          if (renderRequired) {
            this._renderComponent();
          } else if (this._requireRefresh) {
            this._requireRefresh = false;
            this._refresh();
          }
        },

      Из названия функции '_updateDOMComponent' никак не понять что она может выполнить или операцию 'render', или операцию 'refresh', или совсем ничего не сделать. Длят таких 'условных' операций я часто вижу в названии 'tryXXX' и сразу предполагаю вариант "ничего". 


    • <div class="dx-drawer-panel-content">
        <div id="content">content</div></div>

      Под буквами 'content' я понимаю "внутреннее содержимое" и стиль 'dx-drawer-panel-content' я ожидаю найти на внутреннем элементе, но вместо этого этот стиль накладывается на его родительский элемент.

    • const valueFields = descriptions.values;

      'values' - это массив значений. 'valueFields' - это массив имен (наверное свойства какого-то объекта), из которых будут браться значения. Но обе переменные возвращают одно и то же значение. Такое различие в названиях переменных сильно усложняет понимание алгоритма по коду.

    • const expressionArg = new SummaryCell(...);

      Из букв 'expressionArg' я никогда не догадаюсь что переменная ссылается на объект SummaryCell.


    • return {
          direction: drawer.calcTargetPosition(),
          $panel: $(drawer.content()),
          $content: $(drawer.viewContent()),
      };
      Здесь очень художественное передергивание: position становится direction'ом, content превращается в panel, а content'ом становится viewContent. Автор в этом может и не будет путаться, но наследникам кода придется непросто.

  • Присвоение в переменную значения другого типа:

    •     let isEmpty = item.isEmpty;
          if(isEmpty && isEmpty.length) {
              isEmpty = item.isEmpty.filter(function(isEmpty) { return isEmpty; }).length === isEmpty.length;
          }
      Переменная isEmpty возвращала Array, а стала возвращать Boolean

  • Несоответствие имени переменной/функции и возвращаемого значения:

    •     rowItem.isEmpty = [];
      От переменной/свойства с именем 'isEmpty' я ожидаю значение типа Boolean, а не массив.


    •     const cell = expressionArg.cell();
          const value = cell[i] = expression(expressionArg);
      'cell' - это ячейка. Одна ячейка. Ну какой там может быть массив?




    • Метод объекта Drawer называется 'content()' но возвращает элемент с классом 'drawer-panel-content' и в то же время рядом с этим элементом есть другой элемент с классом 'drawer-content', который я и ожидаю получить из метода 'content()'. Прямо наперсточники...




    • Слово 'content' авторы кода используют в двух смыслах: как название для внутренней деталюшки компонента которая содержит клиентский элемент и как название этого клиентского элемента. Такой выбор названия постоянно запутывает, не понятно что именно имеется ввиду в каждом конкретном месте: внутренняя запчасть или клиентский элемент? Обязательно нужно глянуть...

  • Использование '&&' вместо двухстрочных 'if':

    • rowOptions.watch && rowOptions.watch(() => rowOptions.rowIndex)

      Такой вызов метода очень легко пропустить даже когда специально ищешь именно его. А при "беглом проглядывании" алгоритма я очень часто пропускаю вызов в таком оформлении кода.

  • Несогласованность названия функции и реального кода внутри нее:

    •     _disposeDataSource: function() {
              const that = this;
              const dataSource = that._dataSource;

              if(dataSource) {
                  dataSource.off('changed'that._changedHandler);
                  that._dataSource = undefined;
              }
          },
      Буквы 'dispose' подразумевают безвозвратное освобождение/закрытие использованных ресурсов. После такого освобождения ресурсов объект надо создавать заново. Но этот код всего лишь убирает подписку на событие changed, хотя у объекта '_dataSource' есть метод 'dispose'. Это неожиданная реализация метода, которая не понятна из его названия.