해당 포스팅은 인프런 백기선님의 '리팩토링'을 학습 후 정리한 내용입니다.
public class Order {
public double discount(double inputValue, int quantity) {
if (inputValue > 50) inputValue = inputValue - 2;
if (quantity > 100) inputValue = inputValue - 1;
return inputValue;
}
}
매개변수인 inputValue 의 값을 그대로 활용한다
변수를 쪼개보자!
public class Order {
public double discount(double inputValue, int quantity) {
double result = inputValue;
if (inputValue > 50) result -= 2;
if (quantity > 100) result -= 1;
return result;
}
}
inputValue 를 변수화 하여 사용한다.
public class Billing {
private Customer customer;
private EmailGateway emailGateway;
public Billing(Customer customer, EmailGateway emailGateway) {
this.customer = customer;
this.emailGateway = emailGateway;
}
public double getTotalOutstandingAndSendBill() {
double result = customer.getInvoices().stream()
.map(Invoice::getAmount)
.reduce((double) 0, Double::sum);
sendBill();
return result;
}
private void sendBill() {
emailGateway.send(formatBill(customer));
}
private String formatBill(Customer customer) {
return "sending bill for " + customer.getName();
}
}
getTotalOutstandingAndSendBill 함수의 호출할 경우 sendBill() 불필요한 함수를 호출한다.
조회 함수와 명렴 함수를 분리하자!
public class Billing {
private Customer customer;
private EmailGateway emailGateway;
public Billing(Customer customer, EmailGateway emailGateway) {
this.customer = customer;
this.emailGateway = emailGateway;
}
public double getTotalOutstanding() {
return customer.getInvoices().stream()
.map(Invoice::getAmount)
.reduce((double) 0, Double::sum);
}
public void sendBill() {
emailGateway.send(formatBill(customer));
}
private String formatBill(Customer customer) {
return "sending bill for " + customer.getName();
}
}
getTotalOutstanding() 함수는 조회만하고 senBill() 함수는 따로 호출하여 명령과 조회를 분리한다.
• 세터를 제공한다는 것은 해당 필드가 변경될 수 있다는 것을 뜻한다.
• 객체 생성시 처음 설정된 값이 변경될 필요가 없다면 해당 값을 설정할 수 있는 생성자를 만들고 세터를 제거해서 변경될 수 있는 가능성을 제거해야 한다.
public class Person {
private String name;
private int id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
회원을 식별하는 필드인 id 는 변경을 하면 안되지만, setter 를 사용한다.
setter 메서드를 없앤다.
public class ProductionPlan {
private double production;
private List<Double> adjustments = new ArrayList<>();
public void applyAdjustment(double adjustment) {
this.adjustments.add(adjustment);
this.production += adjustment;
}
public double getProduction() {
return this.production;
}
}
applyAdjustment() 함수는 매개변수의 값을 list에 넣어줌과 동시에 총 합을 profuction 필드에 저장을 하는데, 이는 list 에서 총합을 알 수 있는 불필요한 변수이다.
production 필드를 제거 후 list 에 저장된 총합을 return 하도록 한다.
public class ProductionPlan {
private List<Double> adjustments = new ArrayList<>();
public void applyAdjustment(double adjustment) {
this.adjustments.add(adjustment);
}
public double getProduction() {
return adjustments.stream().mapToDouble(Double::valueOf).sum();
}
}
불필요한 production 필드 제거 후, getProduction 에서 총합을 계산한다.
객체 통쨰로 넘기기(preserve whole method) 와 같은 방식을 사용한다.
private TelephoneNumber officeTelephoneNumber;
public String getOfficeAreaCode() {
return this.officeTelephoneNumber.getAreaCode();
}
public void setOfficeAreaCode(String areaCode) {
this.officeTelephoneNumber.setAreaCode(areaCode);
}
public String getOfficeNumber() {
return this.officeTelephoneNumber.getNumber();
}
public void setOfficeNumber(String number) {
this.officeTelephoneNumber.setNumber(number);
}
TelephoneNumber 객체를 레퍼런스 객체가 아닌 값 객체로 사용할 경우 set
setter 를 통해 수정하지 않고 새로운 값 객체를 만들어준다.
public class Person {
private TelephoneNumber officeTelephoneNumber;
public String getOfficeAreaCode() {
return this.officeTelephoneNumber.getAreaCode();
}
public String getOfficeNumber() {
return this.officeTelephoneNumber.getNumber();
}
public void setOfficeAreaCode(String areaCode) {
this.officeTelephoneNumber = new TelephoneNumber(areaCode, this.getOfficeNumber());
}
public void setOfficeNumber(String number) {
this.officeTelephoneNumber = new TelephoneNumber(this.getOfficeAreaCode(), number);
}
}
public class TelephoneNumber {
private final String areaCode;
private final String number;
public TelephoneNumber(String areaCode, String number) {
this.areaCode = areaCode;
this.number = number;
}
public String getAreaCode() {
return areaCode;
}
public String getNumber() {
return number;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TelephoneNumber that = (TelephoneNumber) o;
return Objects.equals(areaCode, that.areaCode) && Objects.equals(number, that.number);
}
@Override
public int hashCode() {
return Objects.hash(areaCode, number);
}
}
값 객체이기 때문에 setter 를 없애고 equals, hashCode 메서드를 각 구현해준다.