MOOD and MOOD2 metrics

The MOOD metrics, defined by Fernando Brito e Abreu, are designed to provide a summary of the overall quality of an object-oriented project. The original MOOD metrics suite consists of 6 metrics. The MOOD2 metrics were added later.

These metrics are useful for projects with heavy use of object-oriented programming. If your project relies mostly on standard modules and classic .frm files, the object-oriented metrics are not a good choice for you, even if your project contains some classes. The MOOD/MOOD2 metrics are available at the project level. They describe the entire project, not an individual class.

See also Object-oriented metrics

MOOD/MOOD2 definitions

When counting the MOOD/MOOD2 metrics, Project Analyzer uses the following definitions.

Class
Classic VB: .cls or .ctl file
VB.NET: Class..End Class
Method (operation)
Classic VB: A sub, function or property procedure.
VB.NET: A sub, function, property accessor or operator.
Attribute (variable)
Classic VB: A variable or array defined in the declarations section of a class.
VB.NET: A class-level variable or array. It can be defined Shared or not. Please notice that MOOD attributes have nothing to do with the .NET <attributes>.

Original MOOD metrics suite

The original MOOD metrics suite includes 6 metrics: MHF, AHF, MIF, AIF, PF and CF.

MHF Method Hiding Factor. AHF Attribute Hiding Factor.

Method and attribute hiding factor measure how variables and methods are encapsulated in a class. Visibility is counted in respect to other classes. MHF and AHF represent the average amoung of hiding among all classes in the system. A private method/attribute is fully hidden. In Visual Basic, hiding decreases in the following order: Protected, Friend, Protected Friend, Public.

MHF = 1 − MethodsVisible

AHF = 1 − AttributesVisible

MethodsVisible = sum(MV) / (C − 1) / Number of methods

MV = number of other classes where method is visible

AttributesVisible = sum(AV) / (C − 1) / Number of attributes

AV = number of other classes where attribute is visible

C = number of classes

For each method, MV is counted. For each attribute, AV is counted.

If all methods or attributes are private, MHF=100% and AHF=100%. If all methods or attributes are public, MHF=0% and AHF=0%.

MHF

The number of visible methods is a measure of the class functionality. Increasing the overall functionality will reduce MHF. However, for implementing the functionality, the abstract interface (visible methods) should be the tip of the iceberg. The implementation will use hidden methods, thus getting information hiding benefits and favoring a MHF increase.

A low MHF indicates insufficiently abstracted implementation. A large proportion of methods are unprotected and the probability of errors is high.

A high MHF indicates very little functionality. It may also indicate that the design includes a high proportion of specialized methods that are not available for reuse.

An acceptable MHF range of 8% to 25% has been suggested but we neither endorse or criticize this view. It is up to you to decide.

Research findings

Increased MHF decreases bug-density and increases quality. Study of 30 C++ projects, Misra and Bhavsar.

Increased MHF decreases defect density and rework effort to find and correct defects. Study of 8 small C++ projects, Abreu and Melo.

AHF

Ideally, all attributes should be hidden, and thus AHF=100% is the ideal value. Very low values of AHF should trigger attention.

MIF Method Inheritance Factor. AIF Attribute Inheritance Factor.

MIF = inherited methods / total methods available in classes

AIF = inherited attributes / total attributes available in classes

A class that inherits lots of methods (attributes) from its ancestor classes contributes to a high MIF (AIF). A child class that redefines its ancestors' methods (attributes) and adds new ones contributes to a lower MIF (AIF). An independent class that does not inherit and has no children contributes to a lower MIF (AIF).

MIF and AIF should be in a reasonable range, not too low and not too high either. Too high a value indicates either superfluous inheritance or too wide member scopes. A low value indicates lack of inheritance or heavy use of Overrides/Shadows.

According to one source, the acceptable MIF range is 20% to 80% and the acceptable AIF range is 0% to 48%. Another view is that AIF should ideally be zero because all variables should be declared Private. The acceptable values for your projects are up to the design pattern you follow.

Even though we cannot present a MIF/AIF range suitable for everyone, we have some tools to control it:

Implementation details

