Качество программного кода. Позаботьтесь о долгой жизни ваших программных продуктов!::Журнал СА 1-2.2014
www.samag.ru
     
Поиск   
              
 www.samag.ru    Web  0 товаров , сумма 0 руб.
E-mail
Пароль  
 Запомнить меня
Регистрация | Забыли пароль?
О журнале
Журнал «БИТ»
Подписка
Где купить
Авторам
Рекламодателям
Магазин
Архив номеров
Вакансии
Контакты
   

ЭКСПЕРТНАЯ СЕССИЯ 2019


  Опросы

Какие курсы вы бы выбрали для себя?  

Очные
Онлайновые
Платные
Бесплатные
Я и так все знаю

 Читать далее...

1001 и 1 книга  
28.05.2019г.
Просмотров: 951
Комментарии: 2
Анализ вредоносных программ

 Читать далее...

28.05.2019г.
Просмотров: 1070
Комментарии: 1
Микросервисы и контейнеры Docker

 Читать далее...

28.05.2019г.
Просмотров: 810
Комментарии: 0
Django 2 в примерах

 Читать далее...

28.05.2019г.
Просмотров: 646
Комментарии: 0
Введение в анализ алгоритмов

 Читать далее...

27.03.2019г.
Просмотров: 1251
Комментарии: 0
Arduino Uno и Raspberry Pi 3: от схемотехники к интернету вещей

 Читать далее...

Друзья сайта  

Форум системных администраторов  

sysadmins.ru

 Качество программного кода. Позаботьтесь о долгой жизни ваших программных продуктов!

Архив номеров / 2014 / Выпуск №1-2 (134-135) / Качество программного кода. Позаботьтесь о долгой жизни ваших программных продуктов!

Рубрика: Карьера/Образование /  Пятая пара

Денис Силаков ДЕНИС СИЛАКОВ, кандидат ф.-м. н., ЗАО «РОСА», член рабочей группы LSB, старший архитектор, занимается автоматизацией разработки ОС «РОСА», denis.silakov@rosalab.ru

Качество программного кода
Позаботьтесь о долгой жизни ваших программных продуктов!

Понятие «качество программного продукта» включает в себя не только полноту и корректность реализации требуемого функционала, но и простоту поддержки и модификации программы. Как же обезопасить себя и коллег от кошмара поддержки нечитаемого кода?

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

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

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

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

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

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

Форматирование и оформление кода

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

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

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

Типичный случай: если в одних местах для отступов используются четыре пробела, а в других – табуляция, то в текстовом редакторе, у которого ширина табуляции равна четырем пробелам, ничего криминального заметно не будет. Но вот если открыть такой код в редакторе, где табуляция равна восьми пробелам, то получится изрядная каша. Поэтому не стоит довольствоваться тем, что в вашей IDE код выглядит красиво, необходимо обеспечить его читаемость вне зависимости от настроек редактора.

Правила форматирования кода включают и ряд других аспектов, единообразие которых влияет на восприятие программы:

  • использование пробелов рядом со скобками и операторами в логических и арифметических выражениях (например, обособлять ли в конструкции «i=1» знак равенства пробелами или нет);
  • правила расстановки фигурных скобок, ограничивающих структурные блоки программы во многих языках (например, следует ли в программе на языке С ставить открывающую фигурную скобку на той же строке, где располагается логическое условие «if», или нужно переносить ее на новую строку);
  • правила разбиения логического выражения с множеством условий на несколько строк;
  • требования к вертикальному выравниванию, чтобы несколько подряд идущих схожих инструкций выглядели красиво:

int son = 0;

int daughter = 1;

int father = 2;

  • и другие моменты, часто специфичные для конкретных языков программирования.

Правила именования

Помимо собственно форматирования, простота восприятия кода во многом зависит от способа именования переменных, функций, членов классов и прочих сущностей. Азбучная истина, что имена должны быть «говорящими», знакома всем, но при ее реализации на практике между разработчиками возникают разночтения. Начнем с вопроса: насколько «говорящими» должны быть имена?

На заре программирования, да и на первых порах развития языков высокого уровня, названия давали по возможности короткие. Ведь размеры экранов по горизонтали были ограничены (к слову, ограничение на длину строки кода в 80 символов до сих пор много где встречается), имена эти приходилось вводить вручную, и в целом средства разработки не очень располагали к использованию длинных идентификаторов. Посмотрите для интереса на список функций POSIX [1] – имена большинства из них не превосходят десяти символов, а те, что длиннее, появились в конце XX – начале XXI века.

Однако прогресс не стоит на месте, экраны мониторов у разработчиков теперь обычно вмещают больше 80 символов (даже если вы набираете код на смартфоне), а IDE предоставляют такие удобные возможности, как индексирование и автодополнение. Неудивительно, что и длина имен увеличилась и идентификаторы стали более информативными – сравните уже упомянутый POSIX с именами классов в .NET [2] или Java [3].

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

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

