Language/Java

[클린 코드] Ch.8 - 경계

비소_ 2022. 8. 15.

클릭하시면 네이버 Book으로 연결됩니다!

해당 서적을 참고하여 개인 공부용으로 정리한 글입니다.


외부 코드 사용하기

인터페이스 제공자는 적용성을 최대한 넓히려 애쓰고,

사용자는 자신의 요구에 집중하는 인터페이스를 바란다.

이것을 '경계에서의 긴장'이라한다.

 

예시로, Map 인터페이스의 다양한 기능 중 clear 메서드는 Map 사용자 누구나 내용을 지울 수 있게 한다.

또한, 어떤 객체 유형도 추가할 수 있다.

제네릭을 이용하면 유형을 지정함과 동시에 코드 가독성을 높일 수 있다.

하지만, Map 인스턴스를 여기저기 넘긴다면, Map이 변할 시 수정할 코드가 많아진다.

(물론 잘 변하지 않을꺼라 생각하지만, 제네릭이 추가될 때 Map이 변경된 것을 고려하면 절대는 없다.)

따라서, 다음 처럼 캡슐화할 경우 코드 가독성을 유지하면서 오용을 줄일 수 있다.

public class Sensors {
    // 경계 인터페이스를 클래스 안으로 숨김
    private Map sensors = new HashMap();
    
    public Sensor getById(String id) {
        return (Sensor)sensors.get(id);
    }
    ...
}

제네릭을 사용하던 안하던 이제 상관없다.

클래스 안에서 유형을 관리하므로, 사용자 입장에서는 생각할 필요가 없기 때문이다.

하지만, 모든 경계 인터페이스를 캡슐화하라는 것은 아니다.

경계 인터페이스를 여기저기 넘기지 말라는 뜻이다.

경계 인터페이스를 이용할 경우 클래스나 클래스 계열 밖으로 노출되지 않도록 주의한다.


경계 살피고 익히기

외부 라이브러리를 사용하려면 사용법을 익혀야 한다.

하지만, 외부 코드를 익히는 것은 어렵고 통합하기도 어렵다.

짐 뉴커크(Jim Newkirk)는 간단한 테스트 케이스를 먼저 작성해 외부 코드를 익히는 것을 '학습 테스트'라 부른다.

학습 테스트는 사용하려는 방식대로 외부 API를 호출해 제대로 이해했는지 확인한다.


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

학습 테스트는 투자하는 노력보다 얻는 성과가 더 크다.

  • API 이해도를 높여주는 정확한 실험
  • 패키지 검증 (변경 사항 발생 시 우리 코드와 호환되는지 바로 확인 가능)

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


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

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

원하는 모듈이 아직 구현되지 않았을 때, 그 모듈이 완성되길 기다리지 않고 인터페이스를 자체적으로 세움으로써 우리가 "원하는 기능"을 정의해놓고, 이를 토대로 우리 코드를 구현한다.

이로써 우리가 인터페이스를 전적으로 통제할 뿐만 아니라, 코드 가독성이 높아진다.

아래는 저자의 예시이다.

public interface Transimitter {
    public void transmit(SomeType frequency, OtherType stream);
}

public class FakeTransmitter implements Transimitter {
    public void transmit(SomeType frequency, OtherType stream) {
        // 실제 구현이 되기 전까지 더미 로직으로 대체
    }
}

// 경계 밖의 API
public class RealTransimitter {
    // 캡슐화된 구현
    ...
}

public class TransmitterAdapter extends RealTransimitter implements Transimitter {
    public void transmit(SomeType frequency, OtherType stream) {
        // RealTransimitter(외부 API)를 사용해 실제 로직을 여기에 구현.
        // Transmitter의 변경이 미치는 영향은 이 부분에 한정된다.
    }
}

public class CommunicationController {
    // Transmitter팀의 API가 제공되기 전에는 아래와 같이 사용한다.
    public void someMethod() {
        Transmitter transmitter = new FakeTransmitter();
        transmitter.transmit(someFrequency, someStream);
    }
    
    // Transmitter팀의 API가 제공되면 아래와 같이 사용한다.
    public void someMethod() {
        Transmitter transmitter = new TransmitterAdapter();
        transmitter.transmit(someFrequency, someStream);
    }
}

깨끗한 경계

경계에서는 많은 일들이 일어난다.

따라서 깔끔히 분리한다.

또한, 기대치를 정의하는 테스트 케이스도 작성한다.

통제가 불가능한 외부 패키지에 의존하는 대신 통제가 가능한 우리 코드에 의존하는 편이 훨씬 좋다.

 

댓글