E11014: low cohesion - class has disconnected method groups, consider splitting

Category: E11xxx Code quality and IR analysis · Reported in phase: PRE_IR_CHECKS · Classification: LOW_COHESION · All error codes

What went wrong

Your class is doing too many unrelated things. Methods don't work together - they're strangers sharing an office. (LCOM4 metric)

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 761 (What is the LCOM4 cohesion limit and how do I stay within...)

Rationale

  • LCOM4 counts "connected components" (method groups)
  • Methods are connected if they share field access
  • Score of 1 = perfect (all methods work together)
  • Thresholds: CLASS=8, COMPONENT=10

Refactoring pattern

Before: BigClass with groups A, B, C (LCOM4=3) After: ClassA (LCOM4=1), ClassB (LCOM4=1), ClassC (LCOM4=1)

Coordinator uses ClassA, ClassB, ClassC

How to fix it

  1. Identify disconnected method groups (use different fields)
  2. Create new class for each group (one responsibility per class)
  3. Move fields and methods together
  4. Use composition if original class needs to coordinate

Common causes

  • Too many responsibilities in one class
  • Methods accumulated without refactoring

See also

  • E11014: lack of cohesion (this error)
  • E11015: coupling (depends on too many types)

Examples

Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.

Fails with E11014: low cohesion - class has disconnected method groups, consider splitting

#!ek9
defines module error.e11014.bad

  defines class

    LowCohesionClass
      //9 separate fields - each used by only one method
      field1 as Integer: 1
      field2 as Integer: 2
      field3 as Integer: 3
      field4 as Integer: 4
      field5 as Integer: 5
      field6 as Integer: 6
      field7 as Integer: 7
      field8 as Integer: 8
      field9 as Integer: 9

      //9 methods - each uses only one field, creating 9 disconnected components
      //LCOM4 = 9 > 8 (class threshold)

      method1()
        <- rtn as Integer: field1

      method2()
        <- rtn as Integer: field2

      method3()
        <- rtn as Integer: field3

      method4()
        <- rtn as Integer: field4

      method5()
        <- rtn as Integer: field5

      method6()
        <- rtn as Integer: field6

      method7()
        <- rtn as Integer: field7

      method8()
        <- rtn as Integer: field8

      method9()
        <- rtn as Integer: field9

      //Custom isSet that only accesses field1 - doesn't connect other methods
      override operator ? as pure
        <- rtn as Boolean: field1?

Fixed: Split the class so each one keeps the data its methods use together

#!ek9
defines module error.e11014.good

  defines class

    Counter
      count as Integer: 0
      step as Integer: 1

      current()
        <- rtn as Integer: count

      increment()
        count += step

      reset()
        count: 0
        step: 1

      default operator ?

Questions and answers

These Q&A entries show E11014 and its fix: