Template Method Pattern의 확장

Template Method 패턴은 템플릿을 구성하는 메소드와 템플릿에서 호출하는 오버라이딩 가능한 훅 메소드(들)로 구성된다. 클래스 구조를 가지는 Template Method 패턴은 오브젝트 구조를 가지는 Strategy 패턴보다는 확장성이 떨어지는 것이 사실이다. 하지만 그 자체로 효과적인 확장을 하는 방법이 있다.

SpringMVC의 Controller 계층구조의 클래스들을 살펴보면 놀랍게도 정교하게 만들어진 Template Method 패턴의 계층적 확장을 살펴볼 수 있다. 하나의 단순한 훅 메소드가 하위 클래스에서는 새로운 템플릿으로 변신하면서 그 서브 템플릿으로부터 파생되는 새로운 훅 메소드들로 새로운 알고리즘의 흐름이 나타난다. 템플릿 메소드 패턴의 묘미는 바로 이런데 있지 않나 싶다.

AssertThrows에는 test()라는 훅 메소드가 존재한다. 이 메소드는 익셉션이 발생할 것으로 기대되는 코드를 넣는데 사용한다. 따라서 이를 확장하는 것은 좋은 방법이 아니다.

익셉션에 대해서 체크하는 책임을 맡은 checkExceptionExpectations() 메소드도 하나의 훅 메소드이다. AssertThrows의 템플릿 메소드인 runTest()에서 test()를 호출하고 익셉션이 발생하면 catch 블록 안에서 바로 이 checkExceptionExpectations를 호출한다. checkExceptionExpectations에서는 예상되는 exception과 실제로 발생한 exception을 비교해서 다르다면 테스트를 실패시킨다.

바로 이 메소드를 확장해서 새로운 템플릿으로 만들면 된다. 기존의 익셉션을 체크하는 AssertThrows의 메소드는 그대로 호출하고, 부가적인 익셉션 체크용 훅 메소드를 만들어서 확장가능하게 한다.

protected void check(T exception) throws AssertionFailedError {
}

@Override
protected void checkExceptionExpectations(Exception actualException) {
	super.checkExceptionExpectations(actualException);
	check((T)actualException);
}

 

이렇게 서브 클래스에서 훅 메소드를 확장해서 새로운 템플릿을 만드는 방식을 사용하는 것은 유용하다. 부가적인 익셉션 체크 로직은 이 check(T exception)을 오버라이드 해서 만들면 된다.  여기서는 Generics를 적용했다, check메소드에서 캐스팅 하는 것보다는 클래스 정의시 타입 파라메터를 주고, 깔끔한 check(T) 메소드를 생성하는게 낫다고 생각된다. 그래봤자 제너릭 파라메터에서 정보를 얻을 수 없으니 expectedException 클래스 파라메터는 그대로 줘야 하겠지만.

 

하지만 이런 구조에는 또 다른 단점이 있다. 그것은 매번 부가적인 체크 로직을 모두 구현해야 하기 때문이다. 재 사용 가능한 체크 로직을 만들어두고 사용하려면 역시 서브클래싱을 해야 한다. JdbcTemplate처럼 기본적으로는 callback/template 방식의 번거로운 구조를 사용하지만, 자주 사용되는 패턴에 대해서 편리한 메소드(convenient method)를 만들어서 손쉽게 사용하는 방법을 만들기가 힘들다.

 

그래서 GoF의 패턴의 기본 원리에 보면 "상속보다는 조합(composition)"이라고 했던가. 이번에는 확장가능한 템플릿을 별도의 인터페이스를 구현한 협력객체를 조합해서 사용하는 방식으로 다시 구성을 해봤다.

 

Special Strategy Pattern(Callback/Template) 적용

스프링의 xxxTemplate류의 클래스들은 모두 단일 메소드를 가진 인터페이스를 이용한 특별한 전략패턴을 이용한다. 특별하기 보다는 사실 단순한 것이다. 자바의 익명내부클래스를 이용한 pluggable behaviour의 적용에 가장 자주 사용된다. 이 경우 사전에 자주 사용되는 callback들을 미리 템플릿 클래스에 적용해 놓고, 구지 매번 callback을 만들지 않고도 단순한 메소드 호출을 통해서 사용할 수 있게 해주는 방법을 사용한다. JdbcTemplate에서는 사실 이런 편리한 메소드만을 이용해서 익명내부클래스의 구현이 들어가는 정신없는 코드없이도 왠만한 것들은 개발이 가능하도록 설계되어있다. 이 것이 템플릿 메소드 패턴보다 나은 strategy pattern의 독특한 장점이다. 상속보다는 (다양한 다이나믹 협력객체의 구현을 통한) 조합이 확장에 훨씬 뛰어나기 때문이다.

 

