
Table of contents
Extract Method
Code Smells
- Too many responsibilities in a single method
- Method that breaks the single responsibility principle
- Long methods
- Comments on part of the method
- Andin the method name
Technique
- Extract the new method by using your IDE feature - Makes its purpose self-evident
 
- Be careful with the needed variables before extracting otherwise you will have to move them by yourself
Practice 1
- Open Orderincomposing.methodspackage
- Extract methods to improve readability / reduce complexity - Comments here can help you split your code
 
@AllArgsConstructor
@Getter
@Builder
public class Order {
    private final Customer customer;
    private final ArrayList<Product> products;
    public String generateStatement() {
        if (customer != null && !customer.getName().isEmpty() && products.size() > 0) {
            StringBuilder statement = new StringBuilder();
            //Add banner
            statement.append("Statement for : " + customer + "%n");
            for (Product p : products) {
                // Add details.
                statement.append("Product: " + p.getName() + " Price: " + p.getPrice() + "%n");
            }
            double total = AmountCalculator.calculatePrice(this, true, customer.getAge());
            statement.append("Total: " + total + "€");
            return statement.toString();
        } else throw new IllegalArgumentException("InvalidOrder");
    }
    public Double totalPrice() {
        return getProducts().stream().map(Product::getPrice).reduce(0.0, Double::sum);
    }
}
Practice 2
- Open AmountCalculatorincomposing.methodspackage
- Extract methods to remove code duplication
- The power of your IDE you will use- Start by trying to extract this piece of code : double discountBasedOnAge = 0; if (age <= 16) { discountBasedOnAge = 0.35 * result; } else if (age >= 60) { discountBasedOnAge = 0.2 * result; }
 
- Start by trying to extract this piece of code : 
Shortcuts
Extract method :
| IntelliJ | Eclipse | 
|---|---|
| Ctrl+Alt+M | Alt+Shift+M | 
| ⌘+⌥+M | ⌥+⌘+M | 
Benefits
- More readable code
- Avoid code duplication
Extract variable
Code Smells
- Hard to understand expressions
Technique
- Place the result of the expression or its parts in separate variables that are self-explanatory.
Practice
- Open Foodincomposing.methodspackage
- Extract variables from the isEdiblemethod
@Builder
public class Food {
    private final LocalDate expirationDate;
    private final Boolean approvedForConsumption;
    private final Integer inspectorId;
    public Food(LocalDate expirationDate, Boolean approvedForConsumption, Integer inspectorId) {
        this.expirationDate = expirationDate;
        this.approvedForConsumption = approvedForConsumption;
        this.inspectorId = inspectorId;
    }
    public boolean isEdible() {
        if (this.expirationDate.isAfter(LocalDate.now()) && this.approvedForConsumption == true && this.inspectorId != null) {
            return true;
        } else {
            return false;
        }
    }
}
Shortcuts
Extract Variable :
| IntelliJ | Eclipse | 
|---|---|
| Ctrl+Alt+V | Alt+Shift+L | 
| ⌘+⌥+V | ⌥+⌘+L | 
Benefits
More readable code
Drawbacks
- More variables
- Performance - Expressions will always be called
 
Inline Temp
Code Smells
- Temporary variable assigning the result of a simple expression - And nothing more
 
Technique
- Replace the references to the variable with the expression itself
Practice 1
- Open OrderHelperincomposing.methodspackage
- Replace the price reference with the expression itself
Practice 2
- Refactor the deserveDiscountBasedOnCustomerby using previous learnings
public class OrderHelper {
    private static final int MINIMUM_ITEMS_IN_STOCK = 10;
    public static boolean deserveDiscount(Order order) {
        double price = order.totalPrice();
        return price > 1000;
    }
    // Customer deserves a discount if customer age / number of products < 5
    public static boolean deserveDiscountBasedOnCustomer(Order order) {
        double nbOfProducts = order.getProducts().size();
        int customerAge = order.getCustomer().getAge();
        int result = (int) (customerAge / nbOfProducts);
        return result < 5;
    }
    public static int calculateNewStock(Stock stock, int outFromStock) {
        stock.setNbOfItems(stock.getNbOfItems() - outFromStock);
        if (stock.getNbOfItems() < MINIMUM_ITEMS_IN_STOCK) {
            return stock.getNbOfItems() + MINIMUM_ITEMS_IN_STOCK;
        }
        return stock.getNbOfItems();
    }
}
Faker library (or alternatives) can really help you save a lot of time when needing data for your tests.
Benefits
- Less code
- Less noise
Remove Assignments to Parameters
Code Smells
- A value is assigned to a parameter inside method’s body
Technique
- Use a local variable instead of a parameter.
Practice 1
- Open OrderHelperincomposing.methodspackage
- Reflect on the method - Its signature
- Which concept does it break ?
 
- Add a new test for the calculateNewStockmethod
Practice 2
- Refactor the calculateNewStockinto a Pure Function
Benefits
- Avoid side effects
- Create pure functions - Easier to maintain / test
 
Replace long method with Method Object (composition)
Code Smells
- Long methods : local variables are so intertwined that you can’t apply Extract Method
- Break often Single Responsibility Principle
Technique
- Transform the method into a separate class - Local variables become fields of the class
 
- Split the method into several methods within the same class
Practice
- Open Warehouseincomposing.methodspackage
- Extract the content of the generateStockReportmethod in aStockReportGeneratorclass
- Inject the Warehouse instance to it
- Refactor the code until you are happy with it
- What are the side effects on the consumers of the Warehouse class ?
@AllArgsConstructor
public class Warehouse {
    private final int id;
    private final LinkedHashMap<Product, Integer> stock;
    public String generateStockReport() {
        StringBuilder report = new StringBuilder();
        report.append("Report for warehouse : " + id + "%n");
        stock.forEach((key, value) -> report.append("Product: " + key.getName() + " Price: " + key.getPrice() + " Stock : " + value + " units%n"));
        report.append("Total: " + stock.entrySet().stream().map(kvp -> kvp.getKey().getPrice() * kvp.getValue()).reduce(0.0, Double::sum) + "€");
        return report.toString();
    }
}
Shortcuts
- With IntelliJ select the content of the generateStockReportmethod
- Go into your Refactor menu : Refactor | Replace Method with Method Object... 
- More info here
Benefits
- Stop a method from growing
Drawbacks
- Another class is added
- Increase the overall complexity of the program
 Skip to main content
   Skip to main content