Не стоит забывать и о некоторых классических названиях – например, обычно можно без комментариев использовать «i» в качестве счетчика цикла, называть методы класса, возвращающие или устанавливающие значение некоторого поля Field – getField() и setField() соответственно и так далее.

Если вы посмотрели на примеры по ссылкам выше, то наверняка заметили, что многие имена состоят из нескольких слов. Желание использовать многословные названия вполне логично (особенно для функций и методов), но перед начинающими программистами традиционно встает вопрос: как разделять слова в названии?

Примеры POSIX, .NET и Java показывают наиболее популярные подходы к этому вопросу, а именно использование для разделения слов подчеркивания и так называемого верблюжьего регистра («CamelCase»), когда каждое слово в идентификаторе начинается с заглавной буквы (возможно, кроме первого – соответствующие варианты получили название «UpperCamelCase» и «lowerCamelCase»). Иногда можно встретить в качестве разделителя дефис, но во многих языках этот символ нельзя использовать в имени идентификатора.

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

Например, строки могут предваряться префиксом «s» (sMyName), указатели – префиксом «p» (pMyVariable) и так далее.

Использование констант

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

Ведь у человека, просматривающего код, может возникнуть вопрос: почему используются именно это число, строка или другое выражение и откуда оно взялось? Конечно, можно это указать в комментарии, но если фиксированные значения мелькают во многих местах, то комментировать их все вряд ли разумно.

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

Классическим примером здесь является число Пи – единственным случаем, когда вам может понадобится «переопределить» его значение, является изменение точности (то есть если вы вдруг решите, что «3.14» недостаточно для расчетов и надо «3.14159»). Если же отбросить вероятность возникновения такой ситуации, то число Пи вряд ли изменится.

Тот, кто просматривает программу и понимает, что она производит, скажем, тригонометрические расчеты, наверняка догадается, что скрывается за повсеместно используемым числом «3.14». Однако вот сможет ли он распознать, что за «1.047» скрывается Пи/3? Можно, конечно, писать «3.14/3». Но согласитесь, «Pi» и «Pi/3» короче, удобнее и нагляднее.

Размер структурных блоков

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

Отчасти ответ на этот вопрос дают сами парадигмы, многие из которых рекомендуют создавать самодостаточные, но в то же время простые модули. Например, философия UNIX подразумевает создание программ, которые делают что-то одно, но делают это хорошо.

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

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

Например, получивший репутацию «write-only» языка Perl позволяет уместить в пол-экрана такую логику, которая в Java может спокойно превысить вертикальный размер монитора вдвое (это при условии, что вы следуете правилам форматирования и не помещаете по несколько операторов на одну строку), но вот разобраться в коде Perl будет сложнее.

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

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

Документированность кода

Широко известно следующее полушуточное утверждение, что хорошо написанный код в комментариях не нуждается – он очевиден сам по себе. Конечно, к такому положению вещей стоит стремиться, но далеко не всегда это получается, и без текстовых подсказок на «человеческом» языке не обойтись.

Впрочем, избыточное комментирование также не является хорошей практикой – согласитесь, не очень удобно изучать код, каждые две строки которого предваряются десятком строк комментариев.

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

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

В современном мире комментарии в коде часто являются основой документации к программе. Широкое распространение получили системы наподобие Javadoc и Doxygen, автоматически генерирующие документацию на основе программного кода и комментариев.

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

Использование подобных инструментов требует оформлять код строго определенным образом (чтобы инструмент мог распознать описания каждого параметра функции, возвращаемого значения и так далее).

Поэтому побочным эффектом их применения является единообразный стиль комментариев, что тоже идет на пользу читаемости.

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

Разрешить эту дилемму не всегда просто. Так что отсутствие комментария – плохо, но неверный комментарий – еще хуже.

Общепринятые стили оформления кода

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

Например, венгерская нотация изначально появилась и активно использовалась в Microsoft (в частности, в MFC), а вот создатель ядра Linux Линус Торвальдс считает ее ущербной и избыточной.

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

Авторы многих современных языков сами разрабатывают для своих продуктов рекомендации по оформлению написанных на них программ. Например, существуют официальные рекомендации для Java [4], C# [5], Perl [6] и многих других языков. В рамках этих стандартов возможны некоторые вариации от команды к команде, но такие различия обычно минимальны и к ним можно быстро адаптироваться.

В случае языков с более глубокими корнями (в частности, С) ситуация сложнее – для них тоже существуют выработанные десятилетиями рекомендации по оформлению кода, но для одного языка может существовать несколько альтернативных наборов правил. Так, для того же С активно используется K&R, основанный на стиле из классической книги Кернигана и Ритчи по этому языку.

Однако популярны и другие стили форматирования – например, используемый в BSD и многих версиях MS Visual Studio Олмана, или GNU, применяемый во всех продуктах одноименного проекта.

