본문 바로가기
독서에서 한걸음

Temporary Field

by oncerun 2022. 12. 24.
반응형

 

클래스에 있는 어떤 필드가 특정한 경우에만 값을 갖는 경우

 

어떤 객체의 필드가 특정한 경우에만 값을 가진다는 것을 이해하는 것은 일반적으로 예상하지 못하기 때문에 이해하기 어렵다. 

 

보통 클래스 추출하기를 통해 해당 변수를 옮길 수 있다, 혹은 함수 옮기기를 사용해서 해당 변수를 사용하는 함수를 특정 클래스로 옮길 수 있다. 

 

Introduce Special Case라는 특이 케이스 추가하기를 적용해 특정한 경우 해당하는 클래스를 만들어 해당 조건을 제거할 수도 있다. 

 

Introduce Special Case

 

특이 케이스 추가하기. 

 

어떤 필드에 특정한 경우에만 값이 설정되는 경우 해당 특정한 값에 따라 동일하게 동작하는 코드가 반복적으로 나타난다면, 해당 필드를 감싸는 특별 케이스를 만들어 해당 조건을 표현할 수 있다.

 

이러한 메커니즘을 특이 케이스 패턴이라고 부르고 Null Object 패턴을 이러한 패턴의 특수한 형태라고 볼 수 있다.

 

예시를 보자.

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

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

    return customerName;
}

 

Customer의 이름을 알아내는 과정에서 "unknown"인 경우 점유자라는 별도의 값을 설정해주고 있다. 아닌 경우 고객 이름을 그대로 추출해서 리턴해준다.

 

 

비슷하게 이와 같은 코드가 다른 메서드에서도 반복된다.

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 UnknownCustomer extends Customer{

    public UnknownCustomer() {
        super("unknown", null, null);
    }
    
    @Override
    public String getName() {
        return "occupant";
    }

    public boolean isUnknown() {
        return true;
    }
}

 

Unknown일 경우 생성자로 설정되어야 하는 값을 지움으로써 생성 시 더욱 쉽게 만들어 줄 수 있다.

 

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

 

Site 클래스에서 Customer를 가져오고 있다. 이 경우 이름에 따라 생성되는 타입이 다르므로 site 코드를 다음과 같이 고치면 위와 같이 한 줄짜리 코드로 변경할 수 있다.

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

 

위 코드를 통해 나머지 두 메서드들도 unknown의 특이 케이스를 추가함에 따라 다음과 같이 변경될 수 있다.

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();
    }

}

 

혹은  PaymentHistory에 Null Object 패턴을 적용할 수 있다. 

 

Null을 대표하는 NullPaymentHistory를 만들어보자.

 

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

 

기존 클래스에 default 값을 설정하거나 null 대표하는 인스턴스를 만들어서 처리해도 된다.

 

이러한 리팩토링을 보고나면 단점만 보였던 상속이 리팩토링에서 상당히 많이 사용됨을 알게 되는데 매우 놀랍다. 

 

인터페이스를 통해 진행 했다면 더 복잡해졌을 것이다. 상속도 객체지향의 한 특징으로 다양하게 사용될 수 있다는 인사이트를 얻었다

반응형

'독서에서 한걸음' 카테고리의 다른 글

Middle Man  (0) 2022.12.24
Message Chains  (0) 2022.12.24
Speculative Generality  (0) 2022.12.24
Lazy Element  (0) 2022.12.24
Repeated Switches  (0) 2022.12.22

댓글