Avoid adding methods to a type that already had many methods
Runs only in diff mode (baseline defined). Lists types that already had more than 40 methods at baseline and got new ones. Such types are hard to maintain, symptom of too many responsibilities. Constructors and compiler-generated methods are not counted.
How to fix
Split the type into smaller types implementing the same logic together.
Estimated Debt: 10 minutes per method added. Severity is High: fix these issues now, before such code gets released in production.
Estimated Debt: 10 minutes per method added. Severity is High: fix these issues now, before such code gets released in production.
CQLinq source
This rule is written in CQLinq, C# LINQ over the NDepend code model. It ships with every new NDepend project: you can read it, adapt it, or use it as a starting point for your own custom rules.
// <Name>Avoid adding methods to a type that already had many methods</Name>
// <Id>ND1106:AvoidAddingMethodsToATypeThatAlreadyHadManyMethods</Id>
warnif count > 0
// Don't count constructors and methods generated by the compiler!
let getMethodsProc = new Func<IType, IList<IMethod>>(
t0 => t0.Methods.Where(m =>
//!m.IsConstructor && !m.IsClassConstructor && // ctor/cctor not enumerated through IType.Methods
!m.IsGeneratedByCompiler).ToArray())
from t in JustMyCode.Types where
t.NbMethods > 40 && // Just here for optimization
t.IsPresentInBothBuilds()
// Optimization: fast discard of non-relevant types
where t.OlderVersion().NbMethods > 40
let oldMethods = getMethodsProc(t.OlderVersion())
where oldMethods.Count > 40
let newMethods = getMethodsProc(t)
where newMethods.Count > oldMethods.Count
let addedMethods = newMethods.Where(m => m.WasAdded())
let removedMethods = oldMethods.Where(m => m.WasRemoved())
orderby addedMethods.Count() descending
select new {
t,
nbOldMethods = oldMethods.Count,
nbNewMethods = newMethods.Count,
addedMethods,
removedMethods,
Debt = (10*addedMethods.Count()).ToMinutes().ToDebt(),
AnnualInterest = addedMethods.Count().Linear(
1, Severity.Medium.AnnualInterestThreshold().Value.TotalMinutes,
100, 4*(Severity.High.AnnualInterestThreshold().Value.TotalMinutes)).ToMinutes().ToAnnualInterest()
}
//<Expl>
// The *{0}* previously contained {1} methods
// in the baseline and now has {2} methods.
// This increase is indicative of excessive complexity,
// suggesting the necessity of breaking it down into multiple
// smaller implementations, each dedicated to a single
// responsibility.
//</Expl>
//<Desc>
// Runs only in *diff mode* (baseline defined). Lists types that already had
// more than 40 methods at baseline and got new ones. Such types are hard to
// maintain, symptom of too many responsibilities. Constructors and
// compiler-generated methods are not counted.
//</Desc>
//<HowToFix>
// Split the type into smaller types implementing the same logic together.
//
// Estimated Debt: 10 minutes per method added. Severity is **High**: fix
// these issues **now**, before such code gets released in production.
//</HowToFix>
Open in the interactive Rules Explorer · All default rules
Code Smells Regression: all 9 items
- From now, all types added should respect basic quality principles ND1100
- From now, all types added should be 100% covered by tests ND1101
- From now, all methods added should respect basic quality principles ND1102
- Avoid decreasing code coverage by tests of types ND1103
- Avoid making complex methods even more complex ND1104
- Avoid making large methods even larger ND1105
- Avoid adding methods to a type that already had many methods ND1106
- Avoid adding instance fields to a type that already had many instance fields ND1107
- Avoid transforming an immutable type into a mutable one ND1108