2014년 2월 11일 화요일

함수포인터를 대체하는 전략. Template & Command



자바에서는 특정 라이브러리를 쓰지 않는한 SE 자체에서 함수 포인터를 지원하지 않는다. 
왜냐하면 C++보다 한 수위의 캡슐화를 지향하는 자바는 커맨드를 함수 단위보다는 오브젝트 단위로 설계하는 것을 지지하기 때문이다. 그래서 절차지향에 익숙한 프로그래머는 함수포인터 조차도 오브젝트단으로 그림을 그려야하는 객체지향을 불편해하기도 한다. 

그러나 아주 작은 단위조차도 오브젝트로 구성되는 객체지향은 절차지향이 따라갈 수 없을 만큼 온 세계를 프로그래밍하기에 적합한 패러다임이다.

필자는 OOP와 패턴을 공부한 이후부터는 절차지향적 코드는 아예 쳐다보지도 않을 만큼(유지보수시에도 그러한 코드를 발견하면 모두 객체지향적으로 뜯어고친다) 이러한 패러다임에 매료되었다.

기본적인 구조가 잡혀있는 템플릿에서 핵심로직을 콜백 방식으로 호출하고 싶을 경우, 자바에서는 함수포인터대신, 오브젝트 포인터를 커맨드 패턴 개념을 도입하여 사용하면 된다. 오히려 펑션단으로 호출하는 명령보다 오브젝트단으로 호출하는 명령이기 때문에 그 확장성은 더욱 높다고 할 수 있겠다.

2
4
6
8
10

이런 숫자들이 적혀있는 test.txt가 있다고 하고, 이 숫자들을 합산해서 콘솔에 찍어주는 예제를 살펴보면

먼저 템플릿 구조는 이렇게 구성할 수 있다.

abstract public class CalulatorSupport {

public Integer fileReadTemplate(String fileLocation, BufferedReaderCallback callback) throws Exception{
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(fileLocation));
return callback.doSomethingWithReader(br);
catch (Exception e) {
throw e;
}finally{
if(br != null){
try {
br.close();
catch (Exception e2) {
// TODO: handle exception
}
}
}
}
public abstract Integer sum(String fileLocation) throws Exception;
}


이 템플릿 로직에서 사용할 커맨드 로직은 클래싱(슈퍼클래스에서 자식클래스로 오버라이딩해서 구현하는 방법등을 표현하는 IT 은어)해서 클로저 방식으로 만들어보면,

public class Calulator extends CalulatorSupport{

@Override
public Integer sum(String fileLocation) throws Exception {
BufferedReaderCallback callback = new BufferedReaderCallback() {
@Override
public Integer doSomethingWithReader(BufferedReader br) throws IOException{
Integer sum = 0;
String result = null;
while( (result=br.readLine()) != null ){
sum += Integer.valueOf(result);
}
return sum;
}
};
return fileReadTemplate(fileLocation, callback);
}

}

코드의 완벽한 해석을 위해선 파일을 읽고 쓰는 자바 스트림에 대해선 따로 학습이 필요하다.

일단 패턴에 관해선 CalulatorSupport를 클래싱한 Calulator에서 클로저 방식으로 BufferedReaderCallback형의 커맨드 객체를 뽑아내고 그 커맨드 객체를 다시 슈퍼클래스로 올리는 형태로써 Template Method Pattern의 전형적인 유형이다. 함수포인터 대신 커맨드의 개념으로 오브젝트 포인터를 만들어 올리기 때문에 Command Pattern도 흉내낸셈이 된다.


public interface BufferedReaderCallback {

public Integer doSomethingWithReader(BufferedReader br) throws IOException;

}



BufferedReaderCallback는 위와 같은 유형만 갖추고 있는 인터페이스이다.
사실 Template Method는 기본적으로 객체의 관계가 상속적 구조를 갖추게 됨으로 인해 상당한 문제점을 가지고 있다.

그래서 필자는 구조 자체가 상속이 아니라 합성을 통해서 관계를 맺는 Strategy Pattern을 더욱 즐겨 사용하는 편이다.

결국 프로그래밍은 구조와 논리의 문제다.









댓글 없음:

댓글 쓰기