Лекция
Привет, Вы узнаете о том , что такое метрики кода, Разберем основные их виды и особенности использования. Еще будет много подробных примеров и описаний. Для того чтобы лучше понимать что такое метрики кода, метрика программного обеспечения, исходящие зависимости, входящие зависимости, цикломатическая сложность, cyclomatic complexity , настоятельно рекомендую прочитать все из категории Проектирование веб сайта или программного обеспечения.
Это количественные показатели, которые можно измерить и которые могут дать представление о качестве кода
Метрики программного кода, в отличие от Agile или Performance метрик, несут истинную ценность только для программиста. Метрики собираются до и после проведения большого рефакторинга для понимания, что же изменилось и не стало ли хуже. В больших проектах метрики позволят быстро обнаружить проблемные участки кода подобно тому, как performance-профайлеры указывают на проблемы с производительностью в конкретном месте. Метрики встраиваются в CI системы, такие как TeamCity или Jenkins, чтобы иметь возможность контролировать изменения значений метрик с каждым комитом кода в репозиторий.
Достаточно часто программистам приходится поддерживать чужой код, очень часто этот код выглядит не самым лучшим образом, и сопровождать его очень сложно. Если это приложение не придется выбросить в скором времени, то естественно его стоит привести в человеческий вид, т.е. отрефакторить. Было бы хорошо иметь какую-нибудь метрику, которая позволила бы оценить качество кода и определить места, которые стоит улучшить. Такая метрика позволила бы оценить, например, то, как программист пишет исходный код или то, насколько качественен код в том приложении, которое Вы собираетесь поддерживать.
многие ИДЕ предоставляет встроенное средство, которое позволяет оценить код вашего проекта.
Получить оценку вашего кода можно нажав правой кнопкой на проекте и выбрав пункт “Calculate Code Metrics” (Эта функциональность доступна в Visual Studio 2008 Team System и Visual Studio 2010 начиная с Premium версии).
Большинство метрик могут быть подсчитаны для метода, класса, пространства имен и целого проекта. Однако не все метрики одинаково полезны на каждом из уровней. Рассмотрим каждую метрику только для того уровня, для которого она несет наибольшую ценность.
Что можно поменять(количественно оценить):
и другие параметры
Maintainability Index – комплексный показатель качества кода. Этот показатель разработан специалистами из Carnegie Mellon Software Engineering Institute. Рассчитывается метрика по следующей формуле:
MI = MAX(0, (171 — 5.2 * ln(HV) — 0.23 * CC — 16.2 * ln(LoC)) * 100 / 171)
Эта метрика может принимать значения от 0 до 100 и показывает относительную сложность поддержки кода. Чем больше значение этой метрики, тем легче поддерживать код.
Visual Studio помечает методы/классы зеленым цветом, если значение метрики находится в пределах от 20 до 100, желтым цветом, если значение находится в пределах от 10 до 20, и красным цветом, когда значение меньше 10.
По умолчанию панель графиков зависимостей NDepend отображает график зависимостей между сборками .NET: NDepend предлагает изучить график зависимостей между пространствами имен проекта Visual Studio. В окне «Редактор решений» (или «Редактор кода») щелкните правой кнопкой мыши меню, NDepend предлагает изучить график зависимостей между типами пространства имен. Обратите внимание, что NDepend поставляется с эвристикой, чтобы попытаться вывести пространство имен из папки в обозревателе решений. В окне «Редактор решений» (или «Редактор кода») щелкните правой кнопкой мыши меню, NDepend предлагает изучить график зависимостей между членами (методы + поля) типа. Обратите внимание, что NDepend поставляется с эвристикой, чтобы попытаться вывести тип из исходного файла в обозревателе решений.
DSM (Matrix Dependency Structure Matrix) - это компактный способ представления и навигации по зависимостям между компонентами. Для большинства инженеров разговор о зависимостях означает разговор о чем-то, что выглядит так:
DSM используется для представления той же информации, что и граф.
Как следствие, в приведенном ниже снимке связь с IronPython с IronMath представлена непустой ячейкой в матрице и стрелкой на графике.
Зачем использовать два разных способа, график и DSM, чтобы представлять одну и ту же информацию? Потому что есть компромисс:
Как только один понимает принципы DSM, как правило, один предпочитает DSM над графом для представления зависимостей. Это связано главным образом с тем, что DSM предлагает возможность мгновенно выявлять структурные шаблоны . Это объясняется во второй половине текущего документа.
NDepend предлагает контекстно- зависимую справку для обучения пользователя тому, что он видит в DSM. DSM DDepend опирается на простую 3-красочную схему для ячейки DSM: синий, зеленый и черный. При наведении строки или столбца с помощью мыши в контекстно-зависимой справке объясняется значение этой схемы раскраски:
Непустая ячейка DSM содержит число. Это число представляет сильные стороны связи, представленной клеткой. Сила связи может быть выражена в терминах количества членов / методов / полей / типов или пространств имен, участвующих в связи, в зависимости от фактического значения параметра « Вес на ячейках» . В дополнение к Контекстно-чувствительной справке, DSM предлагает также Информационную панель,которая объясняет связь с простым английским описанием:
NDepend DSM поставляется с многочисленными опциями, чтобы попробовать:
Рекомендуется использовать все эти функции самостоятельно, анализируя зависимости в вашей базе кода.
NDepend Metric View предлагает множество возможностей для визуализации метрик кода приложения. Таким образом, недостатки кода и шаблоны кода становятся более очевидными. Такие перспективы направлены на то, чтобы улучшить ваше понимание базы кода, чтобы вы могли принять лучшее решение увеличить качество кода и удобство обслуживания кода.
Метричность и трекинг
в метрике В метрическом представлении база кода представлена через Treemap . Об этом говорит сайт https://intellect.icu . Treemapping - это алгоритм визуализации для отображения древовидных данных с использованием иерархии вложенных прямоугольников.Древовидная структура, используемая в NDepend treemap, является обычной иерархией кода:
Треугольные прямоугольники представляют собой элементы кода. Параметр уровень определяет тип элемента кода ,представленный единичных прямоугольники. Параметр уровня может принимать значения 5: сборки, пространство имен, тип, метод и поле. Две картинки ниже показывают один и тот же базовый код , отображаемый Method уровня (слева) ипространство имен уровень (справа).
Обратите внимание, что элементы кода, принадлежащие к тем же элементам родительского кода (например, методы в классе или классы в пространствах имен), расположены рядом с treemap.
Параметр Размер treemap определяет размер прямоугольников. Например, если уровень установлен на тип, а метрика - на число строк кода , каждый прямоугольник единицы представляет собой тип, а размер прямоугольника единицы пропорционален количеству строк кода соответствующего типа.
Для каждого уровня элемента кода предлагается несколько кодовых показателей. Также, как объясняется далее в этом документе, можно определить собственные метрики кода и визуализировать их посредством treemaping.
Опция позволяет сгенерировать кодовый запрос CQLinq дляВыберите элементы верхнего кода N в соответствии с выбранными метриками кода.
В приведенном выше разделе объясняется, как визуализировать кодовую метрику благодаря трекингованию. Вторая метрика кода может быть представлена красными элементами элементов прямоугольника. Следовательно, метрический вид NDepend удобен для корреляции двух кодовых метрик. Например, снимок экрана ниже показывает покрытие кода на основе тестов на базе кода NDepend. С помощью NDepend или некоторых других инструментов можно получить коэффициент покрытия кода для каждого метода / типа / пространства имен / сборки. Но, объединив # строк кода с коэффициентом покрытия цвета, цветной treemaping, мы получаем уникальное проницательное представление о том, что покрывается или нет с помощью тестов. Например, несмотря на то, что база кода NDepend более 80% покрыта тестами, что является хорошим показателем, цветной treemap показывает красные области, такие как NDepend.Addin.dll
SQL подобный синтакс
Запросы к базе кода (code base), чтобы получить метрики
SELECT TOP 10 METHODS ORDER BY NbLinesOfCode DESC
SELECT METHODS WHERE NbLinesOfCode > 10
SELECT FIELDS WHERE HasAttribute "System.ThreadStaticAttribute"
Efferent coupling (Ce): внутренняя связанность, число типов внутри сборки, которые зависят от типов из вне сборки
Afferent coupling (Ca): внешняя связанность, число типов вне сборки которые зависят от типов в внутри сборки
Instability (I): отношение внутренней связанности(Ce) к общей связанности, индикатор устойчивости к изменениям. I = Ce / (Ce + Ca) I=0 – полностью стабильная сборка, сложная для модификации. I=1 – нестабильная сборка, внутри слабая связанность
Abstractness (A): абстрактность, отношение числа внутренних абстрактных типов к числу внутренних типов
. A=0 – полностью «конкретная» сборка
A=1 – полностью абстрактная сборка
Relational Cohesion (H): относительная сцепленность, среднее число внутренних отношений на тип: H = (R + 1) / N, где R = число отношений внутри сборки,
N = число типов сборки
Классы внутри сборки должны сильно соотносится друг с другом, и сцепленность должна быть высокой.
С другой стороны, слишком большие значение могут означать излишнюю связанность. Хорошие значения сцепленности 1.5 <=H<= 4.0
Се = внутренняя связанность, Са – внешняя, I – стабильность, N – число типов сборки, А – абстрактность, Н – относительная сцепленность
Принцип SRP утверждает, что класс должен иметь не более чем одну причину для изменения. Такой класс сцепленный (cohesive). Высокое значение означает плохо сцеплений класс. Типы, у которых LCOM > 0.8 могут быть проблемными. Тем не менее, очень сложно избежать таких не- сцепленных типов
Число следующих выражений в методе: if, while, for, foreach, case, default, continue, goto, &&, ||, catch, ? : (ternary operator), ?? (nonnull operator)
Эти выражения не учитываются: else, do, switch, try, using, throw, finally, return, object creation, method call, field access
CC > 15 сложно понимать, CC > 30 очень сложные и должны быть разбиты на более мелкие методы (кроме сгенерированного кода)
Main sequence в терминах NDepend, A + I = 1, Представляет оптимальный баланс между абстрактностью и стабильностью D – это нормализированное расстояние от main sequence, 0 <=D <=1 Сборки с D > проблематичны Но, в реальной жизни, сложно избежать таких сборок. Просто необходимо удерживать число таких сборок минимальным.
Метрика показывает количество классов, которые используются внутри измеряемого класса. Например, для класса Calculator ниже метрика будет равна 2.
public class Calculator { private DataSource1 _DS1 { get; } = new DataSource1(); private DataSource2 _DS2 { get; } = new DataSource2(); public int Calculate() { return _DS1.GetNumber1() * _DS2.GetNumber2(); } }
Чем больше у класса исходящих зависимостей, тем больше требуется времени для внесения в него изменений, так как дополнительно приходится изучать поведение зависимых классов. Для уверенного внесения изменения в метод Calculate не достаточно изучить только его реализацию, так как она не является изолированной от окружающего мира. Необходимо понять, как работают классы DataSource1 и DataSource2.
Большое количество исходящих зависимостей (5+) обычно говорит о том, что класс делает слишком много, то есть нарушает принцип единой ответственности. Такие классы тяжело сопровождать и они имеют риск бесконечно увеличиваться в размерах. Привести к норме значение метрики поможет выбор более конкретного имени для класса (не Calculator, а например EmployeeCompensationCalculator), вынесение неподходящей логики в отдельные классы, уместное применение шаблонов, помогающих соблюдать принцип единой ответственности (Decorator, Mediator, Chain o Responsibility, Command и тд).
Если класс спроектирован так, что все его зависимости инициализируются в конструкторе, то их количество легко подсчитать вручную, только изучив тело конструктора. Для автоматического подсчета метрик в Visual Studio 2017 можно перейти во вкладку Analyze -> Calculate Code Metrics -> For Solution. Появится окно с метриками под названием Code Metric Results. Сама метрика будет называться Class Coupling.
Метрика показывает количество классов, которые ссылаются на измеряемый класс. Чем выше значение метрики для некоторого класса, тем потенциально возрастает количество мест системы, которые могут быть поломаны в следствии внесенных в него изменений. Однако, если входящие зависимости на некоторый класс отсутствуют совсем, то он является мусором.
public class DataStorage { public int GetValue() => 5; } public class Calculator { public int Calculate() => new DataStorage().GetValue() * 5; } public class AnotherCalculator { public int Calculate() => new DataStorage().GetValue() * 10; }
Класс DataStorage используется в классах Calculator и AnotherCalculator, следовательно, логика обоих может быть поломана в результате изменения в методе GetValue.
Обычно чрезмерное использование некоторого класса во многих местах проекта говорит о том, что он нарушает принцип единой ответственности и является God Object’ом, храня в себе функционал на все случаи жизни. Такой класс требует рефакторинга. Однако, класс может соблюдать SRP и также являться часто используемым в силу своей специфики. В таком случае высокое значение метрики говорит о высоком уровне повторного использования кода.
Visual Studio 2017 позволяет отыскать все ссылки на тип при помощи опции Find All References. Также значение метрики показывают различные статические анализаторы кода.
Метрика определяет сложность некоторого участка кода, например, метода, путем подсчета в нем количества условных операторов, циклов, switch и case операторов.
Cyclomatic Complexity – показывает структурную сложность кода, т.е. количество различных ветвей в коде. Чем больше этот показатель, тем больше тестов должно быть написано, для полного покрытия кода.
//Цикломатическая сложность - 1 int Calculate(int x, int y) { return x / y; } //Цикломатическая сложность - 2 int Calculate(int x, int y) { if (y == 0) throw new ArgumentException(); return x / y; } //Цикломатическая сложность - 3 int Calculate(int x, int y) { if (x == 0) throw new ArgumentException(); else if (y == 0) throw new ArgumentException(); else //блок else не влияет на значение метрики return x / y; } //Цикломатическая сложность - 4 static int Calculate(int x, int y) { if (x > y) throw new ArgumentException(); else if (x == y) return x; int rangeSum = 0; for (int i = x; i <= y; i++) rangeSum += i; return rangeSum; }
Чем выше значение метрики для участка кода, тем сложнее его читать и вносить изменения. Также усложняется написание юнит-тестов, которые бы покрывали все возможные пути выполнения метода (кстати, метрика в юнит-тестировании, которая подсчитывает степень покрытия путей выполнения кода, называется Branch Coverage).
Рефакторить код с высокой цикломатической сложностью (больше 25 считается высокой) можно пересмотром выбранного решения в сторону более красивого, повторным использованием дублирующейся логики, применением шаблона проектирования (например, Rules) либо подхода Table-Driven Methods.
В Visual Studio 2017 метрика отображается в окне Code Metric Results.
Depth of Inheritance – глубина наследования. Эта метрика показывает для каждого класса, какой он по счету в цепочке наследования. Например, есть 3 класса A, B, C, B унаследован от А, а С унаследован от В, то значение этой метрики для классов A,B и C будет равно соответственно 1, 2 и 3.
Показывает порядковый номер класса в иерархии наследования.
public class A { } // Глубина наследования - 1 public class B : A { } //Глубина наследования - 2 public class C : B { } //Глубина наследования - 3
Наследование добавляет классу новую функциональность в виде свойств и методов. Следовательно, класс со значением метрики 5 будет содержать в себе функционал четырех классов выше по иерархии и анализ его поведения будет затруднительным. C другой стороны, классы, находящиеся «на дне» иерархии, повторно используют функционал, описанный в родителях, что есть хорошо. По этой причине нельзя точно сказать хорошо или плохо наличие в проекте классов с большим значением данной метрики. Необходимо анализировать каждый конкретный случай.
В Visual Studio 2017 метрика отображается в окне Code Metric Results.
Метрика показывает количество строк кода, причем строки с комментариями, пустые строки или фигурные скобки в расчет не принимаются. В основном метрика применяется для обнаружения больших методов и классов.
В Visual Studio 2017 метрика отображается в окне Code Metric Results.
Lines of Code – показывает количество строк кода. Этот показатель показывает не точное количество строк в вашем файле, т.к. подсчет основан на IL-коде. В расчет не берутся пустые строчки, комментарии, строчки со скобками, объявление типов и пространств имен. Большое количество строк в методе/классе может показывать на ошибки в проектировании и на то, что этот код можно разделить на несколько частей.
Class Coupling – показывает степень зависимости классов друг с другом. В расчет берутся уникальные классы из параметров, локальных переменных, возвращаемого типа, базового класса, атрибутов (полный список можно найти в MSDN). Хороший дизайн программного обеспечения предполагает небольшое количество связанных классов. Чем их больше, тем сложнее в дальнейшем переиспользовать этот класс, а также поддерживать, т.к. существует очень много зависимостей.
Maintainability Index является комплексной метрикой, которая рассчитывается исходя из цикломатической сложности, количества строк метода и вычислительной сложности (Halstead Volume, чем выше общее количество операторов и операндов в коде, тем выше значение метрики).
Метрика показывает значения от 0 (наихудшее) до 100 (наилучшее).
В Visual Studio 2017 метрика отображается в окне Code Metric Results.
Необходимость качественного и грамотного написания кода — вот одна из основополагающих вещей, которым мы обучаем будущих программистов при выполнении учебных проектов.
И это не только то, чему мы учим, это еще и то, что принято у нас в компании при работе над проектами заказчиков — мы стараемся делать их так, чтобы внутреннее качество продукта (как написан код) не уступало внешнему (как отрабатывает функционал).
Многие зачастую уделяют внимание только второму — главное функционал, чтобы заказчик принял и заплатил. А после нас — хоть трава не расти — какая разница, кто и как будет поддерживать, или пытаться изменить этот код — пусть мучаются, мы же в свое время мучились с чужими макаронами.
Внутреннее качество важно не только из-за того, что мы гуманисты и думаем о том, кто этот код будет поддерживать. Еще мы честолюбивы и эгоистичны.
Итак, внутреннее качество нам обеспечивает:
Как же это внутреннее качество контролировать? Подходов есть несколько. Мы применяем все.
Причем review у нас проводят не тех лиды, а все члены команды. Здесь мы убиваем двух зайцев одним махом: проверяется качество кода, и знание кода распределяется между членами команды.
На что мы проверяем код во время code review? На соответствие стандартам, отсутствие антипаттернов (как в коде, так и в построении схемы БД), следование практик ООП/ООД, качество документации.
На проведение code review мы отдельно закладываем время, у нас есть специальный статус для задач (Open->In Progress->Ready for Code Review->In Review-> Ready for Testing...). Так что не сделать не выйдет.
Мы используем Sonar и для Java, и для .NET проектов. На что мы инспектируем код с помощью Sonar?
Покрытие кода юнит тестами (Code coverage). Да-да, мы пишем юнит тесты:
Комментарии (Comments):
Отсутствие связности внутри методов (LCOM4) — чем эта метрика больше, тем риск падения системы выше при изменении функционала. Особенно из-за тех мест, которые дают увеличение этой метрики (Sonar выдает усредненную).
Индикатор спутанности пакетов (Package tangle index) — количество циклический зависимостей между пакетами и общим числом зависимостей должно стремиться к 0.
Процент дублирования кода (Duplications) — отсутствие копипаста кода — должно стремиться к 0%
Цикломатическая сложность (Сomplexity) кода определяет сложность структуры кода: чем меньше вложенных операторов ветвления и циклов, тем лучше:
Также мы внимательны к замечаниям (violations), которые выдает Sonar — мы исправляем блокеры, критикалы и мажоры.
Sonar — настраиваемый инструмент, и есть еще множество метрик, которые можно собирать и анализировать. Иногда мы к ним прибегаем, но зачастую останавливаемся на вышеперечисленных стандартных.
Что влияет на возможность отслеживать внутренне качество?
Когда я первый раз запустил анализ на одном из проектов, все значения Maintainability Index были зеленые. Это казалось несколько странным, т.к. там явно был код, который надо было бы переписать. Значения MI для таких участков кода были около 30-40. Получается, что показатели по умолчанию являются, скорее всего, субъективными, и решение о том, какой код считать некачественным, придется принимать самим программистам.
Для своих проектов я стараюсь для большинства методов поддерживать показатель MI около 70-90. Бывают методы, у которых этот показатель равен 50-60, и их можно переписать, но стоит оценивать затраты и выгоды.
Благодаря этому инструменту можно достаточно легко провести code review большого проекта, найти места, которые необходимо переписать. Также достаточно полезно следить за процессом изменения вышеописанных метрик. Это может показать руководителю об отношении программистов к разработке того или иного проекта, а также динамику изменения качества кода по каждому программисту, что немаловажно в нашей профессии. Другой причиной слежения за метриками, являются, определенные программистами, пороговые значения, при достижении которых, необходимо заняться рефакторингом.
Заключение
Метрики умеют показывать на проблемные участки кода, но если все метрики находятся в пределах нормы, то не обязательно код является качественным. Метод GeneratePDF, с зашкаливающей цикломатической сложностью, можно разбить на три, назвав их GeneratePDF1, GeneratePDF2 и GeneratePDF3. Метрика на уровне методов придет в норму, однако решение сильно рискует быть высмеянным на код-ревью. Соблюдение метрик никак не должно являться самоцелью. Цель метрик — помочь программисту увидеть проблему, которую нужно решать переосмыслением существующего кода, поиском лучшего алгоритма и выбором более элегантного решения проблемы.
Исследование, описанное в статье про метрики кода, подчеркивает ее значимость в современном мире. Надеюсь, что теперь ты понял что такое метрики кода, метрика программного обеспечения, исходящие зависимости, входящие зависимости, цикломатическая сложность, cyclomatic complexity и для чего все это нужно, а если не понял, или есть замечания, то не стесняйся, пиши или спрашивай в комментариях, с удовольствием отвечу. Для того чтобы глубже понять настоятельно рекомендую изучить всю информацию из категории Проектирование веб сайта или программного обеспечения
Комментарии
Оставить комментарий
Проектирование веб сайта или программного обеспечения
Термины: Проектирование веб сайта или программного обеспечения