When calculating AIF and MIF, Project Analyzer takes into account only the Inherits statement. Implements inheritance isn’t taken into account, because it inherits only the interface, not the actual implementation. Thus, even if a declaration has an Implements clause, it is counted as non-inherited one.

Project Analyzer expects the parent class to be available as source code. If it isn’t available, inheritance is not detected, leading to a too low MIF and AIF.

A method/attribute is inherited if:

  1. It’s defined in the base class
  2. It’s visible in the child class (for intra-project inheritance, Private isn’t used; for cross-project inheritance, the member is declared as Protected, Protected Friend or Public).
  3. It’s not overridden or shadowed in the child.

For a class lacking an Inherits statement, MIF=0% and AIF=0%. For a class with no attributes, AIF=0%. For a class with no methods, MIF=0%. If inheritance is not used at all, MIF and AIF are always 0%. This is the case in VB Classic projects where inheritance does not exist.

Research findings

Increased MHF decreases defect density and rework effort to find and correct defects. Inheritance appears to be an appropriate technique to reduce error density and rework. Study of 8 small C++ projects, Abreu and Melo.

PF Polymorphism Factor

Polymorphism Factor (PF alias POF) measures the degree of method overriding in the class inheritance tree. It equals the number of actual method overrides divided by the maximum number of possible method overrides.

PF = overrides / sum for each class(new methods * descendants)

A call to an object’s method can be statically or dynamically bound to a named method implementation. The latter can have as many shapes as the number of times the method is overridden in that class’s descendants. In the formula, the numerator equals the actual overrides and the denumerator is the maximum number of possible overrides.

Simply put, PF is the "Overrides factor". The more you use the Overrides keyword, the higher PF. If you always override everything, you get a PF of 100%. If your child classes seldom override their parent's methods, you get a low PF. If your parent classes declare sealed methods (you seldom use the Overridable keyword), you will end up with a low PF.

PF varies between 0% and 100%. As mentioned above, when PF=100%, all methods are overridden in all derived classes. A PF value of 0% may indicate one of the following cases:

Polymorphism arises from inheritance and has its pros and cons. We can intuitively expect that polymorphism (Overrides) can be used to a reasonable extent to keep the code clear, but that excessively polymorphic code may be too complex to understand (because several alternative methods can execute for one call statement). PF should lie in a reasonable range with a lower and an upper bound.

We have found conflicting recommendations for the acceptable values of PF. One source recommends PF>=10% but Abreu and Melo suggest that PF values well above 10% are very high and reduce quality benefits. Thus, we don't offer a recommendation for the acceptable values of PF.

Research findings

Increased PF may decrease bug-density and increase quality. This result is somewhat counterintuitive as an increased PF means increased complexity, which would intuitively increase bug-density, not decrease it. The study suggests that the quality effect of PF is low. Study of 30 C++ projects, Misra and Bhavsar.

Increased PF decreases defect density and rework effort to find and correct defects. Very high PF values (well above 10%) are expected to reduce these benefits. Study of 8 small C++ projects, Abreu and Melo.

Implementation details

When calculating PF, Project Analyzer requires that the whole class inheritance tree must be analyzed. In other words, PF is calculated purely from the class hierarchy you have defined in your source code. It is not calculated for classes derived from, say, the .NET framework classes.

Limitations of PF

PF measures polymorphism due to method overriding. It doesn't measure method overloading, or polymorphism due to interface implementation. Actually, if it were measured, implementing an interface would always add a full degree of polymorphism to the project, as the implementing methods override the corresponding method of the interface.

PF is always zero in VB Classic, because it does not support inheritance.

CF Coupling Factor

Coupling Factor (CF alias COF) measures the actual couplings among classes in relation to the maximum number of possible couplings.

CF = Actual couplings / Maximum possible couplings

Class A is coupled to class B if A calls methods or accesses variables of B.
In turn, B is coupled to A only if B calls methods or accesses variables of A. B is not coupled to A if there is no call/access from B to A.

Maximum possible couplings happens when all classes are coupled to (and from) all other classes.

If no classes are coupled, CF = 0%. If all classes are coupled to all other classes, CF=100%.

