Skip to main content Link Menu Expand (external link) Document Search Copy Copied

refactoring-journey

Table of contents
  1. Rename method
  2. Remove Dead Parameter
  3. Separate Query from Modifier
  4. Introduce Parameter Object
  5. Use Factory or Factory Methods

Rename method

Code Smells

  • The name of a method does not explain what the method does

Technique

  • Use the “Rename” functionality of your IDE
  • It will fix each calls for you

Practice

  • Open Employee in simplifying.method.calls package
  • Rename every method with a “shitty” name
public class Employee {
    private final String id;
    private final String name;
    private final String role;
    private final String currentProject;
    private final List<String> skills;

    public Employee(String name, String role, String currentProject, List<String> basicSkills) {
        this.id = UUID.randomUUID().toString();
        this.name = name;
        this.role = role;
        this.currentProject = currentProject;
        this.skills = basicSkills;
    }

    public String get() {
        return id;
    }

    public String getN() {
        return name;
    }

    public String getR() {
        return role;
    }

    public String getP() {
        return currentProject;
    }

    public boolean isProfessionalService() {
        return !getR().equals("Assoc");
    }

    public boolean isIdeal() {
        return getP().equals("Beach") || getP().isEmpty();
    }

    public boolean hasSomething(String skill) {
        return this.skills.contains(skill);
    }
}

Shortcuts

Rename : can be used to rename anything

IntelliJ Eclipse
Shift+F6 Alt+Shift+R
⇧+F6 ⌥+⌘+R

Benefits

  • Improved code readability

Drawbacks

  • N/A

Remove Dead Parameter

Code Smells

  • A parameter isn’t used in the body of a method
  • Every parameter in a method call forces the programmer reading it to figure out what information is in this parameter
  • Additional parameters are extra code that has to be run

Technique

  • Use the “Rename” functionality of your IDE
  • It will fix each calls for you

Practice

  • Open Lottery in simplifying.method.calls package
  • Remove safely every Dead parameter

Don’t forget to clean the tests as well

public class Lottery {
    private static final Random RANDOM = new Random(42);
    private final HashMap<UUID, LotteryTicket> tickets = new HashMap<>();

    public String purchaseTicketForCustomer(UUID id, String name) {
        String ticketNumber = generateTicketNumber(null);
        tickets.put(id, new LotteryTicket(ticketNumber, id));

        return ticketNumber;
    }

    public LotteryTicket drawWinner(double ticketPrice, double prizeAmount) {
        if (tickets.isEmpty()) {
            throw new IllegalStateException("No tickets");
        }

        List<LotteryTicket> randomizedTickets = new ArrayList<>(tickets.values());
        Collections.shuffle(randomizedTickets);

        return randomizedTickets.get(0);
    }

    private String generateTicketNumber(String format) {
        return String.format("%06d", RANDOM.nextInt(1000000));
    }
}

Shortcuts

  • Use Safe Delete feature in your IDE
IntelliJ
Alt+Delete
⌘+Delete

Benefits

  • A method contains only the parameters that it truly requires
  • More understandable code

Drawbacks

  • N/A

Separate Query from Modifier

Code Smells

  • Method that returns a value but also changes the internal state of an object
  • Have an and in your method names

Always remind this sentence : asking a question should not change the answer
To know more read about Command-query separation

Technique

  • Split the method into two separate methods
  • As you would expect :
    • One of them should return the value
    • The other one modifies the object

Practice

  • Open Client in simplifying.method.calls package
  • Apply the technique explained
public class Client {
    private final HashMap<String, Double> orderLines;
    @Getter
    private double totalAmount;

    public Client(HashMap<String, Double> orderLines) {
        this.orderLines = orderLines;
    }

    public String toStatement() {
        return orderLines.entrySet().stream()
                .map(entry -> formatLine(entry.getKey(), entry.getValue()))
                .collect(Collectors.joining("%n"))
                .concat("%nTotal : " + totalAmount + "€");
    }

    private String formatLine(String name, Double value) {
        totalAmount += value;
        return name + " for " + value + "€";
    }
}

Shortcuts

Extract method :

IntelliJ Eclipse
Ctrl+Alt+M Alt+Shift+M
⌘+⌥+M ⌥+⌘+M

Benefits

  • Avoid side effects in your programs

Drawbacks

  • N/A

Introduce Parameter Object

Code Smells

  • Methods contain a repeating group of parameters

Technique

  • Replace these parameters with an object
  • Create a new immutable class representing the group of parameters
  • In all method calls, pass the object created from old method parameters to this parameter

Practice

  • Open AccountingService in simplifying.method.calls package
  • Apply the technique explained
@AllArgsConstructor
public class AccountingService {
    private final List<Bill> bills;

    public ArrayList<Bill> findBillsInvoicedBetween(LocalDate from, LocalDate to) {
        return findBills(Bill::getInvoicedDate, from, to);
    }

    public ArrayList<Bill> findBillsDueBetween(LocalDate from, LocalDate to) {
        return findBills(Bill::getDueDate, from, to);
    }

    public ArrayList<Bill> findBillsPaidBetween(LocalDate from, LocalDate to) {
        return findBills(Bill::getPaymentDate, from, to);
    }

    private ArrayList<Bill> findBills(Function<Bill, LocalDate> dateSelector, LocalDate from, LocalDate to) {
        return bills.stream()
                .filter(bill -> isInRange(dateSelector.apply(bill), from, to))
                .collect(Collectors.toCollection(ArrayList::new));
    }

    private boolean isInRange(LocalDate dateToCheck, LocalDate from, LocalDate to) {
        return dateToCheck != null &&
                (dateToCheck.isAfter(from) || dateToCheck.isEqual(from)) && (dateToCheck.isBefore(to) || dateToCheck.isEqual(to));
    }
}

Shortcuts

  • Right Click on the Method name
  • Refactor | Introduce Parameter Object

introduce-parameter-object

  • Find & Replace in the current file :
IntelliJ
Ctrl+R
⌘+R

Benefits

  • Instead of a lot of parameters
    • You see a single object with a comprehensible name

Drawbacks

  • N/A

BONUS : Add a minimumAmount parameter to each find method and change the code accordingly

Use Factory or Factory Methods

Code Smells

  • Complex constructor that does something more than just setting parameter values in object fields

Technique

  • Create a factory method and use it to replace constructor calls.

Practice

  • Open Notification in simplifying.method.calls package
  • Create a Factory Method for Notification
public class Notification {
    private static List<String> authorizedChannels = List.of("SMS", "EMAIL", "PUSH");
    private final String channel;

    public Notification(String channel) {
        if (channel == null
                || channel.isEmpty()
                || !authorizedChannels.contains(channel)) {
            throw new IllegalArgumentException("Invalid channel provided");
        }
        this.channel = channel;
    }
}

Shortcuts

  • Select the constructor code
  • Use the menu Refactor | Replace Constructor With Factory Method.

Benefits

  • Force instantiation through Factory responsible for creation rules

Drawbacks

  • N/A

Journey proposed by Yoan Thirion #sharingiscaring