개발하고 있던 서비스 기능 중, 여러가지 템플릿에 따라 전자문서를 생성하는 기능이 존재했습니다. 각각의 템플릿에 적용되는 변수가 상이하기 때문에, 이에 따른 validation, convertion 이 따로 진행되어야 했습니다.
다만, 여러가지 템플릿에 공통으로 적용되는 validation, convertion 또한 존재하였고, 이를 최대한 공통으로 처리하는 방향이 코드의 확장성, 반복 제거에 좋을 것 같다고 판단하였습니다.
단순 상속 구현
가장 처음 떠올린 방법은 부모 클래스에서 공통 validation, convertion 을 진행하는 방식으로, 다음과 같습니다.
public abstract class ParentValidator {
public void validate() {
System.out.println("ParentValidator.validate()");
}
}
public class FirstChildValidator extends ParentValidator {
@Override
public void validate() {
super.validate();
System.out.println("FirstChildValidator.validate()");
}
}
public class SecondChildValidator extends ParentValidator {
@Override
public void validate() {
super.validate();
System.out.println("SecondChildValidator.validate()");
}
}
public class Main {
public static void main(String[] args) {
final ParentValidator validator1 = new FirstChildValidator();
validator1.validate();
System.out.println("=====================================");
final ParentValidator validator2 = new SecondChildValidator();
validator2.validate();
}
}
output:
ParentValidator.validate()
FirstChildValidator.validate()
=====================================
ParentValidator.validate()
SecondChildValidator.validate()
자식 class 에서는 super 로 부모 메서드를 호출하여 공통 validation 을 수행하는 방식입니다. 쉽게 구현이 가능하고, 이해하기도 쉽습니다.
Problem
다만, 문제는 자식 클래스에서 super.validate() 를 명시적으로 호출해주어야 한다는 점입니다. 서비스가 고도화 됨에 따라 여러가지 템플릿 클래스가 추가될텐데, 이 때마다 개발자는 super.validate() 를 잊지 않고 호출해 주어야 하는 것입니다. 물론, 똑똑한 에디터가 이를 도와주겠지만, 여전히 실수가 발생할 수 있을 것 같았습니다.
Solution
그렇다면, 부모 class 가 알아서 common validation 을 진행한 뒤, 자식 클래스의 validation 메서드를 호출한다면, 해당 문제가 사라지지 않을까? 하는 생각이 들었습니다. 그리고, 이를 가능하게 하는 디자인 패턴이 바로 템플릿 메소드 패턴입니다.
뭔가, 익숙한 방식이지 않나요? 그렇습니다. 사실, 템플릿 메소드 패턴은 프레임워크에 매우 흔하게 사용되는 패턴이죠. 전체로직을 프레임워크가 주관하면서, 개발자가 overriding 한 메서드를 호출합니다.
템플릿 메소드 패턴에 대한 기본 원리를 한문장으로 표현하면 다음과 같습니다.
don't call us, we'll call you.
즉, 제어의 역전이 일어나고, 프레임워크가 자식 class 의 메서드를 호출하는 방식인 것이죠.
구조는 다음과 같습니다.
templateMethod() 에서 는 순차적으로 stepOne(), stepTwo(), setpThree() 로직을 호출합니다. 자식 class 에서는 필요한 method 를 overriding 해서 구현하면 되는 방식입니다.
템플릿 메소드 패턴 구현
이제 템플릿 메소드 패턴을 위 예제에 적용해 보겠습니다. Main 함수는 그대로 사용됩니다.
public abstract class ParentValidator {
public void validate() {
validateCommon();
validateTypeSpecific();
}
private void validateCommon() {
System.out.println("ParentValidator.validateCommon()");
}
abstract void validateTypeSpecific();
}
public class FirstChildValidator extends ParentValidator {
@Override
void validateTypeSpecific() {
System.out.println("FirstChildValidator.validateTypeSpecific()");
}
}
public class SecondChildValidator extends ParentValidator {
@Override
void validateTypeSpecific() {
System.out.println("SecondChildValidator.validateTypeSpecific()");
}
}
output:
ParentValidator.validateCommon()
FirstChildValidator.validateTypeSpecific()
=====================================
ParentValidator.validateCommon()
SecondChildValidator.validateTypeSpecific()
여기서 validate() 메서드가 위에서 소개한 템플릿 메서드 구조의 templateMethod() 에 해당하는 것이죠.
Java 에선 위와 같이 abstarct 메서드로 정의해 두면, 이를 상속한 class 에선 이를 반드시 구현해야 합니다. 이러한 방식으로, 공통되는 validation 로직의 반복을 제거하고, 자식 클래스를 추가하는 개발자에겐 type specific 한 validation 로직 구현을 강제할 수 있을 것 같습니다.