Статьи

Внутренний дизайн IntelliJ IDEA

Первая версия IntelliJ IDEA была выпущена в январе 2001 года, и в то время это была одна из первых доступных Java IDE с интегрированными возможностями навигации по коду и рефакторинга кода.

В 2009 году JetBrains с открытым исходным кодом выпустила свою версию сообщества . И с тех пор было создано много IDE на его основе, например Android Studio от Google.

Давайте войдем в версию Intellij IDEA для сообщества с использованием JArchitect и обнаружим некоторые внутренние варианты дизайна.

1. Модульность

Intellij IDEA является модульной с использованием многих проектов; главная из них — «идея». Служебные классы реализованы в проекте «util», а jar «openapi» содержит типы, необходимые для разработки плагинов Intellij IDEA.

Вот список проектов Intellij IDEA и некоторые статистические данные об их типах:

image001

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

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

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

image002

2. Разработчики Intellij IDEA широко используют шаблоны проектирования GoF

Шаблоны проектирования — это концепция разработки программного обеспечения, описывающая повторяющиеся решения общих проблем в разработке программного обеспечения. Шаблоны GoF являются самыми популярными.

Разработчики Intellij IDEA широко используют шаблоны GOF; Вот некоторые из них, используемые в исходном коде.

2.1 Фабрика

Использование фабрики интересно, чтобы изолировать логику реализации и обеспечить сплоченность; вот список фабрик, определенных в исходном коде:

image003

Многие заводы реализованы; Вот некоторые из них, унаследованные от TextEditorHighlihtingPassFactory.

image004

2.2 Адаптер

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

Многие адаптеры реализованы в исходном коде Intellij IDEA:

image005

2.3 Декоратор

Шаблон декоратора может использоваться для расширения (декорирования) функциональности определенного объекта без изменения его структуры. Многие декораторы реализованы в Intellij IDEA.

image006

2.4 Прокси

Прокси, в его наиболее общем виде, является классом, функционирующим как интерфейс к чему-то другому.

Вот, например, использование двух прокси VirtualMachineProxy и StackFrameProxy классами FieldBreakpoint и FrameVariablesTree. Интерфейс VirtualMachineProxy используется вместо реализации. Однако это не относится к StackFrameProxyImpl, который связан с FrameVariablesTree. Возможно, рефакторинг для удаления этой зависимости подходит.

image007

2.5 Фасад

Шаблон фасада скрывает сложности системы и предоставляет клиенту интерфейс, с помощью которого клиент может получить доступ к системе. Вот пример фасада CodeStyle, реализованного в Intellij IDEA.

image008

2.6 Посетитель

Шаблон дизайна посетителя — это способ отделить алгоритм от структуры объекта, с которой он работает.

Функция выделения реализована с использованием шаблона посетителя.

image009

2.7 Стратегия

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

Многие классы реализуют шаблон стратегии в исходном коде Intellij IDEA:

image010

2.8 Строитель

Этот шаблон позволяет клиентскому объекту создавать сложный объект; ConrtolFlowBuilder является одним из конструкторов, реализованных в исходном коде Intellij IDEA.

Вот методы, вызываемые методом ControlFlowBuilder.build:

image011

3. Сцепление

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

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

Многие интерфейсы и абстрактные классы определены в Intellij IDEA для обеспечения низкой связи:

image012

А вот синим цветом распределение в Metric View этих типов в исходном коде.

image013

В представлении метрик база кода представлена ​​посредством древовидной карты. Treemapping — это метод отображения древовидных данных с использованием вложенных прямоугольников. Используемая древовидная структура — это обычная иерархия кода:

  • Проект содержит пакеты.
  • Пакет содержит типы.
  • Тип содержит методы и поля.

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

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

4. Сплоченность

Принцип единой ответственности гласит, что у класса не должно быть более одной причины для изменения. Такой класс называется сплоченным. Высокое значение LCOM обычно указывает на плохо сцепленный класс. Есть несколько показателей LCOM. LCOM принимает свои значения в диапазоне [0-1]. LCOM HS (HS означает «Хендерсон-Селлерс») принимает свои значения в диапазоне [0-2]. Значение LCOM HS, превышающее 1, следует считать тревожным. Вот для вычисления метрик LCOM:

1
2
LCOM = 1 – (sum(MF)/M*F)
LCOM HS = (M – sum(MF)/F)(M-1)

Куда:

  • M — число методов в классе (учитываются как статические, так и методы экземпляров, в него входят также конструкторы, методы получения / установки свойств, методы добавления / удаления событий).
  • F — количество полей экземпляра в классе.
  • MF — это число методов класса, обращающихся к конкретному полю экземпляра.
  • Sum (MF) — это сумма MF для всех полей экземпляра класса.

