[Clean Code] Ⅷ. 경계

Clean Code 3판을 읽고 정리한 글입니다

Ⅷ. 경계

모든 프로그램을 직접 개발하지 않음

  • 패키지, 오픈소스, 다른 사내의 컴포넌트등
  • 외부 코드와 우리 코드를 깔끔하게 통합하는 방법?

외부 코드 사용

인터페이스 제공자와 인터페이스 사용자의 긴장

  • 제공자
    • 패키지 제공자, 프레임워크 제공자
    • 적용성 최대한 넓히려 → 더 많은 환경에서 돌아가도록
  • 사용자
    – 자신의 요구에 집중하는 인터페이스를 원함

Java.util.Map

  • Map : 굉장히 다양한 인터페이스로 수많은 기능 제공
  • Map의 기능성/유연성은 유용하나 위험도 큼
  • Map을 여기저기 넘길때 Map.clear()로 누구나 내용 삭제 가능
  • 객체 유형 제한 없음 : 마음만 먹으면 어떤 객체든 추가 가능
    일반적인 Map 사용
    1
    2
    3
    4
    // Sensor 객체를 담는 Map
    Map sensors = new HashMap();
    // Sensor 가 필요하면
    Sensor s = (Sensor) sensors.get(sensorId);
  • 동작은 하나 클린 코드는 아님
    • Map의 리턴값인 Object의 cast 책임이 클라이언트에 있음
    • 코드의 의도가 들어나지 않음
Generics를 사용해서 가독성을 올린 예시
1
2
Map<Sensor> sensors = new HashMap<Sensor>();
Sensor s = sensors.get(sensorId);
  • 사용자에게 필요하지 않은 기능까지 제공하는 문제를 해결하지 못함
  • Map 인터페이스도 변함 (ex)자바5의 제네릭 추가등 : 이경우 수정코드가 많음
Map을 좀 더 깔끔하게 사용한 예시
1
2
3
4
5
6
7
8
9
public class Sensors {

private Map sensors = new HashMap();

public Sensor getById(String id) {
return (Sensor)sensors.get(id);
}
//이하 생략
}
  • 경계 인터페이스인 Map을 Sensors 안으로 숨김
    • Map 인터페이스가 변하더라도 나머지 프로그램에 영향 없음
    • 제네릭 사용 여부에 영향 X → Sensors 클래스 안에서 객체 유형 관리/변환
  • Sensors 클래스가 프로그램에 필요한 인터페이스만 제공
    • 코드 이해가 쉬우며 오용하기는 어려움
    • 나머지 사용 클라이언트에 설계 규칙/ 비지니스 규칙을 따르도록 강제 가능

Map(또는 유사한 경계 인터페이스를 )여기저기 넘기지 마라

  • 이용하는 클래스나 클래스 계열 밖으로 노출되지 않도록 주의
  • 공개 API 인자나 반환값으로 Map 인스턴스를 사용하면 안된다

경계 살피고 익히기

  • 외부 패키지 테스트는 우리 책임이 아니나 사용할 코드를 테스트 함이 바람직함
  • 외부 코드 익히기 어려움
    • 타사 라이브러리 문서 읽음으로사용법 결정
    • 코드 작성해서 라이브러리 예쌍 작동 확인
    • 디버깅 문제 : 우리 코드 버그 or 라이브러리 버그?
      학습 테스트
  • 간단한 테스트 케이스를 작성해서 외부 코드를 익히는 방법
  • 프로그램에서 사용하는 방식대로 외부 API 호출
  • 통제된 환경에서 API를 제대로 이해하는가 확인
  • API 사용목적 초점

log4j 익히기

  • 학습 테스트

  • 지금까지 간단한 콘솔 로거 초기화 방법 익힘

  • 이제 모든 지식을 독자적 로거 클래스로 캡슐화

  • 나머지 프로그램은 log4j 경계 인터페이스를 몰라도 사용 가능

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

  • 학습테스트는 이해도를 높여주는 정확한 실험
  • 투자노력보다 얻는 성과가 더 큼
  • 패키지 새 버전 나오면 학습 테스트를 돌려서 차이가 있는지 검증 가능
  • 이런 경계 테스트가 있으면 패키지 새 버전 이전이 쉬어짐
  • 아니면 필요이상 낡은 버전을 오랫동안 사용하려는 유혹에 빠지기 쉬움

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

경게의 또 다른 유형 : 아는코드와 모르는 코드를 분리하는 경계
예시)

  • 상황
    • sw에 ‘송신기’가 필요
    • 송신기 시스템 책임진 사람들은 인터페이스도 정의하지 못한 상태
    • 프로젝트 지연 없게 하기 위해 송신기와 먼 부분부터 작업
    • 작업 부분과 송신기 시스템 부분의 경계를 부딫치게 되며 기능정의를 하게 되었음
      • 지정한 주파수를 이용해 이 스트림에서 들어오는 자료를 아날로그 신호로 전송하라

    • 아직 API설계가 되지 않으므로 구체적인 방법은 모름 → 구현을 나중으로
    • 자체적 인터페이스 정의
      • Transmitter 인터페이스 클래스 정의
      • transmit 메서드 추가
      • 주파수와 자료스트림을 입력받음
  • 바라는 인터페이스 구현 → 인터페이스 전적 통제한다는 장점 → 코드 가독성과 분명한 코드 의도 반영
  • (통제할수도 없고 정의되지도 않은) 송신기 API에서 Communication Controller 분리
    • 필요한 인터페이스 정의 했으므로 Communication Controller는 깔끔 / 깨끗
    • 실제 송신기 API가 정의된 뒤에는 Transmitter Adapter 구현으로 간극을 매운다
    • ADAPTER 패턴 사용 → API 사용 캡슐화 → API 수정 시 수정될 코드를 한 곳으로
    • 테스트도 간편 : 적절한 Fake Transmitter 사용 → Communication Controller 테스트 가능
  • Transmitter API 인터페이스가 나온다음에 경계 테스트 생성해서 올바로 API 사용하는지도 테스트 가능

깨끗한 경계

경계의 변경

  • sw 개발설계가 우수하다면? 변경시 많은 투자와 재작업이 필요하지 않음
  • 통제 못하는 코드 사용시 너무 많은 투자나 향후 변경 비용이 너무 커지지 않도록 각별히 유의

경계 코드는 깔끔히 분리

  • 기대치 정의 테스트 ㅂ작성
  • 외부 호출한 코드를 가능한 줄여 경계를 관리하자
    • Map의 예처럼 새로운 클래스로 경계를 감싸거나
    • ADAPTER 패턴을 사용해 우리가 원하는 인터페이스를 패키지가 제공하는 인터페이스로 변환하자
    • 어떤 방법이든 가독성향상, 경계 인터페이스 사용 일관성 향상, 패키지 변경시 변경 코드 감소

Related POST

공유하기