Table of contents
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
insimplifying.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
insimplifying.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
insimplifying.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
insimplifying.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
- 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
insimplifying.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