Июль 2015

ТОМ 30 ВЫПУСК 7

Visual Studio 2015 - Анализ архитектуры с помощью Code Map в Visual Studio 2015

Стюарт Кент

Продукты и технологии:

Visual Studio 2015, Microsoft .NET Framework, Code Map

В статье рассматриваются:

новые возможности Code Map в Visual Studio 2015;

применение карт кода (code maps) для понимания общей архитектуры приложения;

анализ зависимостей;

анализ влияния потенциального изменения.

Улучшение архитектуры вашего приложения крайне важно для предотвращения накопления технического долга (technical debt) и для поддержания хорошей скорости кодирования. Более того, важно иметь возможность быстро понять влияние потенциального изменения в коде, чтобы решать, имеет ли смысл это изменение, и, если да, во что оно примерно обойдется. Обе цели требуют понимания архитектуры приложения и анализа зависимостей. Чтобы добиться первой цели, вы обычно работаете сверху вниз, постепенно углубляясь в детали. Для достижения второй цели вы, как правило, начинаете работу от конкретного элемента кода и движетесь с расширением вверх.

В этой статье мы покажем, как Code Map, получившую новый функционал в Visual Studio 2015 Enterprise, можно использовать, чтобы:

  • понять общую архитектуру .NET-приложения;
  • анализировать зависимости, выявляемые в этой архитектуре по мере постепенного углубления в детали;
  • понять и проанализировать влияние предлагаемого изменения в коде, создав карту зависимостей от конкретного элемента кода.

Улучшение архитектуры вашего приложения крайне важно для предотвращения накопления технического долга и для поддержания хорошей скорости кодирования.

Мы использовали два примера для иллюстрации. Для анализа архитектуры сверху вниз с последующим углублением в детали мы взяли кодовую базу «Roslyn» (.NET Compiler Platform). Ее выбрали по двум причинам. Во-первых, она большая, поэтому понять архитектуру в целом не так-то просто. (По сути, она настолько велика, что вам придется подождать несколько минут при создании начальной схемы, пока будут выполнены начальное построение и индексация. На решениях меньшего объема производительность гораздо больше.)

Во-вторых, Roslyn имеет открытый исходный код, поэтому вы сами сможете легко опробовать все, что мы будем здесь делать. Чтобы проанализировать влияние предлагаемого изменения, мы использовали экспериментальный прототип от Microsoft для визуализации журнала незавершенных заданий (backlog) в Team Foundation Server (TFS). Он отсутствует в общем доступе, но мы также идентифицируем элемент кода в кодовой базе Roslyn, которую вы тоже можете исследовать, хотя этот вариант будет не столь наглядным.

Мы применяли RTM-версию Visual Studio 2015 Enterprise, которую можно скачать по ссылке bit.ly/1JbLA4S.

Вникаем в общую архитектуру

Нашей отправной точкой было решение Roslyn, открытое в Visual Studio и уже скомпилированное. Для этого мы клонировали содержимое репозитария Git по ссылке https://github.com/dotnet/roslyn, используя средства клонирования Git на вкладке Connect окна Team Explorer в Visual Studio, а затем открыли решение RoslynLight.sln, которое автоматически обнаруживается при открытии клонированного репозитария. Затем мы запустили скрипт src\.nuget\NuGetRestore.ps1 из командной строки Developer для Visual Studio 2015, чтобы восстановить NuGet-пакеты, необходимые для сборки решения. (Выполнение скриптов Windows PowerShell по умолчанию отключено. Включение выполнения скриптов требует повышенных прав в командной строке. Если вы не знакомы с Windows PowerShell, вам, возможно, будет легче запустить скрипт, щелкнув его правой кнопкой мыши и выбрав Run with PowerShell из контекстного меню в Windows Explorer.)

Затем мы добавили в решение NuGet-пакет xunit.runner.visualstudio, чтобы решение Roslyn, которое является тестами xUnit, распознавалось Visual Studio. Наконец, мы скомпилировали решение (это может занять некоторое время). Чтобы сразу же получить представление об архитектуре этой кодовой базы, мы выбрали команду Generate Code Map for Solution without Building из меню Architecture. Это создает карту, которая показывает граф структуры решения и ссылок проекта. В частности, просмотр ссылок помогает понять, как связаны между собой различные части решения; вы видите их иначе, чем при обычном просмотре решения, при котором вам пришлось бы долго копаться в ссылках проекта, чтобы составить представление о них.

Как только все двоичные файлы проиндексированы и собран индекс кода (по сути, база данных SQL, в которой хранится полная логическая структура кода), карта преобразуется в то, что показано на рис. 1. Новая карта отображает гораздо больше зависимостей с цветовой кодировкой согласно Legend (щелкните кнопку-переключатель на панели инструментов Code Map для просмотра); толщина линий отражает степень зависимости между связанными компонентами. Например, зеленая линия указывает наследование (и, возможно, другие зависимости) между классами в связанных проектах. Более темно-зеленые узлы представляют проекты тестов.

Карта архитектуры после индексации
Рис. 1. Карта архитектуры после индексации

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

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

Например, глядя на корни связей Inherits From и Implements, вы получаете представление о концептуальных абстракциях в инфраструктуре. Вы видите, что инфраструктура Compilers является базовой для всего остального. Внутри инфраструктуры компиляторов Core является базовым уровнем, хотя в Core присутствуют и некоторые классы, производные от уровней CSharp и VisualBasic, что, возможно, указывает на то, что расположение уровней не совсем правильно. (В следующем разделе мы поясним, как исследовать эту проблему глубже.)

