Brain method is a method that tends to centralize the functionality of its owner class (very much in a way a God Class tends to centralize a system’s intelligence). It tends to be long as it carries out most of the tasks of the class that are supposed to be distributed among several methods. It has too many conditional branches and deep nesting.
Impact
- Brain methods are excessively long and complex. This impacts understandability and reusability.
- Too many conditional branches and too deep nesting necessitates excessive testing.
- Long methods often try to do several things in one place. This results in usage of excessive temporary variables. This makes the method error-prone.
Characteristics
- Method is excessively large(high LOC).
- Method has too many conditional branches (High Cyclomatic Complexity).
if (condition1 is TRUE){...... } else if (condition2 is TRUE){...... } else if (condition3 is TRUE){...... } else if (condition4 is TRUE){...... } // several more elseifs... else {......}
- Method has deep nesting level (deeply nested IF-ELSE statements)
if (condition1 is TRUE){ if (condition2 is TRUE){ if (condition3 is TRUE){ if (condition4 is TRUE){ // execute something... } } }
- Method uses too many variables (including local variables, data members, and global variables) – more variables than a human can keep in short-term memory.
Example(s)
public int getFoldLevel(int line) { if(line < 0 || line >= lineMgr.getLineCount()) throw new ArrayIndexOutOfBoundsException(line); if(foldHandler instanceof DummyFoldHandler) return 0; int firstInvalidFoldLevel = lineMgr.getFirstInvalidFoldLevel(); if(firstInvalidFoldLevel == -1 || line < firstInvalidFoldLevel) { return lineMgr.getFoldLevel(line); } else { if(Debug.FOLD_DEBUG) Log.log(Log.DEBUG,this,"Invalid fold levels from " + firstInvalidFoldLevel + " to " + line); int newFoldLevel = 0; boolean changed = false; int firstUpdatedFoldLevel = firstInvalidFoldLevel; for(int i = firstInvalidFoldLevel; i <= line; i++) { Segment seg = new Segment(); newFoldLevel = foldHandler.getFoldLevel(this,i,seg); if(newFoldLevel != lineMgr.getFoldLevel(i)) { if(Debug.FOLD_DEBUG) Log.log(Log.DEBUG,this,i + " fold level changed"); changed = true; // Update preceding fold levels if necessary if (i == firstInvalidFoldLevel) { List precedingFoldLevels = foldHandler.getPrecedingFoldLevels( this,i,seg,newFoldLevel); if (precedingFoldLevels != null) { int j = i; for (Integer foldLevel: precedingFoldLevels) { j--; lineMgr.setFoldLevel(j,foldLevel.intValue()); } if (j < firstUpdatedFoldLevel) firstUpdatedFoldLevel = j; } } } lineMgr.setFoldLevel(i,newFoldLevel); } if(line == lineMgr.getLineCount() - 1) lineMgr.setFirstInvalidFoldLevel(-1); else lineMgr.setFirstInvalidFoldLevel(line + 1); if(changed) { if(Debug.FOLD_DEBUG) Log.log(Log.DEBUG,this,"fold level changed: " + firstUpdatedFoldLevel + ',' + line); fireFoldLevelChanged(firstUpdatedFoldLevel,line); } return newFoldLevel; } }
Guidelines
- Split a brain method into two or more separate methods. This does not mean breaking down the method into several methods that are called in continuation. This simply increases level of nesting. Rather extract pieces of code into separate methods and invoke these methods in parallel from the original method.
Wrong way to split function_A( ):
function_A( ) { // execute few lines of code and then invoke function_B( ) function_B( ); } function_B( ) { // execute few lines of code and then invoke function_C( ) function_C( ); } function_C( ) { // execute few lines of code and then invoke function_D( ) function_D( ); }
Recommended way to split function_A( )
function_A { ...... // execute some code, then invoke function_B( ) function_B( ); ...... // execute some code, then invoke function_C( ) function_C( ); ...... // execute some code , then invoke function_D( ) function_D( ); }
- If a method has significant pieces of code executed via too many conditional branches, use polymorphisms i.e., refactor class design in such a way that the code in conditional branches is delegated to overloaded/overridden functionality in children classes.
- During testing, make sure there is a test case for every independent path.
- Avoid usage of Global variables.
- Don’t use too many variables in a single method.