[TestMethod] public void NestingDepthIsAtMost6() { Verifier.VerifyThat<MemberDescriptor>((x, state) => { var c = Metrics.NestingDepth(x); if (c > 6) { state.Message = string.Format("'nesting depth={0} > 6'", c); return false; } return true; }, Filters.MyMethods); }
The portion of interfaces plus abstract types in relation to the total number of types in a module.
The portion of interfaces plus abstract types in relation to the total number of types in a submodule.
Return the number of types in other modules than the target that refer to the target module.
Return the number of types in other submodules than the target that refer to the target submodule.
Return the number of types that refer to the examined type. Self references are excluded.
Return the number of members that refer to the examined member. Self references are excluded.
The number of members from other types that are directly accessed by the specified type.
The cyclomatic complexity of each member for a type are summed up. This way you have a hint to identify types that are hard to maintain.
The cyclomatic complexity is a measurement for the number of linear independent paths through a members source code. Fields always return 0.
Return the depth of the inheritance tree from this type to System.Object inclusive. Interfaces do not count. Only Abstract and concrete types.
The deeper a class in the hierarchy, the greater the number of methods it will probably inherit which makes it harder to predict its behavior. Deeper trees involve greater design complexity since more classes and methods are involved. Deeper classes in the tree have a greater potential for reusing inherited methods.
Return the number of types the examined module depends on. The type inside the module are excluded.
Return the number of types the examined submodule depends on. The type inside the submodule are excluded.
Return the number of types the examined type depends on. Self references are excluded.
Return the number of members the examined member depends on. The declaring type is excluded.
Halstead's delivered bugs is an estimate for the number of errors in the implementation. Delivered bugs is calculated for all modules in the current dependency model that are marked with IsPartOfProject.
Bugs = ( Effort ^ (2/3) ) / 3000
The difficulty level or error proneness of the program is proportional to the number of unique operators in the program. Difficulty is also proportional to the ration between the total number of operands and the number of unique operands (i.e. if the same operands are used many times in the program, it is more prone to errors).
Difficulty = ( ||operators|| / 2 ) * ( operands / ||operands|| )
The effort to implement or understand a program is proportional to the volume and to the difficulty level of the program.
Effort = Volume * Difficulty
Sum of the total number of operators and operands in the part of the program.
Length = operators + operands
Count of the distinct number of operators and operands in the part of the program.
Vocabulary = ||operators|| + ||operands||
The time to implement or understand a program (T) is proportional to the effort. Halstead has found that dividing the effort by 18 give an approximation for the time in seconds.
Time = Effort / 18
According to Halstead, Program Volume V corresponds to the minimum number of bits required for program coding.
Volume = Length * Log2(Vocabulary)
Modules that contain multiple outgoing but few incoming dependencies are less stable because of the consequences of changes in these modules. Modules containing more incoming dependencies are more stable because they are more difficult to change.
Instability = EfferentCoupling / (EfferentCoupling + AfferentCoupling)
Submodules that contain multiple outgoing but few incoming dependencies are less stable because of the consequences of changes in these submodules. Submodules containing more incoming dependencies are more stable because they are more difficult to change.
Instability = EfferentCoupling / (EfferentCoupling AfferentCoupling)
Lack of Cohesion Of Methods (LCOM): The single responsibility principle states that a class should not have more than one reason to change. Such a class is said to be cohesive. A high LCOM value generally pinpoints a poorly cohesive class. The LCOM
HS (HS stands for Henderson-Sellers) takes its values in the range [0-2].
A LCOM HS value higher than 1 should be considered alarming.
Return the deepest nesting of a members IL instructions. An implemented method at least has a nesting depth of 1 as entering the method counts as scope. Fields return 0. Anything above 5 should be considered for refactoring.
Describes the balance between abstractness and stability of a module. A module becomes stable when there are a lot of referrers to it. If so, then it should be abstract at the same time. A module that is instable should be concrete, as there are no referrers
relying on contracts. Values are in range [-1, +1]. Close to -1 means the module is concrete but has many dependent modules, changes to the module will imply lots of changes to referrers. This is called the zone of pain. On the other side, a value of one means
the module is almost completely abstract, but noone uses it. Which is the zone of uselessness.
NormalizedDistance = Abstractness + Instability - 1
Describes the balance between abstractness and stability of a submodule. A submodule becomes stable when there are a lot of referrers to it. If so, then it should be abstract at the same time. A submodule that is instable should be concrete, as there are
no referrers relying on contracts. Values are in range [-1, +1]. Close to -1 means the submodule is concrete but has many dependent submodules, changes to the submodule will imply lots of changes to referrers. This is called the zone of pain. On the other
side, a value of one means the submodule is almost completely abstract, but noone uses it. Which is the zone of uselessness.
NormalizedDistance = Abstractness + Instability - 1
Count the number of types that inherit from the current type.
Count the number of fields in a type including inherited ones.
Count all interface the current type implements, which equals the number of different contracts a type has to fulfill. Interfaces on inherited types and interfaces that are base types for other interfaces also count. Low values are desired.
Count the total number of methods in a type including inherited ones.
Count the number of methods in a type that are overriding a member of a base class.
Count the number of parameters of a method. For more than 2 parameters you must have good reasons. More than 3 parameters might be caused by implementing framework interfaces. Otherwise having more than 3 parameters is a hint for breaking the single responsibility principle.
Count the number of local variables in a member. Fields return 0.
The average number of relations between types of the module per type. Optimal values lie between 1.5 and 4.0.
RelationalCohesion = (RelationShips + 1) / Types
The average number of relations between types of the submodule per type. Optimal values lie between 1.5 and 4.0.
RelationalCohesion = (RelationShips + 1) / Types
The deeper the inheritance graph and the more members are overridden methods, the more complex a system gets. A high value of specialization is an indicator for lack of maintainability for a type and its callers.
SIX = NumberOfOverriddenMethods * DepthOfInheritance / Max(NumberOfMethods, 1)
A value above 1.2 should be considered alarming.
Egg and Gherkin is a development tool written in C# to qualify the evolvement of your software architecture within predefined limits. Controlled by the unit test framework of your choice, it gives immediate feedback when breaking architectural constraints.