8장 경계
시스템에 들어가는 모든 코드를 직접 개발하는 경우는 없다. 때로는 패키지나 오픈 소스를 활용한다. 혹은 다른 팀이 제공하는 컴포넌트를 사용하기도 한다. 이 장에서는 소프트웨어 경계를 깔끔하게 처리하는 기법을 살펴본다.
#
외부 코드 사용하기제공자는 적용성을 최대한 넓히려 애쓰고 사용자는 자신의 환경에 집중하는 인터페이스를 바란다. 이러한 이유로 시스템 경계에서 문제가 생길 소지가 많다.
예를 들어 Map
의 경우 다양한 인터페이스로 수많은 기능을 제공한다.
Map이 제공하는 다양한 기능성과 유연성을 유용하지만 그만큼 위험도 크다.
Map.clear()
메서드로 Map
사용자라면 누구나 내용을 지울 권한이 생기기 때문이다.
또 Map
은 객체 유형을 제한하지 않기 때문에 사용자는 어떤 객체 유형이라도 추가할 수 있다.
Map sensors = new HashMap()Sensor s = (Sensor)sensors.get(setsorId);
이 경우 아래와 같이 작성하면 코드 가독성이 높아진다.
Map<String, Sensor> sensors = new HashMap<sensor>();...Sensor s = sensors.get(sensorId);
하지만 위의 경우에도 Map<String, Sensor>
가 사용자에게 필요하지 않는 기능까지 제공하는 문제를 해결할 순 없다.
다음은 Map
을 좀 더 깔끔하게 사용한 코드다.
Sensors 사용자는 제네릭이 사용되었는지 여부를 신경 쓸 필요가 없다. 제네릭의 사용 여부는 Sensors 안에서 결정한다.
public class Sensors { private Map sensors = new HashMap();
public Sensor getById(String, id) { return (Sensor) sensors.get(id); } ...}
경계 인터페이스인 Map
을 Sensors 안으로 숨긴다. 따라서 Map
인터페이스가 변하더라도
나머지 프로그램에는 영향을 미치지 않는다. Sensors 클래스 안에서 객체 유형을 관리하기 때문이다.
Map
클래스를 사용할 때마다 위와 같이 캡슐화하라는 건 아니다. Map
과 같은
경계 인터페이스를 클래스나 클래스 계열 밖으로 노출되지 않도록 주의한다.
#
경계 살피고 익히기외부 코드를 사용하면 적은 시간에 더 많은 기능을 출시하기 쉬워진다. 하지만 외부에서 가져온 패키지를 사용한다면 사용할 코드를 테스트하는 편이 좋다. 우리쪽 코드를 작성해 외부 코드를 호출하는 대신 간단한 테스트 케이스를 작성해 외부 코드를 익히는 것을 '학습 테스트'라 부른다.
학습 테스트는 프로그램에서 사용하려는 방식대로 외부 API를 호출한다. 학습 테스트는 API를 사용하려는 목적에 초점을 맞춘다.
#
학습 테스트는 공짜 이상이다.학습 테스트는 이해도를 높여주는 정확한 실헙이다. 투자하는 노력보다 얻는 성과가 더 크다. 학습 테스트는 패키지가 예상대로 도는지 검증한다. 새 버전이 우리 코드와 호환되지 않으면 학습 테스트가 이를 밝혀낸다. 따라서 이런 경계 테스트가 있다면 새 버전의 패키지를 사용하기 쉬워진다. 그렇지 않다면 낡은 버전을 오래 사용하는 유혹에 빠지게 될 것이다.
#
깨끗한 경계소프트웨어 경계에서는 많은 일들이 발생한다. 변경이 대표적인 예다. 스프트웨어 설계가 우수하다면 변경하는 데 많은 투자와 재작업이 필요하지 않다. 통제하지 못하는 코드를 사용할 경우에는 향후 변경 비용이 커지지 않도록 주의한다.
경계에 위치하는 코드는 깔끔히 분리한다. 또한 테스트 케이스도 작성한다.
통제 불가능한 외부 패키지에 의존하는 대신 통제가 가능한 우리 코드에 의존하는 편이 훨씬 좋다.
외부 패키지를 호출하는 코드를 가능한 줄여 경계를 관리하자.
Map
에서 봤듯이, 새로운 클래스로 경계를 감싸거나 아니면 ADAPTER 패턴을 사용하여
우리가 원하는 인터페이스를 패키지가 제공하는 인터페이스로 변환하자.
어떤 방법이든 코드 가독성이 높아지며, 경계 인터페이스를 사용하는 일관성도 높아지며,
외부 패키지가 변했을 때 변경할 코드도 줄어든다.