티스토리 뷰
템플릿 콜백 패턴은 전략 패턴의 변형 패턴입니다.
예시 코드를 제시하며
전략 패턴 이전의 코드 -> 전략 패턴 코드 -> 템플릿 콜백 패턴 코드 의 변경점을 확인해보겠습니다.
전략 패턴 이전 코드 V1
class BeforeStrategyV1 {
void someMethod1() {
System.out.println("A")
System.out.println("이 부분이 핵심 1 메서드");
System.out.println("C");
}
void someMethod2() {
System.out.println("A");
System.out.println("이 부분이 핵심 2 메서드");
System.out.println("C");
}
}
해당 코드를 보면 someMethod에 공통되는 부분이 있고 그 사이에 핵심이 되는 로직이 있습니다.
이 부분을 따로 빼 본다면 아래와 같이 변경할 수 있습니다.
전략 패턴 이전 코드 V2
class BeforeStrategyV2 {
void someMethod1() {
a();
System.out.println("이 부분이 핵심 1 메서드");
c();
}
void someMethod2() {
a();
System.out.println("이 부분이 핵심 2 메서드");
c();
}
void a() {
System.out.println("A");
}
void c() {
System.out.println("C");
}
}
하지만 이 코드에서 여전히 문제가 남아있는데
만일 핵심 로직이 증가하게 된다면 someMethod 메서드도 증가할 것 입니다. 이는 OCP를 위반하게 됩니다.
이를 우리는 전략 패턴을 통해 해결할 수 있습니다.
전략 패턴(Strategy Pattern)
객체들이 할 수 있는 행위 각각에 대해 전략 클래스를 생성하고 유사한 행위들을 캡슐화 하는 인터페이스를 정의하여
객체의 행위를 동적으로 바꾸고 싶은 경우 직접 행위를 수정하지 않고 전략을 바꿔주기만 함으로써 행위를 유연하게 확장하는 방법
간단히 말해서 객체가 할 수 있는 행위들 각각을 전략으로 만들어 놓고
동적으로 행위의 수정이 필요한 경우 전략을 바꾸는 것만으로 행위의 수정이 가능하도록 만든 패턴입니다.
위의 코드를 전략 패턴을 적용하여 수정해보겠습니다.
class Strategy {
void someMethod(PrintMethod printMethod) {
a();
printMethod.print();
c();
}
void a() {
System.out.println("A");
}
void c() {
System.out.println("C");
}
}
interface PrintMethod {
void print();
}
class Method1 implements PrintMethod {
@Override
public void print() {
System.out.println("핵심 메서드 1");
}
}
class Method2 implements PrintMethod {
@Override
public void print() {
System.out.println("핵심 메서드 2");
}
}
public class Main {
public static void main(String[] args) {
Strategy strategy = new Strategy();
PrintMethod printMethod1 = new Method1();
PrintMethod printMethod2 = new Method2();
strategy.someMethod(printMethod1);
strategy.someMethod(printMethod2);
}
}
해당 코드를 보면 PrintMethod라는 인터페이스를 통해 핵심 로직의 구현체를 작성하고
클라이언트에서는 이를 동적으로 생성하여 호출할 수 있게 되었습니다.
핵심 로직이 증가할 때 마다 코드를 수정해야 하는 OCP 위반을 지켰지만
핵심 로직이 증가할 때 마다 코드를 추가해야 하는 번거로움은 여전히 남아있습니다.
이 부분은 템플릿 콜백 패턴을 통해 해결할 수 있습니다. 템플릿 콜백 패턴을 간단하게 설명하자면
변화되는 부분을 매번 객체로 생성하지 않고, 익명 내부 클래스 바로 생성하여 사용하는 방법입니다.
코드로 예시를 들어보겠습니다.
class TemplateCollback {
void someMethod(PrintMethod printMethod) {
a();
printMethod.print();
c();
}
void a() {
System.out.println("A");
}
void c() {
System.out.println("C");
}
}
interface PrintMethod {
void print();
}
public class Main {
public static void main(String[] args) {
TemplateCollback tc = new TemplateCollback();
tc.someMethod(()-> System.out.println("핵심 메서드 1"));
tc.someMethod(()-> System.out.println("핵심 메서드 2"));
}
}
클라이언트에서 익명 내부 클래스를 사용하여 인터페이스 구현체를 사용하고 있습니다.
이를 통해 매번 구현체 클래스를 생성하지 않아도 되는 편리함을 얻을 수 있었습니다.
템플릿과 콜백이라는 용어를 잠시 간단하게 알아보면 아래와 같습니다.
템플릿
어떤 목적을 두고 미리 만들어둔 모양의 틀
고정된 틀의 로직(반복되는 부분)을 갖는 템플릿 메소드를 슈퍼 클래스에 두고
바뀌는 부분을 서브 클래스의 메소드에 두는 구조로 두는 방식
콜백
실행되는 것을 목적으로 다른 오브젝트의 메소드에 전달되는 오브젝트를 의미
사실 템플릿 콜백 패턴을 알게 된 것은 JdbcTemplate이었습니다.
JdbcTemplate이 이를 사용하여 중복 코드를 줄여주는 방식을 사용한다고 하여 이에 대해 조사했습니다.
JdbcTemplate 내부의 메서드 중 하나를 보면
Connection이나 Statement 자원 생성 및 해제를 템플릿 콜백 패턴을 사용하여 구현했기 때문에
우리는 JdbcTemplate을 통해 중복 코드를 줄이면서 repository 클래스를 작성할 수 있는 것 입니다.
또한 스프링에서는 JdbcTemplate 말고도 RestTemplate , TransactionTemplate , RedisTemplate 등
다양한 템플릿 콜백 패턴을 사용합니다.