软件自身是一种固化的思维,因此从本质上来看,软件是不可度量的。
但这并不意味着软件不需要度量,而只是说软件中的度量大多都有一定限度。
应用各种度量数据的时候一旦跨过这种限度,结果就会适得其反。
在这篇文章里,我们将考查一下现有的,对软件进行度量的方法(注意:这篇里主要考察别人的方法,不是我自己的)。
可能不全面,不足的地方欢迎大家进行补充。
对软件“直观可见的质量属性”的度量比较简单,比如:Bug率,性能等,这里就不提了。
这里主要关注的是软件的内在的,不直观可见的质量属性。
圈复杂度主要用于度量函数或方法,从《代码大全》中可以找到圈复杂度的描述。
关于圈复杂度:Tom McCabe曾经建议使用下面的方法来度量复杂度。在这一方法中为了计算复杂度首先要计算子程序中的决策点(decision points),规则如下:
很多静态分析工具都直接提供对圈复杂度的度量,而圈复杂度本身歧义性很小,是非常难得的指标,高于15的代码基本很难看懂。
但圈复杂度更适合用于度量编码的结果,对高层设计则不太适用。
响应集是指类的各个方法直接调用的函数数目。响应集无疑的应该尽可能的小,根据得墨忒耳法则:某个对象的任何方法都应该只调用属于以下情形的方法。
class Demeter
{
private:
A* a;
int func();
public:
//...
void example(B& b);
void Demeter::example(B& b)
{
C c;
int f = func(); //<---- 类自己的方法
b.invert(); //<----传入参数的方法,当然参数本身也可能是函数
a = new A();
a->setActive();// <---创建的对象所拥有的方法
c.print(); // <---创建的,并归自己所有的对象的方法
}
};
---摘自《程序员修炼之道》
在度量包时可以用包内部类的数目除以包内类的总数,其比值用来表示包得内聚性。如果用R表示包内部得类关系数目,用N表示包内类总数。那么:
H = (R+1)/N
不稳定性由输入耦合度(Ca)与输出耦合度(Ce)计算而来。
包得输入耦合度是指处于包外部,但依赖于包内类的数目。
包得输出耦合度是指包内部的依赖于包外部类的类数目。
这样I = Ce / (Ca+ Ce)
包的抽象性用抽象类的数目和包中所有类的数目进行计算。
假如说包中类的总数是Nc, 抽象类的数目是Na ,那么抽象度A = Na/Nc
关系内聚性(H),不稳定性(H),抽象性(H)的进一步说明,请参见《敏捷软件开发:原则,模式与实践》一书。
这些度量指标无疑是有意义的,都可以用来评价软件写的好还是坏,但却不解决这样一个问题:
如果一个方案在关系内聚性(H),不稳定性(H),抽象性(H)上都有好的表现,复杂度有没有提高?如果说复杂度因此而提高了,那么这种额外支出的复杂度值不值得?
如果我们认为复杂度是软件的根本问题,那么在满足需求的前提下,使软件简单化就是最关键的使命(比灵活性等重要)。既如此,究竟应该如何度量软件的复杂度呢?
这是一个需要进一步展开的话题,我完善后会进一步和大家分享。