[Refactoring] 임시 필드 (Temporary Field)

서준교·2023년 12월 24일
0

Refactoring

목록 보기
5/8
post-thumbnail

👉 이 게시글은 inflearn의 코딩으로 학습하는 리팩토링 강의 내용을 바탕으로 작성되었습니다.

클래스에 있는 필드가 특정한 경우에만 값을 갖는 경우 (예를 들어 null이나 empty), 이를 임시 필드라고 합니다. 이러한 필드가 항상 값이 존재하는 필드와 섞여 있다면 코드를 이해하기 어렵게 만듭니다. 이 글에서는 클래스 추가를 통해 임시 필드를 제거하는 리팩토링 기법에 대해 살펴보도록 하겠습니다.

특이 케이스 추가하기 (Introduce Special Case)

특정한 경우에 따라 필드의 값이 바뀌는 로직이 반복해서 등장한다면, 해당 필드를 감싸는 특별한 케이스를 만들어 해당 조건을 표현할 수 있습니다. 이러한 매커니즘을 특이 케이스 패턴이라고 부르며, Null Object 패턴을 이러한 패턴의 특수한 형태로 볼 수 있습니다.

Before Refactoring

리팩토링을 적용할 클래스는 아래와 같습니다.

public class Customer {
    private String name;
    private BillingPlan billingPlan;
    private PaymentHistory paymentHistory;
    public Customer(String name, BillingPlan billingPlan, PaymentHistory paymentHistory) {
        this.name = name;
        this.billingPlan = billingPlan;
        this.paymentHistory = paymentHistory;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public BillingPlan getBillingPlan() {
        return billingPlan;
    }
    public void setBillingPlan(BillingPlan billingPlan) {
        this.billingPlan = billingPlan;
    }
    public PaymentHistory getPaymentHistory() {
        return paymentHistory;
    }
    public void setPaymentHistory(PaymentHistory paymentHistory) {
        this.paymentHistory = paymentHistory;
    }
}
public class CustomerService {

    public String customerName(Site site) {
        Customer customer = site.getCustomer();

        String customerName;
        if (customer.getName().equals("unknown")) {
            customerName = "occupant";
        } else {
            customerName = customer.getName();
        }

        return customerName;
    }

    public BillingPlan billingPlan(Site site) {
        Customer customer = site.getCustomer();
        return customer.getName().equals("unknown") ? new BasicBillingPlan() : customer.getBillingPlan();
    }

    public int weeksDelinquent(Site site) {
        Customer customer = site.getCustomer();
        return customer.getName().equals("unknown") ? 0 : customer.getPaymentHistory().getWeeksDelinquentInLastYear();
    }

}
public class Site {
    private Customer customer;

    public Site(Customer customer) {
        this.customer = customer;
    }

    public Customer getCustomer() {
        return customer;
    }
}

After Refactoring

먼저, Customer 클래스에 unknown customer인지 아닌지 판별하기 위한 boolean type의 메소드를 추가하고 항상 false를 반환하도록 합니다.

public class Customer {
    private String name;
    private BillingPlan billingPlan;
    private PaymentHistory paymentHistory;
    public Customer(String name, BillingPlan billingPlan, PaymentHistory paymentHistory) {
        this.name = name;
        this.billingPlan = billingPlan;
        this.paymentHistory = paymentHistory;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public BillingPlan getBillingPlan() {
        return billingPlan;
    }
    public void setBillingPlan(BillingPlan billingPlan) {
        this.billingPlan = billingPlan;
    }
    public PaymentHistory getPaymentHistory() {
        return paymentHistory;
    }
    public void setPaymentHistory(PaymentHistory paymentHistory) {
        this.paymentHistory = paymentHistory;
    }
    public boolean isKnown() {
        return false;
    }
}

기존의 CustomerService 클래스는 customer의 name 필드가 unknown인 경우, 조건문을 통해 별도의 클래스를 생성하거나 값을 입력받는 로직을 포함하고 있습니다. 이제 이 코드를 unknown customer 로직을 처리하기 위한 클래스와 조건에 따라 처리할 로직을 포함하는 메소드를 아래와 같이 별도로 생성합니다.

또한, 상위 클래스인 Customer에서 선언했었던 isUnknown 메소드를 오버라이딩하여 항상 true를 반환하도록 합니다.

public class UnknownCustomer extends Customer {
    public UnknownCustomer() {
        super("unknown", new BasicBillingPlan(), new NullPaymentHistory());
    }

    @Override
    public boolean isUnknown() {
        return true;
    }

    @Override
    public String getName() {
        return "occupant";
    }
}

unknown customer인 경우 생성할 NullPaymentHistory 클래스는 다음과 같습니다.

public class NullPaymentHistory extends PaymentHistory {
    public NullPaymentHistory() {
        super(0);
    }
}

위에서 본 것과 같이, Site 클래스에 customer의 이름이 unknown일 경우 unknownCustomer 객체를 생성하는 로직을 추가합니다. 따라서 CustomerService 클래스에서는 Customer의 멤버변수를 getter를 통해 가져오는 코드만 남겨둘 수 있게 되어 훨씬 간결한 코드가 되었습니다.

public class Site {
    private Customer customer;

    public Site(Customer customer) {
        this.customer = customer.getName().equals("unknown") ? new UnknownCustomer() : customer;
    }

    public Customer getCustomer() {
        return customer;
    }
}
public class CustomerService {

    public String customerName(Site site) {
        return site.getCustomer().getName();
    }

    public BillingPlan billingPlan(Site site) {
        return site.getCustomer().getBillingPlan();
    }

    public int weeksDelinquent(Site site) {
        return site.getCustomer().getPaymentHistory().getWeeksDelinquentInLastYear();
    }

}
profile
매일 성장하는 개발자가 되고 싶습니다. 😊

0개의 댓글