Вы можете приближать нужные области схемы для дальнейшего исследования и даже выбирать узел или узлы (Ctrl+щелчок позволяет выбирать больше одного узла), чтобы изучить их на другой схеме, используя команду New Graph from Selection контекстного меню. Так, рис. 2 иллюстрирует результат подобной операции применительно к подгруппам Core и CSharp в части Compilers всей инфраструктуры. Применение к результату раскладки «снизу вверх» приводит к размещению базовых типов сверху.

Приближение иерархии наследованияy
Рис. 2. Приближение иерархии наследования

Анализ зависимости

Как вы четко видели на рис. 2, Core наследует некоторые классы от CSharp и VisualBasic, что несколько удивительно. Давайте выясним, что вызывает эту аномалию. Мы начали с выделения линии зависимости (dependency link) между Core и CSharp и выбора Show Contributing Links on New Code Map. Результат показан на рис. 3.

Результат изучения связи между Core и CSharp
Рис. 3. Результат изучения связи между Core и CSharp

Мы обнаружили один класс-виновник и были вынуждены, действительно ли проект/сборка VBCSCompiler.exe должна быть частью группы Core. Возможно, нам требуется переработать структуру Solution Folder, чтобы исключить этот проект. Но, прежде чем вносить это изменение, мы использовали Code Map, чтобы проанализировать потенциальное влияние этого изменения. Вернувшись к карте, приведенной на рис. 2, мы сбросили все фильтры связей, чтобы увидеть все сборки, и отредактировали схему, переместив узел VBCSCompiler.exe из Core в CSharp, чтобы увидеть влияние этого на зависимости (рис. 4). Это сразу же прояснило ситуацию, хотя потом мы нашли, что Roslyn.Compilers.CompilerServer.UnitTests.dll тоже находится в неправильном месте. Исследование можно продолжать и далее в таком ключе. Вы можете даже создать узлы новых групп в схеме, чтобы получить более четкую архитектуру, которую потом можно использовать для получения информации о последствиях рефакторинга папок решения, а также более глубокого рефакторинга вроде разделения классов или интерфейсов между сборками.

Перемещение узлов для анализа влияния потенциального рефакторинга на зависимости
Рис. 4. Перемещение узлов для анализа влияния потенциального рефакторинга на зависимости

Анализ влияния предложенного изменения

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

В нашем примере показывается код из экспериментального прототипа от Microsoft, который применяется для визуализации и внесения изменений в журнал незавершенных заданий в Visual Studio Online или TFS, используя иерархические «доски» по системе Канбан (hierarchical Kanban boards). В этом примере мы хотели добавить возможность изменять название рабочего элемента непосредственно через карту, что требует добавления нового перечислимого литерала в перечисление ChangedProperty. (Если вы хотите опробовать это с помощью Enum в кодовой базе Roslyn, то используйте Microsoft.CodeAnalysis.LocationKind.) Но сначала нам нужно было изучить влияние этого изменения, поэтому мы щелкнули индикатор CodeLens ссылки, а затем выбрали связь Show on Code Map.

Code Map позволяет эффективно выполнять анализ.

Результат показан на рис. 5, где представлены все методы и другие члены классов, которые так или иначе ссылаются на перечисление ChangedProperty. Эта схема демонстрирует члены в контексте архитектуры, т. е. они группируются по классу, пространству имен, сборке и папке решения. Затем мы использовали фильтры для исключения Solution Folders и Assemblies и сменили раскладку (Layout) на формат «слева направо».

TКарта зависимости ссылок после скрытия папок решения и сборок и выбора разметки «слева направо»
Рис. 5. Карта зависимости ссылок после скрытия папок решения и сборок и выбора разметки «слева направо»

Теперь эта карта позволяет исследовать все области кода, которые могут быть затронуты изменением. Вы можете использовать карту не только для навигации по коду (двойной щелчок узла позволяет перейти в соответствующее место в коде), но и для выявления шаблонов в дизайне. На рис. 5 сразу же видно, что метод SortAndReparent появляется в классах пространства имен StoryMaps.ViewModel, а быстрый анализ кода любого из этих классов указывает, что никаких изменений в коде не потребуется. Однако, взглянув на метод Process в классе WorkItemsWriter, можно увидеть, что он вызывает подпроцесс — по одному на каждый литерал в ChangedProperty. Добавление нового литерала скорее всего потребует нового метода подпроцесса. Изучение кода подтверждает это подозрение.

По мере прохождения этого процесса можно делать узлы зелеными, красными или какого-то другого цвета, указывая, нужен ли контроль за исполнением (follow-up) (в контекстном меню откройте подменю Edit и выберите Flag for Follow-up), а также добавить более подробные комментарии на схему (рис. 6).

Аннотированная карта зависимостей ссылок
Рис. 6. Аннотированная карта зависимостей ссылок

Заключение

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

В этой статье мы показали, как использовать Code Map (возможности которой в Visual Studio 2015 расширены), чтобы понять и проанализировать общую архитектуру .NET-приложения, а также изучить зависимости на высоком уровне. К новым возможностям относятся:

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

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

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


Стюарт Кент (Stuart Kent) — менеджер программ группы в Microsoft, отвечающий за удобство использования разработчиками Visual Studio и Visual Studio Online. Особое внимание уделяет контролю технического долга (technical debt), совместному использованию кода и коллективной работе. Применяет средства анализа архитектур, CodeLens и поиск кода.

Жан-Марк Приер (Jean-Marc Prieur) — старший менеджер программ в Microsoft. Планирует и руководит обеспечением удобства использования Visual Studio и Visual Studio Online, уделяя особое внимание контролю технического долга, включая средства анализа архитектуры.

Блэр Макглашан (Blair McGlashan) — технический руководитель группы в Кембридже (Великобритания), обеспечивает удобство использования, уделяя особое внимание experiences контролю технического долга, включая средства анализа архитектуры.