Couplings due to the use of the Inherits statement are not included in CF. Naturally, a class is heavily coupled to its ancestors via inheritance. Ancestor-descendant class pairs are assumed to be impossible to couple when counting CF.

Coupling relations increase complexity, reduce encapsulation and potential reuse, and limit understandability and maintainability. Very high values of CF should be avoided. However, classes must cooperate somehow, and CF is expected to be lower bounded. It has been suggested that CF should not exceed 12%, but we cannot comment whether that is a viable upper limit for all systems.

Two-way coupling has twice the effect of one-way coupling. You can get coupling down by breaking two-way calls (or variable access) between classes. If you need to communicate both ways, you can use events for the backward calls. As to data access, you can pass data as parameters instead of reading variables directly from other classes.

It is to be noted that CF only takes direct class coupling into account. If two classes are coupled via a standard module, the coupling is not counted in CF. Therefore, CF is meaningful in fully object-oriented systems only.

Research findings. Increased CF increases defect density and rework effort to find and correct defects. Coupling should be avoided. Study of 8 small C++ projects, Abreu and Melo.

The definition of coupling for CF equals the definition used for CBO, with the following differences:

MOOD reference values

Here are some reference MOOD values for comparison with your own projects.

System MFC GNU ET+ Motif
MHF 24.6% 13.3% 9.6% 39.2%
AHF 68.4% 84.1% 69.4% 100.0%
MIF 83.2% 63.1% 83.9% 64.3%
AIF 59.6% 62.6% 51.8% 50.3%
CF 9.0% 2.8% 7.7% 7.6%
PF 2.7% 3.5% 4.5% 9.8%

The original data was available online but has disappeared.

MOOD2 metrics

The MOOD2 metrics set is a later addition by the author of the MOOD metrics set. Project Metrics provides the following metrics from the new suite: OHEF, AHEF, IIF, PPF. There are even more metrics in MOOD2, but they are not suitable for automated code analysis.

OHEF & AHEF: Operation/Attribute Hiding Effectiveness Factor

OHEF = Classes that do access operations / Classes that can access operations

AHEF = Classes that do access attributes / Classes that can access attributes

OHEF measures the goodness of scope settings on class operations i.e. methods. Similarly, AHEF measures the same on class attributes i.e. variables.

When OHEF=1, scope settings are perfect. When OHEF approaches zero, scope settings are too wide compared to the callers of the methods. The same can be said about AHEF and variables.

OHEF is related to MHF. MHF measures the general level of method hiding whereas OHEF measures how well the hiding succeeds. Don't be confused about "operations" and "methods". The author of MOOD and MOOD2 first used the name "methods" and then changed his mind in favor of "operations". They are the same thing.

AHEF is related to AHF. AHF measures the general level of attribute hiding whereas AHEF measures how well the hiding succeeds.

OHEF and AHEF should be used with purely object oriented systems consisting of classes only. For systems with non-OO code such as standard modules and classic VB .frm files, OHEF and AHEF frequently get low values and are not useful.

IIF Internal inheritance factor

IIF = Classes that inherit a VB class / All classes that inherit something

If there is no inheritance, IIF=0.

IIF measures the amount of internal inheritance in your system. Internal inheritance happens when a class inherits another class in the same system. The opposite happens when a class inherits an external class, such as a .NET framework class or any other binary class. Here we assume that all VB source code classes are internal and all unknown or binary classes are external to your system. A system with a high IIF defines its own class tree. A system with a low IIF reuses external classes.

In VB.NET, all classes inherit a parent class. Ultimately, they derive from System.Object. We can simplify IIF for VB.NET as:

IIF for VB.NET = Classes that inherit a VB.NET class / All VB.NET classes

In VB Classic there is no inheritance and IIF is always zero.

PPF Parametric polymorphism factor

PPF = Parametrized classes / All classes

This metric is simply the percentage of the classes that are parametrized. A parametrized class is also called a generic class. Generic classes are available since .NET 2005. For earlier systems PPF is always zero.

Further readings on MOOD and MOOD2

MOOD readings

MOOD2 readings

MOOD research

CF readings

See also Object-oriented metrics

© Project Analyzer Help