IntelliJ IDEA的第一版于2001年1月发布,当时它是第一个集成了高级代码导航和代码重构功能的Java IDE之一。
2009年,JetBrains开源了其社区版本 。 从那时起,创建了许多基于它的IDE,例如Google的Android Studio。
让我们使用JArchitect进入Intellij IDEA的社区版本,并发现一些内部设计选择。
1.模块化
Intellij IDEA使用许多项目进行了模块化。 主要的是“想法”。 实用程序类在“ util”项目中实现,“ openapi” jar包含开发Intellij IDEA插件所需的类型。
这是Intellij IDEA项目的列表,以及有关其类型的一些统计信息:
每个项目都包含许多软件包以对其代码库进行模块化,并且采用了按功能打包的方法。
逐功能包使用程序包来反映功能集。 它将与单个功能(仅该功能)相关的所有项目放置在单个目录/程序包中。 这导致包装具有高内聚性和高模块化性,并且包装之间的耦合最小。 紧密协作的项目彼此相邻放置。
例如,这是来自idea项目的一些软件包,这些软件包显示了按功能分组的类型。
2. Intellij IDEA开发人员广泛使用GoF设计模式
设计模式是一种软件工程概念,描述了针对软件设计中常见问题的重复解决方案。 GoF模式是最受欢迎的模式。
Intellij IDEA开发人员广泛使用GOF模式。 这是源代码中使用的一些方法。
2.1工厂
使用工厂来隔离实例化逻辑并增强内聚力很有趣。 这是源代码中定义的工厂列表:
实施了许多工厂; 这里有一些是从TextEditorHighlihtingPassFactory继承的。
2.2转接器
适配器模式充当两个不兼容接口之间的桥梁。 这种设计模式属于结构模式,因为该模式结合了两个独立接口的功能。
Intellij IDEA源代码中实现了许多适配器:
2.3装饰器
装饰器模式可用于扩展(装饰)某个对象的功能,而无需更改其结构。 在Intellij IDEA中实现了许多装饰器。
2.4代理
在最一般的形式上,代理是一个类,它充当与其他对象的接口。
例如,这是通过FieldBreakpoint和FrameVariablesTree类使用两个代理VirtualMachineProxy和StackFrameProxy的。 使用VirtualMachineProxy接口代替实现。 但是,与FrameVariablesTree耦合的StackFrameProxyImpl并非如此。 也许可以通过重构来消除这种依赖性。
2.5门面
外观模式隐藏了系统的复杂性,并提供了到客户端的接口,客户端可以使用该接口访问系统。 这是在Intellij IDEA中实现的CodeStyle外观的示例。
2.6访客
访客设计模式是一种将算法与操作对象的结构分离的方法。
突出显示功能是使用访问者模式实现的。
2.7策略
在某些情况下,类仅在行为上有所不同。 在这些情况下,最好将算法隔离在单独的类中,以便能够在运行时选择不同的算法。
许多类在Intellij IDEA源代码中实现策略模式:
2.8建造者
这种模式允许客户对象构造一个复杂的对象。 ConrtolFlowBuilder是Intellij IDEA源代码中实现的构建器之一。
这是ControlFlowBuilder.build方法调用的方法:
3.联轴器
低耦合是理想的,因为在一个应用程序区域中进行更改将需要在整个应用程序中进行较少的更改。 从长远来看,这可以减轻与修改和向应用程序添加新功能相关的大量时间,精力和成本。
这是使用接口带来的三个主要好处:
- 接口提供了一种定义可促进重用的合同的方法。 如果一个对象实现一个接口,则该对象将符合标准。 使用另一个对象的对象称为使用者。 接口是对象与其使用者之间的契约。
- 接口还提供一定程度的抽象,使程序更易于理解。 接口使开发人员可以开始讨论代码行为的一般方式,而不必深入探讨许多具体细节。
- 接口强制组件之间的耦合度很低,这很容易保护接口使用者免受实现接口的类中任何实现更改的影响。
Intellij IDEA中定义了许多接口和抽象类,以强制实现低耦合:
这是蓝色的在源代码的“衡量指标视图”中这些类型的分布。
在“度量标准视图”中,代码库通过树形图表示。 Treemapping是一种使用嵌套矩形显示树状结构数据的方法。 使用的树结构是通常的代码层次结构:
- 项目包含软件包。
- 包中包含类型。
- 类型包含方法和字段。
树状图视图提供了一种有用的方式来表示CQLinq请求的结果。 蓝色矩形代表此结果,因此我们可以直观地看到请求所涉及的类型。
如我们所见,几乎在所有包中都定义了接口和抽象类,这对于将包提供的功能作为合同表示很有用。
4.凝聚力
单一责任原则规定,一个阶级改变的理由不应该超过一个。 这类课程据说具有凝聚力。 LCOM值较高通常会指出内聚性较差的类别。 有几个LCOM指标。 LCOM的取值范围为[0-1]。 LCOM HS(HS代表Henderson-Sellers)的值在[0-2]范围内。 高于1的LCOM HS值应视为警报。 以下是计算LCOM指标的方法:
LCOM = 1 – (sum(MF)/M*F)
LCOM HS = (M – sum(MF)/F)(M-1)
哪里:
- M是类中方法的数量(包括静态方法和实例方法,均包括构造函数,属性获取器/设置器,事件添加/删除方法)。
- F是类中实例字段的数量。
- MF是类访问特定实例字段的方法的数量。
- Sum(MF)是该类所有实例字段上MF的总和。
这些公式背后的基本思想可以表述为:如果一个类的所有方法都使用其所有实例字段,则该类是完全内聚的,这意味着sum(MF)= M * F然后LCOM = 0且LCOMHS = 0。
高于1的LCOMHS值应视为警报。
只有极少数的类型可以被认为是没有凝聚力的。
5.多线程和并发
为了使Intellij IDEA更具反应性,创建了许多线程,从而改善了用户体验。
让我们搜索直接或间接启动线程的所有方法:
并发逻辑被隔离在以下软件包中:
为了促进并发开发,使用了JSR166 。
以下是jsr166 jar使用的所有类型的列表:
6.抽象与不稳定性图
该图背后的想法是,程序的代码元素越受大众欢迎,它应该越抽象。 换句话说,就是避免过多地直接依赖于实现,而要依赖于抽象。 流行的代码元素是指一个项目(但该想法也适用于包和类型),该项目被该程序的其他项目大量使用。
在您的代码库中使用非常流行的具体类型不是一个好主意。 这会引起程序中的某些痛苦区域,在其中更改实现可能会影响程序的很大一部分。 并且已知实现比抽象更容易演化。
下图中的主序列线(虚线)显示了如何平衡抽象性和不稳定性。 稳定的组件将位于左侧。 如果检查主序列,您会发现这样的组件应该非常抽象才能接近所需的线–另一方面,如果其抽象程度较低,则将其放置在称为“区域”的区域中痛”。
只有util处于痛苦区域,这并不是真正的问题。 实际上,总的来说,实用程序库提供的实用程序类别比接口定义的功能更多。
7.开放的API和插件系统
插件的使用使您可以扩展Intellij IDEA。 提供“ openapi”罐来实现此目标。
openapi jar提供了许多接口,这些接口代表了我们可以使用并从插件扩展的所有功能。
Intellij IDEA插件包含一个或多个动作。 如以下CQLinq查询所示,源代码中实现了成千上万的操作:
探索现有的已实施操作可以帮助开发人员轻松开发其自定义插件。
8.使用缓存提高性能
使用缓存是优化应用程序的一种流行方法。 Intellij IDEA使用两个缓存管理器:
FindInProjectTask使用CacheManager接口搜索单词。
这是FindInProjectTask.getFilesForFastWordSearch方法调用的所有方法的列表:
9.使用的外部库
Intellij IDEA使用许多外部jar,以下是所有使用过的jar的列表:
使用外部库时,最好检查一下我们是否可以在不影响整个应用程序的情况下轻松地将第三方库换成另一个库。 有许多原因可以鼓励我们更改第三方库。 另一个库可以:
- 具有更多功能。
- 更加强大。
- 更加安全。
让我们发现某些外部库是否高度耦合。
摇摆:
Swing实现了一组用于构建图形用户界面(GUI)并向Java应用程序添加丰富的图形功能和交互性的组件。 Swing组件完全用Java编程语言实现。 可插入的外观使您可以创建在各个平台上看起来相同或可以采用当前OS平台(例如Microsoft Windows,Solaris™或Linux)外观的GUI。
让我们使用直接摆动组件搜索所有类型:
许多类型都直接使用摆动组件,如下面的树图所示,这些树以蓝色显示。
通过另一个Gui框架来改变摆动并不容易。 即使Swing引起争议,Intellij IDEA惊人的GUI证明Swing是Gui需求的不错选择。
净值:
Netty是一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。
这是使用此库的所有类型的列表:
只有少数几种类型直接使用它,如果我们想用另一个库进行更改,这将非常有用。
ASM:
ASM是一个非常小且非常快的Java字节码操作框架。 它变得非常流行,许多工具都在使用它。 我们还在工具JArchitect中使用它来分析字节码。
这是直接使用ASM的所有类型的列表:
作为Netty,ASM的使用被隔离在某些软件包中,我们可以轻松对其进行更改。
除了Swing以外,几乎所有其他外部jar都不与Intellij IDEA高度耦合。
10.统计
最常用的类型
了解项目中最常用的类型很有趣。 实际上,这些类型必须经过精心设计,实施和测试。 它们发生的任何变化都可能影响整个项目。
我们可以使用TypesUsingMe指标找到它们:
但是,还有一个有趣的度量标准可以搜索流行类型:TypeRank。
通过将Google PageRank算法应用于类型的依存关系图来计算TypeRank值。 应用中心乘以0.15使其成为TypeRank的平均值为1。
具有较高TypeRank的类型应进行更仔细的测试,因为此类错误可能会带来更大的灾难性后果。
这是根据TypeRank指标得出的所有流行类型的结果:
使用此度量标准,PsiElement成为了最常用的类型,而不是Project接口。
10.2最常用的方法:
10.3方法调用许多其他方法
知道使用许多其他方法的方法很有趣。 这些方法可能会揭示设计问题。 在某些情况下,需要进行重构以使其更具可读性和可维护性。
摘要
Intellij IDEA的设计和实现非常好,使用了许多模式,并实现了许多最佳实践。 探索其源代码是学习如何设计和实现应用程序的实用方法。 这比只阅读网络上的书籍和文章来提高您的设计技能要好。
JArchitect为 所有开源Java贡献者 提供了 专业许可 。 分析他们的代码库可能很有用。 因此,如果您想尝试一下,请 在此处 查看更多详细信息。
翻译自: https://www.javacodegeeks.com/2015/03/intellij-idea-internal-design.html