Thursday, May 27, 2010

Метрики кода

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

К настоящему времени разработано множество метрик, которые позволяют оценить код с самых разных сторон. Например:
  • по размеру – различные версии метрики «Число строк кода», «Число методов в классе», «Число классов в системе» и т.д.
  • по сложности - оценка архитектуры и алгоритмов программы (цикломатическая сложность, связность кода, глубина наследования и др.).
  • по сложности поддержки - показывают трудоемкость процесса поддержки и развития кода (метрика Холстеда).
Метрик много, и я не буду рассматривать здесь их все. Остановлюсь только на наиболее, на мой взгляд, полезных из них.

Сплоченность (cohesion) – показывает насколько тесно связана («сфокусирована») функциональность модуля. Как правило, для оценки сплоченности используются не точные числовые значения, а относительные показатели «сильная» (“high”) и «слабая» (“low”). Модули, обладающие сильной сплоченностью более предпочтительны, т.к. сильная сплоченность связана со следующими положительными качествами кода: прочность, надежность, простота повторного использования. В то же время слабая сплоченность говорит о том, что код наверняка трудно тестировать, поддерживать и повторно использовать.

Связность (coupling) – показывает насколько сильно один модуль зависит от других. Под зависимостью здесь подразумевается использование в одном модуле, типов определенных в другом модуле, для описания параметров методов, локальных переменных, возвращаемых значений и т.д. Слабая связность является признаком хорошего дизайна, т.к. означает простоту повторного использования кода. Модули с сильной связностью наоборот демонстрируют все признаки плохой архитектуры: жесткость, хрупкость, неподвижность и вязкость.

Пример расчета связности для классов.

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

Число строк кода (Lines of Code) – общий смысл того что показывает эта метрика понятен без всяких комментариев. Однако, существует много вариаций на тему. Вот, например, Visual Studio считает только исполняемые строки и основывается при этом на IL, а не на исходном коде. Для этой метрики нет рекомендуемых значений, которых обязательно нужно придерживаться. Однако если для метода или класса число строк слишком велико, то, возможно, они взвалили на себя слишком много, и их стоит отрефакторить. Всем известное правило: «простота – залог успеха», работает и для кода. Правда, понимать его нужно следующим образом: меньше кода значит меньше ошибок и проще поддержка.
Пример расчета числа строк кода.

Цикломатическая сложность (Cyclomatic сomplexity) – показывает сложность структуры кода. Вычисляется путем определения числа уникальных путей выполнения программы (подсчитывается количество операторов ветвления и циклов). Также как и с числом строк кода здесь, чем проще, тем лучше. Чем меньше путей выполнения, тем меньше надо тестов чтобы их покрыть, тем проще поддержка.
Пример расчета цикломатической сложности.

Глубина наследования (Depth of Inheritance) – Показывает сколько классов цепочке наследования отделяют данный класс от корня иерархии. Более предпочтительными являются не глубокие, а широкие иерархии типов, т.к. наследование существенно повышает связность в системе, а чем это плохо мы только что разобрали.
Пример расчета глубины наследования.

Инструменты анализа кода
Visual Studio 2008. Анализ метрик имеется только в самой дорогой Team edition. Позволяет вычислять всего 5 основных метрик и отслеживать их в режиме реального времени.

Visual Studio 2010. Как и в предыдущей версии студии, анализ метрик есть только в самых дорогих редакциях Premium и Ultimate. Количество доступных метрик, по сравнению с предыдущей версией, не изменилось.

NDepend. На сегодняшний день, наверное самый функциональный инструмент. Умеет анализировать 86 метрик. Полностью интегрируется с Visual Studio.

Reflector.CodeMetrics. Бесплатное дополнение к Reflector’у. Позволяет считать некоторые метрики (например, цикломатическую сложность) и сохранять результаты в файл. Не особо удобно, но лучше чем ничего.

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

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

Ссылки:
1.       Software metric
2.       Cohesion
3.       Coupling

Sunday, May 23, 2010

Признаки плохого дизайна

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


Жесткость (Rigidity)
Жестким считается код, в который трудно вносить даже самые простые изменения. Потому что каждое изменение влечет за собой изменения в модулях, которые зависят от изменяемого модуля. Это очень неприятная ситуация, когда изначально казавшаяся простой задача растягивается на неопределенное время из-за необходимости вносить дополнительные изменения во множестве модулей программы.

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

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

Вязкость архитектуры (Viscosity of design)
Вязкость показывает насколько сложно вносить изменения в программу, учитывая требования архитектуры. Изменения, которые не учитывают архитектуру принято называть хаками. Когда хак реализовать проще, чем изменение учитывающее архитектуру, тогда вязкость архитектуры приложения считается сильной. Т.е. проще писать "неправильный" код, чем "правильный".

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

Принципы проектирования

Если пользуетесь каким-либо инструментом, применяйте его правильно. Очень важно знать его особенности (достоинства и недостатки), знать ситуации и причины когда его стоит применять и понимать последствия его применения. Можно, конечно, и шурупы молотком забивать, но лучше все-таки оставить молоток для гвоздей. Это правило работает для любой сферы деятельности. Прежде чем приступить к любой задаче, вы должны уметь пользоваться инструментами для ее решения. И чем лучше вы овладеете ими, тем быстрее, проще и эффективнее вы сможете решить задачу.

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

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

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

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

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

Ссылки:
1. Principles of OOD
2. Введение в принципы проектирования
3. Принципы проектирования классов (S.O.L.I.D.)