Про некоторые различия этих стилей можно почитать в Википедии [7]. Кстати, нередко сторонники каждого конкретного стиля крайне негативно относятся к альтернативным подходам. Имейте это в виду, когда будете вливаться в новую команду!

Наконец, во многих компаниях и проектах стиль оформления кода является обязательной частью более общих соглашений о кодировании (Coding conventions). Помимо собственно оформления, они могут включать в себя правила организации файлов в проекте (например, требовать следования принципу «один класс – один файл» в ООП), рекомендации по использованию той или иной парадигмы программирования, алгоритмов, шаблонов проектирования и тому подобного.

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

Инструментальная поддержка

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

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

К тому же редко бывает, что код создается плавно и равномерно – программисты часто переписывают одни и те же участки по несколько раз и копируют код из других источников. Не всегда текстовые редакторы способны обработать все подобные изменения «на лету».

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

Инструменты, проверяющие стиль написания программ, ведут свою родословную от утилиты lint, разработанной еще в конце семидесятых годов прошлого века для анализа программ на языке C. Сегодня подобные утилиты существуют и для других языков, при этом традиционно многие из них содержат слово «lint» в своем названии.

Например, pylint проверяет программы на языке Python, cpplint – на языке С++ и так далее. Конечно, использование «lint» не обязательно, есть много отличных программ с другими говорящими названиями – например, checkstyle для Java.

Отмечу, что «lint» стало практически нарицательным словом для обозначения различного рода статических проверок на корректность во многих других областях ИТ.

Например, утилита dlint используется для поиска DNS-ошибок, fslint – для поиска ненужных файлов в файловой системе.

При разработке дистрибутивов Linux, распространяющих ПО в виде прекомпилированных пакетов, для контроля их качества используют инструменты lintian (в Debian, Ubuntu и производных) и rpmlint (в RPM-based системах – Red Hat/Fedora, OpenSUSE, ROSA, ALT Linux и других).

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

Имеются и соответствующие инструменты для разных языков программирования – C++ Beautifier, indent (для языка C), PerlTidy, PHP Beautifier, JavaScript Beautifier и им подобные.

Многие инструменты могут интегрироваться со средами разработки, такими как MS Visual Studio или Eclipse.

Рисунок 1. Запуск pylint на проекте в Eclipse (изображение с pylint.org)

Рисунок 1. Запуск pylint на проекте в Eclipse (изображение с pylint.org)

Запуск всевозможных анализаторов – дело полезное, но само по себе рутинное. Не удивительно, что в больших проектах оно осуществляется автоматически. В частности, инструменты непрерывной интеграции (например, Hudson и Jenkins) предоставляют плагины для запуска различных утилит анализа кода и удобного представления их результатов.

Рисунок 2. Отчет pylint в Jenkins (изображение с сайта www.shiningpanda-ci.com)

Рисунок 2. Отчет pylint в Jenkins (изображение с сайта www.shiningpanda-ci.com)

Впрочем, автоматизация – это хорошо, но многие огрехи никакой инструмент исправить не в состоянии, и лучше изначально следовать правилам оформления кода, чем потом работать над его приведением в порядок.

***

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

При работе в команде необходимо придерживаться единого стиля, даже в ущерб собственным вкусам. Если вы приходите в проект или компанию с богатой историей, то выбирать скорее всего не придется – вас просто поставят перед фактом, какой именно набор правил следует использовать. А вот в учебных командных проектах (если таковые предусмотрены в вашем учебном заведении) такие правила необходимо выработать самостоятельно перед началом написания кода. Однако брать эти требования с потолка не стоит – для всех популярных языков программирования уже имеются более-менее стандартные правила, на них и надо основываться. Отлично сформулировали разработчики Google в своем C++ Style Guide – «придерживайтесь здравого смысла и будьте последовательны».

  1. Cписок функций POSIX – http://www.opennet.ru/man.shtml?list=3.
  2. Имена классов в .NET – http://msdn.microsoft.com/en-us/library/System.aspx.
  3. Классы Java – http://docs.oracle.com/javase/7/docs/api/allclasses-frame.html.
  4. Рекомендации по оформлению написанных для Java – http://www.oracle.com/technetwork/java/codeconv-138413.html.
  5. Рекомендации по оформлению написанных для C# – http://msdn.microsoft.com/en-us/library/ff926074.aspx.
  6. Рекомендации по оформлению написанных для Perl – http://perldoc.perl.org/perlstyle.html.
  7. Различия стилей – http://en.wikipedia.org/wiki/Indent_style.

Ключевые слова: качество кода, стили программирования, рекомендации по оформлению программ.


Комментарии отсутствуют

Добавить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

               Copyright © Системный администратор

Яндекс.Метрика
Tel.: (499) 277-12-41
Fax: (499) 277-12-45
E-mail: sa@samag.ru