A method returns special code to indicate a wrong path. Throw an exception instead.
In computers, as in life, things go wrong occasionally, when things go wrong, you need to do something about it. In the simplest case, you can stop the program with an error code. When a routine finds an error, it needs to let its caller know, and the caller may pass the error up to the chain.
Example
Let’s consider the following class who try to withdraw an amount from user, and return true as success or false as failure at attemp.
package com.josdem.refactoring;
import java.math.BigDecimal;
public class TransactionApplier {
private AmountValidator amountValidator = new AmountValidator();
public Boolean subtractAmount(User user, BigDecimal amount) {
if(amountValidator.hasFunds(user, amount)){
user.setBalance(user.getBalance().subtract(amount));
return true;
}
return false;
}
}
TransactionApplier has a collaborator, AmountValidator who knows if user has funds or not. Here the code.
package com.josdem.refactoring;
import java.math.BigDecimal;
public class AmountValidator {
public Boolean hasFunds(User user, BigDecimal amount) {
return user.getBalance().compareTo(amount) >= 0;
}
}
Now refactor, first we change AmountValidator to throw an exception if user has no funds
package com.josdem.refactoring;
import java.math.BigDecimal;
public class AmountValidator {
public void hasFunds(User user, BigDecimal amount) {
if(user.getBalance().compareTo(amount) < 0){
throw new RuntimeException("No Sufficient Funds");
}
}
}
Now we are ready to delete boolean code from TransactionApplier
package com.josdem.refactoring;
import java.math.BigDecimal;
public class TransactionApplier {
private AmountValidator amountValidator = new AmountValidator();
public void subtractAmount(User user, BigDecimal amount) {
amountValidator.hasFunds(user, amount);
user.setBalance(user.getBalance().subtract(amount));
}
}
This is the test case to cover functionality
package com.josdem.refactoring;
import static org.junit.Assert.assertEquals;
import java.math.BigDecimal;
import org.junit.Test;
public class TestTransactionApplier {
private TransactionApplier transactionApplier = new TransactionApplier();
private BigDecimal userBalance = new BigDecimal(100);
@Test
public void shouldSubstractAmount() {
BigDecimal amount = new BigDecimal(20);
BigDecimal expectedBalance = new BigDecimal(80);
User user = setUserExpectations();
transactionApplier.subtractAmount(user, amount);
assertEquals(expectedBalance, user.getBalance());
}
@Test (expected=RuntimeException.class)
public void shouldNotSubtractAmountDueToNotSufficientFunds() {
BigDecimal amount = new BigDecimal(200);
User user = setUserExpectations();
transactionApplier.subtractAmount(user, amount);
assertEquals(userBalance, user.getBalance());
}
private User setUserExpectations() {
User user = new User();
user.setBalance(userBalance);
return user;
}
}
To download the project:
git clone https://github.com/josdem/refactoring.git
git fetch
git checkout feature/replace-error-code-with-exception-setup
git checkout feature/replace-error-code-with-exception-complete
To run the project:
gradle test
Return to the main article. Refactoring