Simple Way to Compute Cyclomatic Complexity of a Program
Cyclomatic complexity is a software metric used to indicate program complexity.
It is a quantitative measure of the number of linearly independent paths through a program's source code.
Computing Cyclomatic Complexity
As a TDD expert, understanding cyclomatic complexity is crucial for writing testable code.
The most common way to compute cyclomatic complexity is using McCabe's formula:
Where:
- V(G) = Cyclomatic Complexity
- E = Number of edges in the control flow graph
- N = Number of nodes in the control flow graph
- P = Number of connected components (usually 1 for a single program or function)
Alternatively, for a structured program (which is what we aim for with TDD), cyclomatic complexity can also be calculated as:
V(G) = Number of decision points + 1
Decision points include `if`, `for`, `while`, `case` (in `switch` statements), `catch` blocks, and `&&` or `||` operators. Each introduces a new execution path.
Steps to Compute:
- Draw the Control Flow Graph (CFG): Represent the code as a graph where nodes are processing tasks (blocks of code) and edges represent the flow of control.
- Count Edges (E): Count every arrow in your CFG.
- Count Nodes (N): Count all the blocks of code or statements in your CFG.
- Apply McCabe's Formula: Plug the values into V(G) = E - N + 2P
- Alternatively, Count Decision Points: Identify and count all explicit decision points in the code.
Examples of Computing Cyclomatic Complexity with Java Functions
Let's illustrate the computation of cyclomatic complexity with a few Java function examples. We'll use the formula: V(G) = Number of decision points + 1.
Example 1: Simple Function (No Decision Points)
public void simpleFunction() {
System.out.println("This is a simple function.");
}
Decision Points: 0 (no `if`, `for`, `while`, `switch`, `catch`, `&&`, `||`)
|---|
Cyclomatic Complexity: 0 + 1 = 1
Example 2: Function with an `if` Statement
public String checkNumber(int number) {
if (number > 0) { // Decision point 1
return "Positive";
} else {
return "Non-positive";
}
}
Decision Points: 1 (`if`)
Cyclomatic Complexity: 1 + 1 = 2
Example 3: Function with `if-else if-else` Statements
public String getGrade(int score) {
if (score >= 90) { // Decision point 1
return "A";
} else if (score >= 80) { // Decision point 2
return "B";
} else {
return "C";
}
}
Decision Points: 2 (`if`, `else if`)
Cyclomatic Complexity: 2 + 1 = 3
Example 4: Function with a `for` Loop
public int sumArray(int[] arr) {
int sum = 0;
for (int i = 0; i < arr.length; i++) { // Decision point 1
sum += arr[i];
}
return sum;
}
Decision Points: 1 (`for` loop condition)
Cyclomatic Complexity: 1 + 1 = 2
Example 5: Function with `while` Loop and `&&` Operator
public boolean findElement(int[] arr, int target) {
int i = 0;
while (i < arr.length && arr[i] != target) { // Decision point 1 (while), Decision point 2 (&&)
i++;
}
return i < arr.length;
}
Decision Points: 2 (`while`, `&&`)
Cyclomatic Complexity: 2 + 1 = 3
Example 6: Function with `switch` Statement
public String getDayOfWeek(int day) {
String dayName;
switch (day) { // Decision point 1
case 1: // Each case is effectively a decision point, but typically
dayName = "Monday"; // the switch itself counts as one, and
break; // additional logic within cases adds to complexity.
case 2: // For simplicity, we count the switch once for the branching.
dayName = "Tuesday";
break;
default:
dayName = "Invalid day";
break;
}
return dayName;
}
Decision Points: 1 (`switch` statement itself for the branching logic)
Cyclomatic Complexity: 1 + 1 = 2
Note: For `switch` statements, different interpretations exist. Some count each `case` as a decision point, others count the `switch` itself as one, plus any additional `case` statements that lead to new paths. The `Number of decision points + 1` often simplifies this to just the initial branching logic. For more precise calculation with McCabe's formula using a CFG, each case would typically represent an edge.
Example 7: Function with `try-catch` Block
public String readFile(String filePath) {
try {
// Code that might throw an exception
return "File content";
} catch (IOException e) { // Decision point 1 (catch block)
return "Error reading file: " + e.getMessage();
}
}
Decision Points: 1 (`catch` block)
Cyclomatic Complexity: 1 + 1 = 2
These examples demonstrate how each type of decision-making construct in Java code increases the cyclomatic complexity, directly correlating to the number of independent execution paths. As a TDD expert, aiming for functions with lower cyclomatic complexity (ideally 1-10) makes them easier to test thoroughly and maintain.
Importance of Cyclomatic Complexity Number
From a TDD perspective, the cyclomatic complexity number is highly significant for several reasons:
- Testability: A higher cyclomatic complexity indicates more paths through the code, which in turn means more test cases are required to achieve adequate code coverage. As a TDD expert, you're constantly striving for testable code, and high complexity flags areas that will be difficult and time-consuming to test thoroughly.
- Maintainability: Complex code is harder to understand, modify, and debug. High complexity often correlates with more defects and increased maintenance costs. TDD promotes writing simple, focused code, which naturally leads to lower complexity and better maintainability.
- Readability: Code with a lower cyclomatic complexity is generally easier to read and comprehend. Each function should ideally do one thing and do it well, leading to less intricate control flow.
- Refactoring Opportunities: A high cyclomatic complexity number can serve as a strong indicator that a function or method needs to be refactored. It suggests that the code might be doing too much, and could benefit from being broken down into smaller, more manageable units. This aligns perfectly with the TDD principle of writing code, making it pass, and then refactoring for better design.
- Risk Assessment: Areas of the codebase with higher cyclomatic complexity are typically more prone to errors and represent higher risk. By identifying these areas, you can focus your testing efforts and potentially prioritize refactoring.
In essence, cyclomatic complexity helps TDD practitioners design functions that are concise, focused, and therefore easier to test and maintain, which are core tenets of the TDD methodology.

No SPAMS please.Give constructive Feedbacks.