경계

시스템에 들어가는 모든 소프트웨어를 직접 개발하는 경우는 드물다.

어떤 식으로든 외부 코드를 우리 코드에 깔끔하게 통합해야만 한다.

1. 외부코드 사용하기.

인터페이스 제공자와 인터페이스 사용자 사이에는 특유의 긴장이 존재한다.

사용자는 자신의 요구에 집중하는 인터페이스르 바라지만 인터페이스 제공자는 적용성을 최대한 넓히려 애쓴다.

이러한 긴장으로 인해 시스템 경계에서 문제가 생길 소지가 많다.

그 예로 Java에서 제공하는 Map이 있다.

프로그램에서 Map을 만들어 여기저기 넘긴다고 가정해보자.

넘기는 쪽에서는 아무도 Map의 내용을 삭제하지 않으리라 믿을지도 모르지만 Map에서는 clear()라는 메서드를 제공한다.

즉, Map을 사용하는 누구나 Map의 내용을 지울 권한이 있다는 말이다.

Sensor라는 객체를 담는 Map을 만들려면 다음과 같이 Map을 생성한다.

Map sensors = new HashMap();

여기서 Sensor 객체를 가져오려면 다음곽 같이 해야한다.

Sensor s = (Sensor)sensors.get(sensorId);

Map이 반환하는 Object를 올바른 유형으로 변환하는 책임이 클라이언트에 있게된다.

하지만 이건 깨끗한 코드라고 보기는 어렵다.

대신 다음과 같이 제네릭스를 이용하면 코드의 가독성을 높일 수 있다.

Map<String, Sensor> sensors = new HashMap<Sensor>();

Sensor s = sensors.get(sensorId);

그렇지만 이 방법도 Map<String, Sensor>가 사용자에게 필요가지 않은 기능까지 제공한다는 문제는 해결하지 못한다.

프로그램에서 Map<String, Sensor> 객체를 이리저리 넘긴다면 Map 인터페이스가 변할 경우 수정할 코드가 상당히 많아진다.

다음은 Map을 좀 더 깔끔하게 사용한 코드이다.

public class Sensors {
    private Map sensors = new HashMap();

    public Sensor getById(String id) {
        return (Sensor)sensors.get(sensorId);
    }
}

경계 인터페이스인 Map을 Sensor안으로 숨긴 방법이다.

이렇게하면 Map 인터페이스가 변경되더라도 나머지 프로그램에는 영향을 미치지 않는다.

Map 클래스를 사용할 때마다 캡슐화하라는 뜻이 아닌 Map과 같은 경계 인터페이스를 여기저기 넘기지 말라는 뜻이다.

Map 인스턴스를 공개 api의 인수로 넘기거나 반환값으로 사용하지 않는다.

2. 경계 살피고 익히기.

외부 코드를 익히기는 어렵다.

외부 코드를 통합하기도 어렵다.

위의 두 가지를 동시에 하기에는 두 배나 어렵다.

대신 간단한 테스트 케이스를 작성해 외부 코드를 익히도록 한다. 이를 학습 테스트 라 부른다.

학습 테스트는 API를 사용하려는 목적에 초점을 맞춘다.

3. 학습 테스트는 공짜 이상이다.

학습 테스트에 드는 비용은 없다. 어쨋든 API를 학습해야 하기 때문이다.

오히려 필요한 지식만 확보하는 손쉬운 방법이다.

학습 테스트는 패키지가 예상대로 도는지 검증한다.

만약 새 버전이 우리 코드와 호환되지 않는다면 학습 테스트가 이 사실을 곧바로 밝혀낸다.

이런 경계 테스트가 있다면 패키지의 새 버전으로 이전하기 쉬워진다.

4. 아직 존재하지 않는 코드를 사용하기.

경계와 관련해 또 다른 유형은 아는 코드와 모르는 코드를 분리하는 경계이다.

다른 팀에서 진행하는 api가 필요한대 아직 정보를 모른다면 자체적으로 인터페이스를 정의해 사용하도록 한다.

그 후 다른 팀의 api가 정의된다면 ADAPTER 패턴으로 API를 캡슐화해 API가 바뀔 때 수정할 코드를 한곳으로 모아둔다.

이런 방식의 설계는 테스트도 아주 편하다.

5. 깨끗한 경계.

경계에서는 흥미로운 일이 많이 벌어진다. 그 대표적인 예로 변경이 있다.

소프트웨어 설계가 우수하다면 변경하는데 많은 투자와 재작업이 필요하지 않다.

통제하지 못하는 코드를 사용할 때는 너무 많은 투자를 하거나 향후 변경 비용이 지나치게 커지지 않도록 각별히 주의해야 한다.

외부 패키지에 의존하는 대신 우리 코드에 의존하는 편이 훨씬 좋다.

자칫하면 오히려 외부 코드에 휘둘리고 만다.

새로운 클래스를 만들어 감싸거나 ADAPTER 패턴을 이용해 우리가 원하는 인터페이스를 패키지가 제공하는 인터페이스로 변환하도록 하자.

Last updated