하지만 AssertThrows는 이미 템플릿 패턴의 틀을 가지고 만들어져있다. Stateless방식으로 사용해서 최적의 성능을 발휘할 수 있도록 만들어진 JdbcTemplate류와는 다르게 매번 검증할 익셉션이 익명내부클래스 생성시 결정된다. AssertThrows를 무시하고 새롭게 설계하는 방법도 있겠지만, (귀찮은 관계로…) 이번엔 그냥 템플릿 메소드 패턴을 가진 기본 구조를 유지한 채로 추가 로직을 검증하는 부분을 callback형태로 넣도록 한다.

먼저 Callback이 구현할 인터페이스를 만든다.

public interface ExceptionCheck<T extends Exception> {
    void check(T actualException) throws AssertionFailedError;
}

 

이미 체크할 익셉션 타입을 멤버 변수로 가지고 있으니, 이 callback도 생성자에서 설정해서 저장하도록 한다.

private ExceptionCheck<T> exceptionCheck;
...
public AssertThrowsTemplate(Class<T> expectedException, ExceptionCheck<T> exceptionCheck) {
	super(expectedException);
	this.exceptionCheck = exceptionCheck;
}

 

이렇게 삽입된 ExceptionCheck 인터페이스를 구현한 callback을 위의 checkExceptionExpectations에서 확인하고 설정이 되어있다면, 이를 호출하여 부가 체크 로직을 수행한다.

protected void checkExceptionExpectations(Exception actualException) {
	super.checkExceptionExpectations(actualException);
	if (exceptionCheck != null) exceptionCheck.check((T)actualException);
}

 

이렇게 전략패턴을 사용하면 AssertThrowsTemplate를 부차적으로 확장하지 않고, 대신 ExceptionCheck를 구현한 재활용 가능한 체크 로직을 활용하거나, 이를 AssertThrowsTemplate 내부에서 미리 정의해놓고 편리한 메소드 방식으로 호출해서 사용할 수 있다. 따라서 사용방법은 위의 템플릿 메소드를 확장한 것과 유사하지만 훨씬 유연하다.

 

이쯤에서 자주 사용되는 익셉션 체크 로직, 예를 들면 코드 확인이나 메시지 비교 등을 적용할 편리한 메소드를 만들어 적용해서 사용할 수 있다. 예를 들어 MessageExceptionCheck라는 구현을 만들고 이를 이용해서

new AssertThrowsTemplate(XXXException.class, new MessageExceptionCheck(“expectedmessage”)) { 
… 
}.runTest();

처럼 사용할 수 있다.

 

이쯤하면 매우 유연하게 다양한 검증 로직을 다이나믹하게 적용하면서도 AssertThrows의 깔끔한 검증 구조를 사용할 수 있다고 생각했지만… 사실 여기서 만족하기는 이르다.

 

Spring Expression Langauge를 이용한 확장

이번에는 한단계 더 나아가서 번거롭게 체크 로직을 전략패턴을 써서 구현하지 말고, 좀 더 간결한 체크 로직설정으로 만드는 방법을 생각해보기로 했다.

이때 떠오른 것이 바로 Spring 3.0에서 새롭게 소개된 Spring Expression Langauge – SPEL이다. 이미 공개된 다양한 EL이 있는데 왜 구지 스프링팀은 직접 EL을 만들었을까? 특정 영역에서 사용되는 EL, 예를 들면 JSP2.x의 EL처럼 UI의 출력 같은 특별한 목적을 가지는 것 말고, 범용적으로 매우 다양한 경우에 강력한 기능을 가지고 사용되어질 EL이 필요했기 때문일 것이다.

특히 Spring 3.0 EL은 애노테이션 설정과 매우 잘 어울린다. 내가 처음 본 SPEL의 활용예는 바로 Spring Security에 적용된 것이다. 이전에 프로그램을 이용한(programmatic) 방식으로만 가능했던 상세한 조건을 가진 보안설정을 SPEL을 이용해서 만들어지고 있는 Spring Security의 새 버전에서는 간단한 애노테이션 설정에 EL을 파라메터로 넘기는 것으로 끝난다.

 

