도당탕탕

Item51 : 메소드 시그니처를 신중히 설계하라 본문

JAVA

Item51 : 메소드 시그니처를 신중히 설계하라

backlo 2023. 1. 9. 15:24

이번 아이템에는 개별 아이템으로 두기 애매한 API 설계 요령들을 모아 놓았다. 다음을 보자.

1. 메서드 이름을 신중히 짓자.

항상 표준 명명 규칙을 따라야 한다. 이해하기 쉽고, 같은 패키지에 속한 다른 이름들과 일관되게 짓는 게 최우선 목표이다. 그다음 목표는 개발자 커뮤니티에서 널리 받아들여지는 이름을 사용하는 것이다.

긴 이름은 피하자. 애매하면 자바 라이브러리의 API 가이드를 참조하는 것이 좋다.

2. 편의 메서드를 너무 많이 만들지 말자.

모든 메서드는 각각 자신의 소임을 다해야 한다. 메서드가 너무 많은 클래스는 익히고, 사용하고, 문서화하고, 테스트하고, 유지보수 하는데 어렵다. 인터페이스도 마찬가지이다.

따라서 아주 자주 쓰일 경우에만 별도의 약칭 메서드를 두기 바란다. 확신이 서지 않으면 만들지 말자!

3. 매개변수 목록은 짧게 유지하자.

매개변수는 4개 이하가 좋다. 일단 4개가 넘어가면 매개변수를 전부 기억하기가 쉽지 않다. 특히 같은 타입의 매개변수 여러 개가 연달아 나오는 경우 해롭다. 사용자가 매개변수 순서를 기억하기 어려울뿐더러, 실수로 순서를 바꿔 입력해도 그대로 실행되어 의도와 다르게 동작한다.

따라서 과하게 긴 매개변수 목록을 짧게 줄여 주는 기술 3가지가 있다.

여러 매소드로 쪼갠다.

쪼개진 메서드 각각은 원래 매개변수 목록의 부분집합을 받는다. 잘못하면 메서드가 너무 많아질 수 있지만, 직교성을 높여 오히려 메소드 수를 줄여주는 효과도 있다. 예를 들면 다음과 같다.

  • List에서 지정된 범위의 부분리스트에서의 인덱스를 찾는 경우
    1. void searchRange (int startIdex, int endIndex, int value) <- 메소드가 길어짐
    2. void range(int statIndex, int endIndex), void search(int value) <- 이런 식으로 메소드 분리

이런식으로 분배하면 다른 곳에서도 유연하게 사용할 수 있어 강함과 유연함이 절묘하게 균형을 이룬 API가 만들어질 수 있다.

매개변수 여러 개를 묶어주는 도우미 클래스를 만드는 것이다.

일반적으로 이런 도우미 클래스는 정적 멤버 클래스로 둔다. 특히 잇따른 매개변수 몇 개를 독립된 하나의 개념으로 볼 수 있을 때 추천하는 기법이다.

예를 들면 카드 관련한 메서드를 호출할 때 숫자와 무늬를 뜻하는 두 매개변수를 도우미 클래스를 만들어 하나의 매개변수로 주고 받으면 API는 물론 클래스 내부 구현도 깔끔해 질 것이다.

객체 생성에 사용한 빌더 패턴을 메소드 호출에 응용하자.

매개변수가 많을 때, 특히 그중 일부는 생략해도 괜찮을 때 이 기법이 도움이 된다. 모든 매개변수를 하나로 추상화한 객체를 정의하고, 클라이언트에서 이 객체의 세터 메서드를 호출해 필요한 값을 설정하게 한다. 이때 각 세터 메소드는 매개변수 하나 혹은 서로 연관된 몇 개만 설정하게 한다.

예를 들어 클라이언트는 먼저 필요한 매개변수를 다 설정한 다음, execute 메소드를 호출해 앞서 설정한 매개변수들의 유효성을 검사한다. 마지막으로, 설정이 완료된 객체를 넘겨 원하는 계산을 수행한다.

4. 매개변수의 타입으로는 클래스보다는 인터페이스가 더 낫다.

매개변수로 적합한 인터페이스가 있다면 그 인터페이스를 직접 사용하자. 만약 클래스를 넘기면 클라이언트에게는 특정 구현체만 사용하도록 제한하는 꼴이 된다.

예를 들어 메서드에 HashMap을 넘기는 대신 Map을 넘기면 나중에 HashMap 뿐만 아니라 TreeMap, ConcurrentHashMap 등 어떠한 구현체도 넘길 수 있다.

5. boolean보다는 원소 2개짜리 열거 타입이 낫다.

열거 타입을 사용하면 코드를 읽고 쓰기가 더 쉬워진다. 그리고 나중에 선택지를 추가하기도 쉽다.

예를 들어 화씨온도, 섭씨온도를 원소로 정의한 열거타입이 있다고 하자.

  • 화씨 온도, 섭씨 온도 열거 타입
public enum TemperatureScale { FAHRENHEIT, CELSIUS }

// 사용
public test() {
  Thermometer.newInstance(true) // 이거보다는
  Thermometer.newInstance(TemperatureScale.CELSIUS) // 이 경우가 훨씬 명확히 알려준다.
}

또한 나중에 켈빈 온도를 지원해주어야 한다면, Thermometer에 또 다른 정적 메서드를 추가할 필요 없이 ThemperatureScale 열거 타입에 켈빈온도를 추가하면 된다.

또한 온도 단위에 대한 의존성을 개별 열거 타입 상수의 메소드 안으로 리팩터링해 넣을 수도 있다. 예컨대 double 값을 받아 섭씨온도로 변환해주는 메서드를 열거 타입 상수 각각에 정의해둘 수도 있다.

Comments