include::../intro.adoc[] == Why is this an issue? include::../why.adoc[] === Exceptions `equals` and `hashCode` methods are ignored because they might be automatically generated and might end up being difficult to understand, especially in the presence of many fields. == How to fix it include::../how.adoc[] === Code examples **Extraction of a complex condition in a new function.** ==== Noncompliant code example The code is using a complex condition and has a cognitive cost of 3. [source,java,diff-id=1,diff-type=noncompliant] ---- double calculateFinalPrice(User user, Cart cart) { double total = calculateTotal(cart); if (user.hasMembership() // +1 (if) && user.ordersCount() > 10 // +1 (more than one condition) && user.isAccountActive() && !user.hasDiscount() || user.ordersCount() == 1) { // +1 (change of operator in condition) total = applyDiscount(user, total); } return total; } ---- ==== Compliant solution Even if the cognitive complexity of the whole program did not change, it is easier for a reader to understand the code of the `calculateFinalPrice` function, which now only has a cognitive cost of 1. [source,java,diff-id=1,diff-type=compliant] ---- double calculateFinalPrice(User user, Cart cart) { double total = calculateTotal(cart); if (isEligibleForDiscount(user)) { // +1 (if) total = applyDiscount(user, total); } return total; } boolean isEligibleForDiscount(User user) { return user.hasMembership() && user.ordersCount() > 10 // +1 (more than one condition) && user.isAccountActive() && !user.hasDiscount() || user.ordersCount() == 1; // +1 (change of operator in condition) } ---- **Break down large functions.** ==== Noncompliant code example For example, consider a function that calculates the total price of a shopping cart, including sales tax and shipping. + __Note:__ The code is simplified here, to illustrate the purpose. Please imagine there is more happening in the `for` loops. [source,java,diff-id=2,diff-type=noncompliant] ---- double calculateTotal(Cart cart) { double total = 0; for (Item item : cart.items()) { // +1 (for) total += item.price; } // calculateSalesTax for (Item item : cart.items()) { // +1 (for) total += 0.2 * item.price; } //calculateShipping total += 5 * cart.items().size(); return total; } ---- This function could be refactored into smaller functions: The complexity is spread over multiple functions and the complex `calculateTotal` has now a complexity score of zero. ==== Compliant solution [source,java,diff-id=2,diff-type=compliant] ---- double calculateTotal(Cart cart) { double total = 0; total = calculateSubtotal(cart, total); total += calculateSalesTax(cart, total); total += calculateShipping(cart, total); return total; } double calculateShipping(Cart cart, double total) { total += 5 * cart.items().size(); return total; } double calculateSalesTax(Cart cart, double total) { for (Item item : cart.items()) { // +1 (for) total += 0.2 * item.price; } return total; } double calculateSubtotal(Cart cart, double total) { for (Item item : cart.items()) { // +1 (for) total += item.price; } return total; } ---- **Avoid deep nesting by returning early.** ==== Noncompliant code example The below code has a cognitive complexity of 6. [source,java,diff-id=3,diff-type=noncompliant] ---- double calculateDiscount(double price, User user) { if (isEligibleForDiscount(user)) { // +1 ( if ) if (user.hasMembership()) { // +2 ( nested if ) return price * 0.9; } else if (user.ordersCount() == 1) { // +1 ( else ) return price * 0.95; } else { // +1 ( else ) return price; } } else { // +1 ( else ) return price; } } ---- ==== Compliant solution Checking for the edge case first flattens the `if` statements and reduces the cognitive complexity to 3. [source,java,diff-id=3,diff-type=compliant] ---- double calculateDiscount(double price, User user) { if (!isEligibleForDiscount(user)) { // +1 ( if ) return price; } if (user.hasMembership()) { // +1 return price * 0.9; } if (user.ordersCount() == 1) { // +1 ( if ) return price * 0.95; } return price; } ---- === Pitfalls As this code is complex, ensure that you have unit tests that cover the code before refactoring. include::../resources.adoc[] include::../rspecator.adoc[]