해당 포스트는 "Effective Java" 책의 내용을 요약한 것이다.



※ 전략을 표현하고 싶을 때는 함수 객체를 사용해라

: 자바는 함수 포인터를 지원하지 않는다. C의 경우를 보면 qsort() 함수는 비교자 함수에 대한 포인터를 인자를 받는다. 자바에서 함수 포인터를 구현하고자 한다면 클래스 내부에 메서드 하나를 두어서 포인터 구실을 하게 만들면 된다. 이를 함수 객체라고 부른다. 다음의 예를 보자.

class StringLengthComparator{ public int compare(String s1, String s2){ return s1.length() - s2.length(); } }

위 클래스를 통해서 String을 길이 순서대로 정렬할 수 있다. 즉, 함수 포인터 역할을 한다. 위 예제를 싱글턴 패턴으로 구현하면 쓸데 없는 객체 생성 또한 피할 수 있다. 

class StringLengthComparator{
  private StringLengthComparator(){}
  public static final StringLengthComparator INSTANCE = new StringLengthComparator(); 
  public int compare(String s1, String s2){
      return s1.length() - s2.length();
  }
}

StringLengthComparator 객체를 메서드에 전달하기 위해서는 인자의 자료형이 맞아야 한다. 그리고 문자열을 비교하는 데 StringLengthComparator이 아닌 다른 방법으로 할 수도 있다. 따라서 전략 패턴을 활용하여 전략 인터페이스를 구현할 필요가 있다. 다음은 전략 인터페이스의 예이다.

public interface Comparator<T>{
   public int compare(T t1, T t2);
}

전략 인터페이스를 보면 제네릭으로 되어 있다. 따라서 String이 아닌 다른 객체에 대해서 비교하는 데 사용할 수 있다. 대신 위에서 구현한 StringLengthComparator을 다음과 같이 바꿔야 한다.

class StringLengthComparator implements Comparator<String>{
      ....
} 

만약 StringLengthComparator을 사용하는 일이 거의 없다면 익명 클래스로 구현하는 것도 좋은 방법 중 하나이다. 반복적으로 사용하게 된다면 private static 멤버 클래스로 전략을 표현한 다음, 전략 인터페이스가 자료형인 public static final 필드를 통해 외부에 공개하는 것도 좋다. 실제 String 클래스가 그렇게 구현되어 있다. 다음 예제를 보자.


class Host{
   private static class StrLenCmp implements Comparator<String>, Serializable{
     public int compare(String s1, String s2){
       return s1.length() - s2.length();
     }
   //이 비교자는 직렬화 가능
  public static final Comparator<String> STRING_LENGTH_COMPARATOR = new StrLenCmp();
}

+ Recent posts