在当今世界,开发一个具有庞大代码库的真实世界应用程序时,需要考虑许多属性,如可维护性、可理解性、清晰度、依赖性、产品优越性等。付出很多努力来维护代码,使代码具有良好的注释,以便未来的开发者能更好地理解代码;寻找两个或更多程序集之间的依赖关系,并尝试最小化依赖性,限制代码行数等等,最后在发布产品之前进行测试,以便将来使用。在这个过程中,失去了很多本可以用来进行更好编码的时间。
如果一切都能在不思考任何事情的情况下完成,会怎样呢?是的,NDepend允许处理大型项目,以获得任何时刻的总结。因此,不管程序集有多大,程序集中有多少类型,项目中有多少行代码;可以轻松地确定代码质量,以及应该查看和重构代码的部分。
Patrick Smacchia,一位C# MVP,于2007年引入了NDepend。NDepend的主要动机是允许架构师清楚地了解对象之间的交叉依赖关系,它们之间的关联程度,并轻松找到代码的状况。将讨论如何在应用程序中使用NDepend来轻松管理代码。
许多人会同意,避免组件之间的依赖循环是良好软件设计的首要原则。如果组件A依赖于B,B依赖于C,C又依赖于A,那么组件A不能在没有B和C的情况下独立测试或开发。因此,A、B和C形成了一个不可分割的单元。这个不可分割的单元在代码可维护性方面比单独单元的成本要高。建议说,维护1000行代码可能需要三到四倍的努力,而不是维护两个独立的500 LOC块所需的努力。因此,对于任何合理的架构,一个人接受每个组件是500-1000 LOC,并且它们之间没有依赖循环。
依赖结构矩阵用于了解一个命名空间在多大程度上依赖于另一个命名空间。控制台帮助了解一个类与另一个类之间的依赖关系。让看看下面的图片:
蓝色单元格显示了x轴和y轴上两个命名空间之间的依赖关系。例如,左下角的单元格(0,12)显示命名空间Spring.Proxy间接使用命名空间Spring.Threading,最短路径长度为五。最短路径意味着项目不是直接依赖的,而是依赖于反过来依赖于另一个的程序集,最后依赖于它。
黑色单元格显示两个命名空间之间存在依赖循环。例如,单元格对[9,7]和[7,9]揭示了命名空间Spring.Validation和Spring.Core.*属于最短长度为五的依赖循环。
围绕黑色单元格的红色框表明每个组件都直接或间接地依赖于彼此,因此创建了一个超级组件,它不能独立运行。
可以重构代码结构,以消除代码之间的依赖。可以从这里阅读更多关于依赖循环的信息。
NDepend为提供的另一个有用且独特的功能是CQL。它允许编写.NET对象的查询。这对于处理大量的代码库非常有用。可能会使用简单的SQL查询来找到想要的需求。例如,假设想要获取所有超过200行的方法(总是希望单个方法的行数少于200行,以获得更好的代码结构)。可能会使用简单的查询,如:
SELECT METHODS OUT OF NAMESPACES "MyApp.MyNamespace1", "MyApp.MyNamespace2" WHERE NbILInstructions > 200 ORDER BY NbILInstructions DESC
因此,它将在MyNamespace1和MyNamespace2中搜索,尝试找到超过200 LOC的方法,并按LOC的顺序在控制台中列出。
如果想要获取实现IDisposable的类型,可以写:
SELECT TYPES WHERE IsClass AND Implements "System.IDisposable"
此外,CQL支持内置功能,将规则集成到.NET应用程序中。
在所有这些初步讨论之后,现在是时候获得一些关于如何使用NDepend的实际知识了。将讨论NDepend控制台提供的其他UI元素。
1. 比较相同程序集的不同构建版本
NDepend的独特功能是提供两个构建之间所有更改的快速概览。NDepend提供了一个优越的UI来比较两个构建。可以轻松地使用CQL来查看哪些方法发生了变化,哪些被移除,哪些发生了变化等等。让举一个快速的例子:
在上面的图中,可以看到如何开始比较程序集之间的构建。启动Visual NDepend。选择两个版本的代码库。在出现的对话框中,选择想要比较的两个程序集。也可以选择彼此之间有依赖关系的多个程序集。然后点击OK。
在初始构建过程之后,它打开了比较控制台,如下图所示。在图中,可以看到4个部分:
类浏览器:在这个部分,控制台显示了类结构的层次结构,以确定程序集中的变化。正如看到的,它清楚地划分了每个命名空间。删除线命名空间表示在旧版本中存在,但在新版本中被移除;下划线表示在新版本中修改;粗体节点是新添加的。因此,从这个树中可以清楚地了解两个版本之间发生了什么变化。
指标:它表示两个程序集中定义的所有方法的指标。如果将鼠标悬停在这个部分的任何地方,它会告诉行数和方法的名称,如下图所示。
信息:这个部分显示了当前选定项目的摘要,例如IL指令的数量、注释行数、行数、循环复杂度等。
CQL查询部分:这个部分允许定义自定义CQL查询以过滤输出。可以定义CQL,如:
SELECT NAMESPACES FROM ASSEMBLIES "XXXX.AssetTracker.Data" WHERE NbLinesOfCode > 100
它将列出程序集XXXX.AssetTracker.Data下所有行数超过100的命名空间。
这4个部分可以用来获取两个程序集之间所有更改的完整知识。
除了自己输入查询之外,还可以通过右键单击命名空间并选择“what was changed?”来找到最合适的查询,如下图所示。类型将在CQL查询结果中列出。
在比较代码库之后,让分析代码。要分析,请选择Visual Ndepend控制台初始屏幕的“分析一组.NET程序集”。在出现的对话框中,选择想要分析的dll,然后点击OK。该过程将构建程序集,分析它,并为生成一个报告。
如果查看报告,可以清楚地了解那些dll中做了什么,依赖矩阵等。让看看下面的图片:
SELECT TYPES FROM ASSEMBLIES "XXX.Framework" WHERE IsPublic
这里CQL返回了所有在程序集中公开的命名空间的输出。列表显示有60种这样的类型,每种类型都定义了行数。下面的面板给出了搜索统计的分析。
除了所有这些功能之外,NDepend还允许与Visual Studio集成。从控制台点击“安装”以打开这个窗口:
对话框将NDepend插件安装到Visual Studio。它还允许将Reflector安装到Visual Studio。对话框中出现的按钮将帮助完成这个操作。现在,从Visual Studio IDE中,可以访问CQL查询,并轻松检查依赖矩阵。任何时候,只需右键单击代码,就会发现NDepend上下文菜单选项。
因此,也可以轻松地直接从代码中访问CQL查询。这为提供了额外的好处,以便在编写代码时更好地掌握代码。NDepend还允许直接在代码中编写约束。它通常使用XML文件来存储这个约束。如果将NDepend.CQL.dll添加到项目中,可能会添加:
NDepend.CQL.CQLConstraint("WARN IF Count>0 SELECT METHODS WHERE NbLinesOfCode > 400")
它直接警告,当代码编译时没有遵循约束。