[리팩토링] 악취 17 : 메시지 체인

 

✍️ 악취 17 : 메시지 체인

객체에 메시지를 전달해서 기능을 수행한단 의미에서 '메시지란 메서드 호출'을 의미한다. 즉, 메시지 체인이란 연속된 메서드 호출을 의미한다.

// 메시지 체인
person.getInfo().getAddress();

 

메시지 체인이 리팩토링의 대상이 되는 이유는 클라이언트가 메서드 호출을 위해 너무 많은 것을 알아야 한다는 것이다.

가령 클라이언트는 Person에 Info 필드가 있고, Info에 Address 필드가 존재함을 이해해야 하만 원하는 정보를 얻을 수 있기 때문이다. 심지어 체인 구조가 변경되면 모든 클라언트의 코드가 변경된다는 단점도 존재한다.

 

여기 메시지 체인을 해결하기 위한 두 가지 리팩토링 기법이 있다.

1. "위임 숨기기메시지 체인을 캡슐화하기

2. "함수 추출하기, 함수 옮기기메시지 체인을 함수로 추출하고, 적절한 위치로 함수 옮기기

 

본 포스팅에서 관심 있게 살펴볼 리팩토링은 '위임 숨기기'이다.

 

🍊 위임 숨기기

메시지 체인은 위임 숨기기를 통해서 끊어낼 수 있다. 위임 숨기기는 일종의 캡슐화에 해당하는 방식으로, 클라이언트에게 최소한의 정보만을 제공하기 위해 사용된다.

* 캡슐화란 클래스의 필드와 메서드를 감싸서 최소한의 정보만을 제공하는 정보 은닉 개념이다. 클래스가 불필요하게 많은 정보를 외부에 제공하면 원하지 않은 방향으로 데이터가 조작될 수 있고, 작은 수정만으로도 클라이언트 코드의 많은 수정이 요구될 수 있다.

필드를 private으로 선언한다든지, 필드에 접근은 getter/setter로만 제어하는 방식이 캡슐화의 일종이다. 그뿐만 아
니라, 객체지향에서 캡슐화를 배울 때, 필드만 캡슐화의 대상인 듯 말하지만, 메서드 자체도 캡슐의 대상이 된다.

 

클라이언트가 Person의 Address를 알기 위해선 반드시 Info의 존재를 알아야한다.

// before
@AllArgsConstructor
@Getter
public class Person {

    private int personId;
    private Information info;
}


@AllArgsConstructor
@Getter
public class Information {

    private String name;
    private String address;
}

class PersonTest {

    @Test
    void test() {
        Person person = new Person(1, new Information("kang", "seoul"));
        String address = person.getInfo().getAddress();

        assertEquals("seoul", address);
    }
}

 

 

위임을 숨긴다면 클라이언트는 getAddress만 알아도 Address를 알 수 있다. 메시지 체인의 구조 변경과 같은 내부 구현이 변경되더라도 클라이언트 코드는 그대로 유지되는 장점도 있다.

// after
@AllArgsConstructor
public class Person {

    @Getter
    private int personId;
    private Information info;

    public String getAddress() {
        return info.getAddress();
    }
}

class PersonTest {

    @Test
    void test() {
        Person person = new Person(1, new Information("kang", "seoul"));
        String address = person.getAddress();

        assertEquals("seoul", address);
    }
}