개발하는 두더지

[Effective Java 규칙21] 전략을 표현하고 싶을 때는 함수 객체를 사용하라 본문

Java,Android

[Effective Java 규칙21] 전략을 표현하고 싶을 때는 함수 객체를 사용하라

덜지 2018. 9. 28. 11:26

[Effective Java 규칙21] 전략을 표현하고 싶을 때는 함수 객체를 사용하라

Effective Java 2/E 책과 구글링을 통해 내용을 정리하고 개인적인 견해가 포함된 글입니다.



함수 포인터(function pointer), 대리자(delegate), 람다 표현식(lambda expression)은 파라미터 값으로 메서드를 전달하여 실행할 수 있다.

자바는 함수 포인터를 지원하지 않지만 비슷한 효과를 낼수있다. 


String[] fruits = {"pineapple", "banana", "apple"};
Arrays.sort(fruits, (c1, c2) -> {
return c1.length() - c2.length();
});

System.out.println(Arrays.toString(fruits));


sort 메서드에서 두번째 파라미터로 받는 람다 함수를 함수 객체라고 한다. 이 함수 객체는 문자열을 비교할 수 있는 실행 가능 전략이라고 한다. 그리고 이 패턴은 런타임에 사용할 알고리즘을 정하고 싶은 곳에서 유용하게 사용할 수 있다.


예를들면, 다른 포멧으로 파일을 저장하거나, 다양한 정렬 알고리즘을 실행하거나, 파일 압축등에 사용하는 것이다.


interface CompressionStrategy {
void compressFiles(ArrayList<File> files);
}

class ZipCompressionStrategy implements CompressionStrategy {
@Override
public void compressFiles(ArrayList<File> files) {
// zip 생성
}
}

class RarCompressionStrategy implements CompressionStrategy {
@Override
public void compressFiles(ArrayList<File> files) {
// rar 생성
}
}

class CompressionContext {
private CompressionStrategy strategy;
void setCompressionStrategy(CompressionStrategy strategy) {
this.strategy = strategy;
}

// 전략 사용
void createArchive(ArrayList<File> files) {
strategy.compressFiles(files);
}
}

class Client {
public static void main(String[] args) {
ArrayList<File> fileList = new ArrayList<>();
fileList.add(new File("file1.txt"));
fileList.add(new File("file2.txt"));

CompressionContext ctx = new CompressionContext();
// android preference에 저장되어있는 타입이라면 런타임마다 다른 전략으로 파일을 생성할 수 있다.
ctx.setCompressionStrategy(new ZipCompressionStrategy());
ctx.createArchive(fileList);
}
}


함수 객체의 주된 용도는 전략 패턴을 구현하기 위한 것인데, 자바로 이 패턴을 구현하기 위해서는 전략을 표현하는 인터페이스를 선언해야 하고 그 인터페이스를 구현한 구현체 클래스들을 만들어서 전략 클래스로 사용한다. 위 예제로보면 압축을 사용한다는 인터페이스 선언과 Zip압축, Rar압축 2가지 구현체 클래스를 만들어서 사용한다. 


전략이 한번만 사용되는 경우에는 보통 익명 클래스 객체로 구현한다. 하지만 반복적으로 사용된다면 객체가 계속 생성되게 되므로 private static 멤버 클래스로 만들고, public static final 필드를 통해서 외부에 공개하는 것이 좋은 방법이다.


아래는 전략 패턴이 여러번 사용될 것을 고려하여 private static 멤버 클래스로 만들고, public static final 필드를 통해서 외부에 공개하는 문자열을 비교하는 클래스 예제이다.


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

class Host {
private static class StrLenCmp implements Comparator<String>, Serializable {
@Override
public int compare(String t1, String t2) {
return t1.length() - t2.length();
}
}

public static final Comparator<String> STRING_LENGTH_COMPARATOR =
new StrLenCmp();
}



참고

Strategy Pattern Tutorial with Java Examples

Comments