|
The Lakos level of a type gives an indication of how "high" it is in the design of a system. For example, the Main classes usually are high up in this sense, whereas utility classes are lower. The metric is named after John Lakos,
who introduced it in his book "Large-scale C++ software design".
One type depends t depends on another (different) type dep
if t != dep and t makes use of dep in some way.
For a further discussion of the precise definition of dependencies between
types, please refer to the description of
afferent coupling.
The level of a type is defined as follows.
An element has no level defined if it is cyclically dependent
on itself. Otherwise, it has level 0 of it does not depend
on any other elements. It has level 1 if it does not
directly depend
on another element that occurs in the source. Finally, if it
depends on another element at level n then it has level n+1.
This definition possibly assigns multiple levels to the same type.
The Lakos level of a type is the maximum over all such levels.
There is no recommended value for the Lakos level; it is simply a means
of exploration, typically when we wish to understand a design in
a top-down manner. We then first explore the highest Lakos levels, and
work our way down.
Pre-packaged Query
Query name = "Semmle/Metrics/Types/Coupling/Reftypes with high Lakos level"
Reports types with a Lakos level greater than 10, in descending order, as a bar chart.
from MetricRefType t, int n
where t.fromSource() and
n = t.getLevel() and
n > 10
select t,n order by n desc
.QL Source of Metric
The Lakos level is in fact defined for multiple kinds of program element,
so there is a generic definition in MetricLevel, and the relevant
notion of dependency is overridden in MetricRefType. First,
that overriding is simply:
MetricElement getADependency() {
depends(this,result) and this != result
}
That instantiates the generic definition in MetricElement, which
itself reads as shown below. Note a small subtlety: we restrict
ourselves to dependency cycles that occur in the source, rather than
via some library.
/* a dependency in the source */
MetricElement getADependencySrc() {
result = this.getADependency() and result.fromSource()
}
/* get some level value */
int getALevel() {
this.fromSource()
and
not(this.getADependencySrc+()=this)
and
( (not(exists(MetricElement t | t=this.getADependency()))
and
result=0)
or
(not(this.getADependency().fromSource())
and
result=1)
or
(result = this.getADependency().getALevel() + 1) )
}
/* getLevel returns John Lakos's level metric */
int getLevel() {
result = max(int d | d = this.getALevel())
}
Further Remarks
In other metrics tools, it is sometimes suggested that you use the
absence of a Lakos level to test for the existence of dependency
cycles. In .QL, it is much nicer to write a special query for that,
and some hints on how to do that can be found in the description
of Cyclic Package Dependencies.
References
John Lakos.
Large-scale C++ Software Design
Addison-Wesley, 1996.
Antranig Basman.
Levelization
Wiki Page, 2006.
Patrick Smacchia.
NDepend - Level metric
,
2006.
|