바로 이 EL을 활용해보자. 아직 문서도 없고, 활용 예도 공개된 것이 거의 없기는 하지만 expression모듈의 테스트 코드를 최대한 분석해보면 그 사용 시나리오에 대해서 대략 짐작할만 하다.

 

최종적으로 만들고 싶은 것은 구지 체크를 위한 callback을 만들지 않고 다음과 같은 EL설정으로 부가적인 익셉션 체크 로직을 적용하는 것이다. 처음 M잡지에 실린 try/catch를 이용한 예제 테스트코드를 그대로 AssertThrowsTemplate를 이용해서 구현하면 다음과 같이 가능하다.

new AssertThrowsTemplate<ApplicationException>(ApplicationException.class, "예외타입 오류입니다") {
    @Check("getMessageCode() == T(BusinessCalendarMessageConstants).BIZ_CAL_DATE_NULL", "유효하지 않은 에러코드입니다")
    public void test() throws Exception {
        target = this.businessCalendar.getWorkingDateByTime(null, 11);
    }
}.runTest();

 

여기서 Check 애노테이션의 첫번째 파라메터로 사용된 것이 SPEL이다. SPEL은 매우 강력해서 제법 복잡한 체크 로직을 EL만으로 만들 수 있다.

 

구현내용은 다음 포스팅으로…

Related posts:

  1. Spring 3.0 EL (Spel)을 이용한 AssertThrows 확장 (3)
  2. Spring 3.0 EL (Spel)을 이용한 AssertThrows 확장 (1)
  3. Spring 3.0 (59) 프로퍼티 파일 이용하기 – placeholder vs SpEL
  4. Spring Expressions(SpEL)를 이용한 Mockito Argument Matcher 만들기
  5. Spring 3.0 (35) Spring 3.0 Reference Document 공개
  6. Spring 3.0 (28) R-669 Update
  7. Spring 3.0 (4) – Maven에서 Spring 3.0 최신버전 사용하기
  8. 미리 보는 Spring 3.0.1의 변경사항
  9. Spring 3.0 (44) Spring 3.0과 JEE6
  10. Spring 3.0 (38) Spring Reference 업데이트
  11. Spring 3.0 (26) Spring Expression Language와 @Value
  12. Spring 3.0 (2) R-518 스프링의 새 모듈 OXM(Object/XML Mapping)
  13. Spring 3.0 @MVC 메소드에서 자동으로 리턴 모델에 추가되는 것들
  14. Spring 상식퀴즈 (1) – DI 태클하기
  15. Spring 3.0 (58) Static Class를 XML없이 빈으로 등록하기

Facebook comments:

to “Spring 3.0 EL (Spel)을 이용한 AssertThrows 확장 (2)”

  1. QxOMbP newmjqpmotrx, [url=http://jjscsxbopdiu.com/]jjscsxbopdiu[/url], [link=http://wuokkfalntet.com/]wuokkfalntet[/link], http://mromybjnvmom.com/

  2. JBL5q0 onpzikfogcjl, [url=http://zkaiuwmngckb.com/]zkaiuwmngckb[/url], [link=http://fjhchfpgahzs.com/]fjhchfpgahzs[/link], http://isqlxvfwlkbk.com/

  3. You could check here

  4. Look here
    [url=http://www.lieyee.com/membery/michael-korsenebro-sandaliaa6w.html]bolsas michael kors[/url]
    bolsas michael kors

  5. nice articles

  6. You Could Try HERE
    [url=http://www.cruiseenglish.com/member/listshopt.php?/air-jordan-3-nice-kicks-are-a-piece-of-outstanding-art-works.html]air jordan 3 nice kicks-Are a piece of outstanding art works[/url]
    air jordan 3 nice kicks-Are a piece of outstanding art works

  7. thanks for share!

  8. Check HERE
    [url=http://ailiannie.com/member/loadingk.php?/burberry-london-lotionburberryfor-all.html]for all[/url]
    for all

  9. Click Here To Investigate
    [url=http://bootsforwomenoutlet.com]uggs on sale for women[/url]
    uggs on sale for women

  10. Click For MORE
    [url=http://baileybuttontriplett.com]ugg 1873[/url]
    ugg 1873

Leave a Reply

(required)

(required)

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

© 2017 Toby's Epril Suffusion theme by Sayontan Sinha