...

суббота, 24 июня 2017 г.

Интегрируем Copy-Paste-Detector для Swift в Xcode

DRY Principle


Одним из основных мета-принципов программирования является DRY (Don’t Repeat Yourself). Суть данного мета-принципа проста и должна являться негаснущим маяком для любого разработчика. Она гласит, что в разрабатываемой системе не должно быть кусков кода, имеющих одинаковый код. Выражаясь более простым языком, в программе не должно быть копипасты!
Для начала, давайте точно определим, что будем считать за копипасту. Если в проекте если одинаковые файлы — это грубейшая копипаста. Если есть одинаковые классы с разным названием и выполняющие одно и тоже — грубейшая копипаста. Даже если 10 строк одинакового кода — это тоже является копипастой. Возможно вы возразите, что 10 строк кода продублировать иногда позволительно. Из моего опыта, проект в 100000 строк вполне реально писать без подобной копипасты.
Какие же минусы несёт нам копипаста?


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

Как избежать копипасты


Одним из основных способов нахождения некачественного кода, к коему относится и нарушение принципа DRY, является code-review.
Важно отметить, что у этого способа есть ряд существенных недостатков, что заставляет искать другие способы поиска некачественного кода.


  • Code-review проводится уже после того, как код написан, а фича доделана. Поэтому если дублирование найдено, то приходится переделывать этот код.
  • Ревьюер вообще может не обнаружить копипасту, особенно если старый кусок сделан давно, а в pull request это продублировано. Часто разработчики забывают написанный код крайне быстро, чтобы освободить память под новые задачи.
  • Тратится время на написание начального кода, на ревью проекта и на исправление. А также на повторное ревью. Временные затраты вы можете посчитать сами. А как известно время=деньги! Особенно в разработке.

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


Установка CPD


Для языка Swift было найдено всего 2 детектора дублирования кода.



Второй является более кастомизируемым, поддерживаемым и в целом более стабильным.
Для начала работы необходимо установить pmd путем выполнения команды в консоли “brew install pmd”.
Важно заметить, что для других языков в пакете pmd содержатся и статические анализаторы кода. В пакете для языка Swift есть только CPD (copy paste detector). Поэтому анализаторы кода нужно ставить дополнительно. Лучшим выбором на данный момент будет: http://ift.tt/1bZmpap


Интеграция с Xcode


Как ранее было выяснено, самым удобным способом обнаружения ошибок и копипасты является этап прекомпиляции проекта. В Xcode можно добавлять свои скрипты во вкладке Build Phases, чтобы они также отрабатывали на этом этапе и не давали скомпилироваться проекту успешно, если был найден дублирующийся код.


Для подключения в проект необходимо создать новый build script с именем “CopyPaste Detection” во вкладке Build Phases и добавить внутрь следующий скрипт:


# Running CPD
pmd cpd --files ${EXECUTABLE_NAME} --minimum-tokens 50 --language swift --encoding UTF-8 --format net.sourceforge.pmd.cpd.XMLRenderer > cpd-output.xml --failOnViolation true
# Running script
php ./cpd_script.php -cpd-xml cpd-output.xml

Разберем подробнее, что означает эта магия. Первая часть скрипта означает, что мы запускаем CPD для файлов, лежащих в корневой папке нашего проекта.  minimum-tokens означает минимальное количество токенов, при нахождении которые скрипт будет помечать фрагмент кода как копипасту. Токеном является некая абстрактная единица, поэтому не нужно принимать это за букву, слово и конструкцию. Эмпирическим путём было выяснено, что цифра 50 является оптимальной для языка Swift. Если взять меньше, то можно найти то, что не является копипастой, если больше, то есть большой шанс пропустить дублирование. Следующий параметр — format означает, что вывод будет происходить в xml-файл. И последний — failOnViolation говорит о том, что приложение не соберется, если найден хоть 1 фрагмент дублирующегося кода.


В результате выполнения первой части скрипта, у нас есть xml файл с логами анализа проекта. Теперь необходимо показать нашу копипасту прямо в Xcode в привычном для разработчика формате в виде warnings. Для этого добавим еще один скрипт прямо в корень проекта:


duplication as $duplication) {
   $files = $duplication->xpath('file');
   foreach ($files as $file) {
       echo $file['path'].':'.$file['line'].':1: warning: '.$duplication['lines'].' copy-pasted lines from: '
           .implode(', ', array_map(function ($otherFile) { return $otherFile['path'].':'.$otherFile['line']; },
           array_filter($files, function ($f) use (&$file) { return $f != $file; }))).PHP_EOL;
   }
}
?>

Вторая часть нашего первого скрипта запустит второй скрипт и преобразует сгенерированный xml файл в warnings в Xcode. Подробнее о том, как можно самому генерировать warnings в Xcode можно прочитать здесь.


Теперь запустим наш проект, cmd+B/R и убедимся, что наш скрипт работает. Получаем следующую картину после запуска.


В Issue Navigator:


Внутри файла:


Сopypaste — зло


Бытует мнение, что любая методология или техника хороша, если её использовать с умом и знать меру. Так вот, к copypaste в проекте это не относится. Это явное зло, с которым нужно бороться и которое является растущим техническим долгом. И никаких проявлений слабости или половинчатости здесь допускать нельзя.


Теперь у вас есть средство для борьбы с этим злом, интегрированное прямо в Xcode в качестве скрипта в билд фазу. Да поможет вам это перейти на светлую сторону!

Комментарии (0)

    Let's block ads! (Why?)

    Комментариев нет:

    Отправить комментарий