How to Identify Shotgun Surgery Using NDepend
A method suffers from shotgun surgery if it is called many times from many other classes. You can identify it by looking at changing methods and changing classes.
Join the DZone community and get the full member experience.
Join For FreeIn the previous articles. in this series we’ve seen:
- How to identify a god class
- How to identify feature envy
- How to identify a data class
- How to identify a brain method
- How to identify intensive coupling and dispersed coupling
In this article, we’ll see how to identify an afferent (incoming) coupling code smell: Shotgun Surgery.
Shotgun Surgery Detection Strategy
A method suffers from shotgun surgery if it is called many times from many other classes. Object-Oriented Metrics in Practice by Michele Lanza and Radu Marinescu proposes the following detection strategy for Shotgun Surgery:
(CM > Short Memory Cap) AND (CC > Many)
This detection strategy uses two metrics:
- CM (Changing Methods): The number of methods that call the measured method.
- CC (Changing Classes): The number of classes in which the changing methods are defined.
This detection strategy uses generally-accepted meaning thresholds. I used seven as the value for short memory cap and 10 for many.
Metrics Definitions
Let’s go over the definitions for the used metrics and how to implement them with NDepend. For a more detailed definition, be sure to check Appendix A.2 of Object-Oriented Metrics in Practice. If you’re not familiar with CQLinq, check out the NDepend documentation or my blog post on how to query your code base.
CM (Changing Methods)
This metric counts the number of distinct methods that call the measured method. We only care about our own code for both caller methods and measured methods. However, since framework code doesn’t call our code, we can rely on the NbMethodsCallingMe NDepend metric.
// <Name>CM</Name>
from m in JustMyCode.Methods
let cm = m.NbMethodsCallingMe
orderby cm descending
select new { m, cm }
CC (Changing Classes)
This metric counts the number of classes in which the changing methods are defined.
// <Name>CC</Name>
from m in JustMyCode.Methods
let changingClasses = m.MethodsCallingMe.Select(method => method.ParentType).ToHashSet()
let cc = changingClasses.Count()
orderby cc descending
select new { m, cc, changingClasses }
Putting It All Together
Now that we know how to compute each of the required metrics, let’s see how the detection strategy looks like:
// <Name>Shotgun Surgery</Name>
warnif count > 0
// ** Thresholds **
let ShortMemoryCap = 7
let Many = 10
// ** Detection Strategy **
from m in JustMyCode.Methods
let methodsCallingMe = m.MethodsCallingMe
let changingClasses = methodsCallingMe.Select(method => method.ParentType).ToHashSet()
let cm = m.NbMethodsCallingMe
let cc = changingClasses.Count()
where
// Operation is called by too many other methods
cm > ShortMemoryCap &&
// Incoming calls are from many classes
cc > Many
select new { m, cm, cc, changingClasses, methodsCallingMe }
And that's it!
Published at DZone with permission of Victor Chircu, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments