Afferent Coupling of a Type (Incoming Dependencies)

The afferent coupling of a type is the number of other types that directly depend on it.

If the afferent coupling of type t is high, it is an indication that t has many responsibilities, and it is therefore likely that any changes have a high impact across the system. In such cases you probably want to drill down further and explore the reasons for the dependencies; one way of going about that is suggested below.

One type t depends on another type dep if t makes use of dep in some way. More precisely:

  • t is a direct subtype of (implements or extends) dep
  • t declares a field of type dep
  • t declares a method or constructor that
    • has dep as its return type
    • has a parameter of type dep
    • throws an exception of type dep
    • calls a method declared in dep
    • accesses a field declared in dep

There are some subtle issues relating to generic types, discussed in more detail with the .QL source code for this metric below.

Pre-packaged Query

Query name = "Semmle/Metrics/Types/Reftypes with high afferent coupling"

Reports types that have an afferent coupling of more than 25, in descending order, as a bar chart.

from MetricRefType t, int n
where t.fromSource() and
      n = t.getAfferentCoupling() and
      n > 25
select t,n order by n desc

.QL Source of Metric

This metric is defined in the class MetricRefType. Its main method is disarmingly simple:

int getAfferentCoupling() {
  result = count(RefType t | depends(t,this))
}

The challenge is to give a correct definition of dependency between types. Above we gave an informal definition; it ignored, however, the use of generic types which may cause reverse dependencies to occur. For instance, consider a class Foo in package bar that declares a field of type List< Foo >. The parameterized type List< Foo > is placed in the same package as List< T >. Therefore we make Foo not depend on the parameterized type List< Foo > but rather on the generic type List and its argument Foo separately. In the definition of depends below, that is handled by calling the predicate usesType:

predicate depends(RefType t, RefType dep) {
   not isParameterized(t) and not isRaw(t) and
   (
   usesType(t.getASupertype(), dep)
   or
   usesType(t.getAField().getType(), dep)
   or
   usesType(t.getAMethod().getType(), dep)
   or
   usesType(t.getACallable().getAParamType(), dep)
   or
   usesType(t.getACallable().getAnException().getType(), dep)
   or
   usesType(t.getACallable().getACall().getDeclaringType(), dep)
   or
   usesType(t.getACallable().getAnAccess().getDeclaringType(), dep)
   )
}

Note that parameterized types are deemed not to be dependent on anything themselves, and neither are their raw counterparts (generic types used as if they were non-generic).

We now proceed to the definition of usesType itself. The intention is that usesType(t,dep) holds when dep occurs in type t. If t=X< Y >, we have dep=X or dep=Y. If t is raw, it uses the corresponding generic version. An array type just uses its element type. Finally, in all other cases, we require t=dep.

predicate usesType(Type t, RefType dep) { 
  ((ParameterizedType)t).getErasure() = dep 
  or 
  ((ParameterizedType)t).getATypeArgument() = dep
  or
  ((RawType)t).getErasure() = dep 
  or
  (((Array)t).getElementType() = dep) 
  or
  (not isParameterized(t) and not isRaw(t) and not(t instanceof Array) and t = dep) 
}

Further remarks

When writing queries that involve dependencies, it is sometimes useful to be able to explore the dependencies themselves. The default library provides a special version of depends for this purpose, which takes the types t and dep, but also a string explanation and an element to click on for further investigation.

/** dependencies with reasons as strings and locatable to click on */
predicate depends(RefType t, RefType dep, string reason, Reason click) {
    not isParameterized(t) and not isRaw(t) and
    (
    (usesType(t.getASupertype(), dep) and reason = "super type" and click = t)
		or
    (exists(Field f | t.getAField()=f and 
                      usesType(f.getType(), dep) and 
                      reason = "field "+f.getName() and 
                      click = f))
    or
    (exists(Method m | m.getDeclaringType() = t and
                        usesType(m.getType(), dep) and 
                        reason = "return of method "+m.getName() and
                        click=m)) 
    or
    (exists(Callable c | c.getDeclaringType() = t and 
             usesType(c.getAParamType(), dep) and 
             reason = "parameter of callable "+c.toString() and
             click=c))
		or
    (exists(Exception e, Callable c | c=t.getACallable() and 
             c.getAnException()= e and 
             usesType(e.getType(), dep) and 
             reason = c.getName() + " throws "+e.toString() and
             click = c))
    or
    (exists(Call c, Callable n, Callable m | 
             n = c.getCaller() and
             n.getDeclaringType()=t and
             m = c.getCallee() and
             usesType(m.getDeclaringType(), dep) and 
             reason = "call of "+m.getName()+" by "+n.getName() and
             click=c))
    )
    or
    (exists(Field f, FieldRead fr, Callable m | 
             t.getACallable() = m and m = fr.getSite() and
             f = fr.getField() and
             usesType(f.getDeclaringType(),dep) and
             reason = "read of "+f.getName()+ " by "+ m.getName() and
             click = fr))
    or
    (exists(Field f, FieldWrite fw, Callable m | 
             t.getACallable() = m and m = fw.getSite() and
             f = fw.getField() and
             usesType(f.getDeclaringType(),dep) and
             reason = "write of "+f.getName()+ " by "+ m.getName() and
             click = fw))
                                  
}

References

Robert C. Martin. OO Design Quality Metrics. October 24, 1994.

Robert C. Martin. Agile Software Development, Principles, Patterns and Practices. Addison Wesley, 2002.

Andrew Glover. In Pursuit of Code Quality: Code Quality for Software Architects. April 2006.

Jack Shirazi and Kirk Pepperdine. Eye on performance: Determining the riskiness of change. July 2004.

Digg!Reddit!Del.icio.us!Facebook!Slashdot!Technorati!StumbleUpon!Ma.gnolia!

Comments
Only registered users can write comments!
 
< Prev   Next >