Основная идея, лежащая в основе этих формул, может быть сформулирована следующим образом: класс является полностью связным, если все его методы используют все его поля экземпляра, что означает, что сумма (MF) = M * F, а затем LCOM = 0 и LCOMHS = 0.

Значение LCOMHS выше 1 следует считать тревожным.

image014

Только очень немногие типы могут считаться несвязными.

5. Многопоточность и параллелизм

Чтобы сделать Intellij IDEA более реактивным, создано много потоков, которые улучшают пользовательский опыт.

Давайте найдем все методы, начинающие прямо или косвенно потоки:

image015

Логика параллелизма изолирована в следующих пакетах:

image016

И для облегчения развития параллелизма используется JSR166 .

Вот список всех типов, используемых из jsr166 jar:

image017

6. График зависимости от неустойчивости

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

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

Главная линия последовательности (пунктирная) на диаграмме ниже показывает, как абстрактность и нестабильность должны быть сбалансированы Стабильный компонент будет расположен слева. Если вы проверите основную последовательность, вы увидите, что такой компонент должен быть очень абстрактным, чтобы быть рядом с желаемой линией — с другой стороны, если его степень абстракции низкая, он располагается в области, которая называется «зоной боль».

image018

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

7. Открытый API и система плагинов

Использование плагинов позволяет расширить Intellij IDEA. Для достижения этой цели предусмотрена банка «openapi».

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

image019

Плагин Intellij IDEA содержит одно или несколько действий; многие тысячи действий реализованы в исходном коде, как показано в следующем CQLinq-запросе:

image020

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

8. Улучшите производительность, используя кеш

Использование кэша является популярным способом оптимизации вашего приложения. Intellij IDEA использует два менеджера кеша:

image021

Интерфейс CacheManager используется FindInProjectTask для поиска слов.

Вот список всех методов, вызываемых методом FindInProjectTask.getFilesForFastWordSearch:

image022

9. Используются внешние библиотеки

Intellij IDEA использует много внешних jar-файлов, вот список всех используемых jar-файлов:

image023 image024

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

  • Есть больше возможностей.
  • Будь более могущественным.
  • Будьте более в безопасности.

Давайте выясним, сильно ли связаны некоторые внешние библиотеки.

Качели:

Swing реализует набор компонентов для создания графических пользовательских интерфейсов (GUI) и добавления многофункциональных графических функций и интерактивности в приложения Java. Компоненты Swing полностью реализованы на языке программирования Java. Внешний вид подключаемого модуля позволяет создавать графические интерфейсы, которые могут выглядеть одинаково на разных платформах или воспринимать внешний вид текущей платформы ОС (например, Microsoft Windows, Solaris ™ или Linux).

Давайте найдем все типы, используя непосредственно компоненты Swing:

image025

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

image026

Нелегко поменять свинг другим фреймворком Gui. И даже если Swing является предметом споров, удивительный графический интерфейс Intellij IDEA доказывает, что Swing является хорошим выбором для нужд графического интерфейса.

Нетти:

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

Вот список всех типов, использующих эту библиотеку:

image027

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

КАК М:

ASM — это очень маленькая и очень быстрая среда манипулирования байт-кодом Java. Он становится очень популярным, его используют многие инструменты. Мы также использовали его в нашем инструменте JArchitect для анализа байт-кода.

Вот список всех типов, использующих ASM напрямую:

image028

Как и у Netty, использование ASM в некоторых пакетах изолированно, и мы можем легко его изменить.

За исключением Swing почти все другие внешние банки не сильно связаны с Intellij IDEA.

10. Статистика

10.1. Наиболее используемый тип

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

Мы можем найти их, используя метрику TypesUsingMe :

image029

Однако есть еще один интересный показатель для поиска популярных типов: TypeRank.

Значения TypeRank вычисляются путем применения алгоритма Google PageRank к графику зависимостей типов. Применяется гомотетия центра 0,15, чтобы среднее значение TypeRank равнялось 1.

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

Вот результат всех популярных типов в соответствии с метрикой TypeRank:

image030

Благодаря этой метрике PsiElement стал наиболее используемым типом вместо интерфейса проекта.

10.2 Наиболее используемые методы:

image031

10.3 Методы, вызывающие многие другие методы

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

image032

Резюме

Intellij IDEA очень хорошо спроектирован и реализован, используется много шаблонов и много лучших практик. Изучение его исходного кода — это прагматичный способ научиться проектировать и реализовывать ваше приложение. Это лучше, чем читать только книги и статьи из Интернета, чтобы улучшить свои дизайнерские навыки.

JArchitect предоставляет профессиональную лицензию всем разработчикам Java с открытым исходным кодом. Может быть полезно проанализировать их кодовую базу. Итак, если вы хотите попробовать, проверьте здесь для более подробной информации.