Avoid types with too many methods

Rule ND1001 Code Smells

Matches types with more than 20 methods (constructors, property and event accessors excluded): hard to understand and maintain, symptom of too many responsibilities, maybe a God Class (The Class That Does Everything). Types with only constant fields are skipped: many stateless methods is ok.

How to fix

See HowToFix advices of the default rule Types too Big.

Estimated Debt: linear from 1 hour (20 methods) to 10 hours (200+). Static types with no mutable fields cost less: their static methods are easily moved between types.

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 types with too many methods</Name>
// <Id>ND1001:AvoidTypesWithTooManyMethods</Id>
warnif count > 0 from t in JustMyCode.Types 

  // Optimization: Fast discard of non-relevant types 
  where !t.IsInterface &&  // Specially handled by the rule 'Avoid interfaces too big'
         t.NbMethods > 20 &&
         // Require at least a non-constant field
         // because it is ok to have a class with many stateless methods
         t.Fields.Any(f => !f.IsLiteral)

  // Don't match these methods
  let methods = t.Methods.Where(
       m => !(m.IsGeneratedByCompiler ||
              // m.IsConstructor || m.IsClassConstructor ||  // ctor/cctor not enumerated through IType.Methods
              m.IsPropertyGetter || m.IsPropertySetter ||
              m.IsEventAdder || m.IsEventRemover))

  where methods.Count() > 20 
  orderby methods.Count() descending

  let isStaticWithNoMutableState = (t.IsStatic && t.Fields.Any(f => !f.IsImmutable))
  let staticFactor = (isStaticWithNoMutableState ? 0.2 : 1)

select new { 
   t, 
   nbMethods = methods.Count(),
   instanceMethods = methods.Where(m => !m.IsStatic), 
   staticMethods = methods.Where(m => m.IsStatic),

   t.NbLinesOfCode,

   Debt = (staticFactor*methods.Count().Linear(20, 1, 200, 10)).ToHours().ToDebt(),

   // The annual interest varies linearly from interest for severity major for 30 methods
   // to interest for severity critical for 200 methods
   AnnualInterest = (staticFactor*methods.Count().Linear(
                              20,  Severity.Medium.AnnualInterestThreshold().Value.TotalMinutes, 
                              200, Severity.Critical.AnnualInterestThreshold().Value.TotalMinutes)).ToMinutes().ToAnnualInterest()
}

//<Expl>The *{0}* has {1} methods. This is too many suggesting 
// the need to divide it into multiple smaller implementations, 
// each focused on a single responsibility.</Expl>

//<Desc>
// Matches types with more than 20 methods (constructors, property and event
// accessors excluded): hard to understand and maintain, symptom of too many
// responsibilities, maybe a **God Class** (*The Class That Does Everything*).
// Types with only constant fields are skipped: many stateless methods is ok.
//</Desc>

//<HowToFix>
// See *HowToFix advices* of the default rule **Types too Big**.
//
// Estimated Debt: linear from 1 hour (20 methods) to 10 hours (200+).
// Static types with no mutable fields cost less: their static methods are
// easily moved between types.
//</HowToFix>

Code Smells: all 10 items