使用NDepend衡量代码的SOLID程度
SOLID是面向对象的软件开发中的5条准则,也是开发人员可以提升自己代码质量的准则。那么如何衡量自己的代码是否符合SOLID准则呢?NDepend这款工具也许可以帮得上忙。本文将介绍一些NDepend的规则,这些规则可以帮助你来衡量你的代码的SOLID程度,并且提供一些可以让你的代码变得更好的建议。
原文连接:Use NDepend to Measure How SOLID Your Code Is - NDepend
作者 Carlos Schults。授权翻译,转载请保留原文链接。
NDepend和SRP
我们将首先处理SOLID中的“ S”:单一责任原则。这可能是SOLID原则中最容易被误解的东西,公平地说,这似乎是有原因的。关于什么构成责任存在一定程度的主观性。那么NDepend如何帮助解决这个难题呢?
避免类型过大
顾名思义,这条规则建议你保持较小的类型。保证你的类较小并不一定会使它们只承担一种责任,但是肯定会引导事情朝着这个方向发展。
听上去不错,但是“太大”有多大?根据规则的文档,超过200条逻辑代码行的类就属于过大了。
避免太多方法的类型
你可能会认为,鉴于前一个规则,这条规则有些多余,但事实并非如此。 除了大量方法外,类型还可能由于其他原因而过大,例如它可能有很多其他成员,或者可能只有一个巨大的方法。
但是,按照以前的规则,我们不得不问:“太多”是多少? 该规则的文档指出,如果每种类型有20个或更多的方法则被视为拥有太多方法,并补充说,诸如构造函数,属性和事件访问器之类的方法不被视为计数的一部分。
避免太多字段的类型
作为“避免太多...”系列的规则,这条规则涉及到了字段。该规则的文档说,它针对具有15个以上字段的类型,并且不考虑常量和只读静态字段以及枚举类型。
避免过大和过于复杂的方法
在这里,我们继续遵循鼓励简化设计的规则,但是现在我们关注的是方法级别。那么,方法的情况如何呢?我们是否只是要重复使用刚刚看到的相同规则,但是将“类型”替换为“方法”?
不,不是那么容易。在处理方法时,事情会变得有些复杂。首先让我们看看“避免方法过大和过于复杂”规则的文档说了些什么:
//<b> This rule matches methods where *ILNestingDepth* > 2
</b>//<b> and (*NbLinesOfCode* > 35
</b>//<b> or *CyclomaticComplexity* > 20
</b>//<b> or *ILCyclomaticComplexity* > 60)
</b>//<b> Such method is typically hard to understand and maintain.</b>
简而言之,该规则担心嵌套深度大于2且也符合其他几种条件之一的方法,例如代码行和循环复杂度。
避免使用参数过多的方法
该规则的名字已经表明了它的内容。我们真正需要做的是量化。多少是太多?
同样,我们必须求助于文档,这次文档中描述了参数过多指的是超过8个参数。调用这样的方法会十分痛苦。同样需要注意的是,一个有很多参数的方法是否也有可能本身的逻辑已经十分冗杂,处理多个事务了呢?有这样方法的类,应该使用单一职责原则来进行重构。
避免使用过多局部变量的方法
终于,最后一条用来帮助你使用单一职责原则检查自己代码的规则。就像之前介绍过的大多数规则一样,这条规则的名字也说明了它的内容。并且,这条规则只需要定义它所谓的“过多”是多少。而这个问题的答案,根据文档,是15个。
通过这一节的介绍,我们大致介绍了NDepend中可以帮助你使用SOLID原则中的单一职责原则的规则。
OCP 开闭原则
很多人认为开闭原则是所有原则中最难理解和应用的。所谓的开闭原则,指的是你的类型应该对拓展开放,但是对修改关闭。换句话说,对子类的增加一定不能导致父类的任何修改。NDEpend中的“基类不应该使用派生”规则通过指示基类永远不要提及其子类来帮助我们防止这种情况。
LSP 里氏替换原则
Robert Martin在一篇文章中对里氏替换原则下过如下定义:
派生类必须可以替代其基类。
当面对这样一条定义时,一些人会认为里氏替换原则多此一举,显得多余。那么这条原则的意义是什么呢?
好吧,这条原则的关键是人们一直在违反它!一种非常常见(也许是最常见)的情况是,让一个类实现一个或多个接口,然后抛出某种方式不适用的方法。
而NDepend恰好有一条规则可以警告你这种情况,这条规则被相当恰当地称为“实现抛出NotImplementedException的方法。”
你会发现,在使用TDD时,此异常最有用。这是一个非常常见的情况,如下所示:
- 为一段代码编写测试时,你将调用一些不存在的方法。
- 此时编译器会抱怨,而这是一件好事。
- 然后你接受了Visual Studio的建议并生成了一个方法存根,因为你现在也不希望立即实现该方法。
如果你以后忘记实现该方法,则测试将失败,而这会提醒你。
这是NotImplementedException的可接受用法。如果你使用此(或其他)异常来避免实现某个接口,那么你将破坏LSP准则。
ISP 接口隔离原则
接口隔离原则指的是:
不应强迫客户端依赖于不使用的接口。
为了遵循此原理,你应该使接口尽可能的小和精细。换句话说,你应该避免接口太大,这是我们现在要讨论的NDepend规则的确切名称。
“太大”有多大?根据NDepend的文档:10。也就是说,该规则将提示具有10种以上方法的接口。
关于此规则的一件好事是,它也可以帮助你遵守之前提到过的里氏替换原则。如果使接口尽可能小,则可以降低客户端必须提供有意义的实现的可能性。一石二鸟!
DIP 依赖反转原则
所谓的依赖反转原则指的是:抽象接口不应该依赖于具体实现。而具体实现则应该依赖于抽象接口。
假设你有一个应用程序紧密耦合使用Microsoft SQL Server作为持久性解决方案。如果之后你需要更改使用另一个RDBMS,将会发生什么?这注定是一个痛苦的过渡过程。
反过来,如果你的应用程序的业务逻辑甚至没有意识到数据库的存在(通过采用适当的体系结构是可能的),则无需进行任何更改即可进行过渡。
为了帮助您解决此问题,NDepend制定了一个规则,即“高凝聚,低耦合”。 “高凝聚,低耦合”类似于“关注点分离”,简而言之,你希望你的软件具有很高的内聚性(“做一件事情并做好”),同时彼此之间的耦合性也很低。
这条NDepend规则通过鼓励你将包含抽象的命名空间与包含具体内容的命名空间(即所述抽象的实现)分开来帮助你实现此目的。
准备好编写更多SOLID代码了吗?
我们上面介绍的规则只是NDepend提供的规则中的一部分。它们足以使您的应用程序符合SOLID原则吗?当然不会。没有任何工具或技术可以保证一定改善你的代码。
但是,虽然规则本身并不能自动使你的代码完美无缺,但它们鼓励你编写较小,集中且不太复杂的类型,从而为你提供极大的帮助。
除此之外,通过不断阅读和研究SOLID原理,适当的架构以及通用的最佳实践和模式,你便可以正确地编写出色的应用程序。
阅读更多:
https://docs.microsoft.com/en-au/learn/?WT.mc_id=DT-MVP-5001664