Skip to content

item 51 sijun

Sijun Noh edited this page Aug 28, 2020 · 1 revision

[아이템51] 메서드 시그니처를 신중히 설계하라

메서드 시그니처

메서드 시그니처(Method Signature): 메서드의 이름과 매개변수 리스트의 조합으로 메서드의 선언부의 일부분이다.

// 메서드 시그니처 -> doSomething(String name)
public static void doSomething(String name){

}

메서드 이름을 신중히 짓자

항상 표준 명명 규칙(아이템 68)을 따라야 한다.

이해할 수 있고, 같은 패키지에 속한 다른 이름들과 일관되게 지어야 한다.

개발자 커뮤니티에서 널리 받아들여지는 이름을 사용해야하며, 긴 이름을 피해야 한다.

애매하면 자바 라이브러리의 API 가이드를 참조하라. 자바 라이브러리가 워낙 방대하다 보니 일관되지 않은 이름도 제법 많지만 대부분은 납득할 만한 수준이다.

편의 메서드를 너무 많이 만들지 마라

모든 메서드는 각각 자신의 소임을 다해야 한다.

클래스나 인터페이스 모두 메서드가 많으면 익히고, 사용하고, 문서화하고, 테스트하고, 유지보수하기 어렵다.

클래스나 인터페이스는 자신의 각 기능을 완벽히 수행하는 메서드로 제공해야 한다. 아주 자주 쓰일 경우에만 별도의 약칭 메서드를 두기 바란다. 확신이 서지 않으면 만들지 말자.

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

매개변수는 4개 이하가 좋고, 같은 타입의 매개변수 여러 개가 연달아 나오면 실수로 순서를 바꾸는 경우가 발생하니 주의해야 한다.

과하게 긴 매개변수 목록을 줄이는 방법 3가지를 소개한다.

  1. 여러 메서드로 쪼갠다.

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

    직교성이 높다→공통점이 없는 기능들이 잘 분리되어 있다. 기능을 원자적으로 쪼개 제공한다.

    예를들어, List의 지정된 범위에서 인덱스를 찾는다고 가정하자.

    하나의 메서드로 구현하면, 지정된 범위의 (시작, 끝, 찾을 원소) 3가지 매개변수가 필요하다.

    하지만 subList 메서드와 indexOf 메서드를 사용하여 매개변수를 줄이며 직교성도 높일 수 있다.

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

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

    예를들어, 카드게임을 클래스로 만든다고 해보자. 그러면 메서드를 호출할 때 카드의 숫자(rank)와 무늬(suit)를 뜻하는 두 매개변수를 항상 같은 순서로 전달할 것이다. 따라서 이 둘을 묶는 도우미 클래스를 만들어 하나의 매개변수로 주고 받으면 API는 물론 클래스 내부 구현도 깔끔해질 것이다.

    public class CardGame {
    
    	...
    
    	static class Card{
    		int rank;
    		String suit;
    		
    		public Card(int rank, String suit) {
    			super();
    			this.rank = rank;
    			this.suit = suit;
    		}
    	}
    }
  3. 객체 생성에 사용한 빌더 패턴을 메서드 호출에 응용한다.

    앞선 두 기법을 혼합한 것으로, 객체 생성에 사용한 빌더 패턴을 메서드 호출에 응용한다고 보면 된다. 이 기법은 매개변수가 많을 때, 특히 그중 일부는 생략해도 괜찮을 때 도움이 된다.

    먼저 모든 매개변수를 하나의 객체로 정의하고, 클라이언트에서 이 객체의 세터 메서드를 호출해 필요한 값을 설정하게 하는 것이다. 필요한 매개변수를 다 설정한 다음, execute 메서드를 호출해 앞서 설정한 매개변수들의 유효성을 검사한다.

    public class NutritionFacts {
    
    	...
      private final int calories;
    	...
    
      public static class Builder {
    		...
        private int calories      = 0;
    		...
    
    	  public Builder calories(int val){
    			calories = val;      return this;
    		}
    		...
    			
    		public execute(Builder builder){
    			// 매개변수 유효성 검사하는 코드가 들어간다
    		}
    	  public NutritionFacts build() {
    			return new NutritionFacts(this);
    		}
    
        private NutritionFacts(Builder builder) {
    			calories     = builder.calories;
    			...
    		}
    }

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

매개변수로 적합한 인터페이스가 있다면 그 인터페이스를 직접 사용하자. 다양한 형태의 구현체도 인수로 건넬 수 있고, 심지어 존재하지 않는 구현체도 전달가능하다 (성능 좋은 새로운 구현체가 나오면 전달 가능).

// 하자
public void doSomethingGood(List<String> list) {	
}

// 하지말자
public void doSomethingBad(ArrayList<String> list) {	
}

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

열거 타입을 사용하면 코드를 읽고 쓰기가 더 쉬워진다.

예를들어, 화씨온도와 섭씨온도를 전달하여야 한다고 가정하자.

public enum TemperatureScale { FARENHEIT, CELSIUS }

Thermometer.newInstance(true) // 불명확하다
Thermometer.newInstance(Temperaturescale.CELSIUS) // 명확하다

두번째 코드 호출부만 보더라도 무엇이 전달되는지 명확하다.

또한, 추후에 KELVIN 온도가 추가 되더라도 열거 타입에만 추가하면 된다.

Clone this wiki locally