[review] 템플릿 메서드 패턴과 콜백 패턴

corncheese·2023년 11월 2일
0

이 포스팅은 인프런 : 스프링 핵심 원리 - 고급편 을 참고하여 작성되었습니다.

우리가 작성하는 소스는 핵심 기능과 부가 기능으로 분류 할 수 있다.

  • 핵심 기능 : 해당 객체가 제공하는 고유기능
  • 부가 기능 : 핵심 기능을 보조하기 위해 제공되는 기능이다. 예를 들어 로그 추적 로직, 트랜잭션 기능으로 이러한 기능은 단독으로 사용되지 않고, 핵심 기능과 함께 사용된다.

이러한 핵심, 부가 기능을 분리하기 위한 디자인패턴으로
탬플릿 메서드 패턴(Templete Method Pattern)이 있다.

탬플릿 메서드 패턴(Templete Method Pattern)

템플릿 메서드 패턴은 이름 그대로 템플릿을 사용하는 방식이다.
템플릿에 변하지 않는 부분을 작성하며, 일부 변하는 부분을 호출하여 사용한다.

// 변하는 부분은 자식 클래스에 두고 상속과 오버라이딩을 사용해서 처리한다.

template1.execute() 를 호출하면, 템플릿 로직인 AbstractTemplate.execute()를 실행한다. 여기서 중간에 call() 메서드를 호출하는데, 이 부분이 오버라이딩 되어 있다.
따라서 SubClassLogic1 인스턴스의 SubClassLogic1.call() 메서드가 호출된다.

익명 내부 클래스 사용하기

템플릿 메서드 패턴은 위의 SubClassLogic1, 2처럼 클래스를 계속 만들어야 하는 단점이 있다. 익명 내부 클래스를 사용하면 이런 단점을 보완할 수 있다.
익명 내부 클래스를 사용하면 객체 인스턴스를 생성하면서 동시에 생성할 클래스를 상속 받은 자식 클래스에 정의할 수 있다.
이 클래스는 SubClassLogic1처럼 직접 지정하는 이름이 없고 클래스 내부에 선언되는 클래스라서 익명 내부 클래스라고 한다.

템플릿 메서드 패턴 - 정의

GOF 디자인 패턴에서는 템플릿 메서드 패턴을 다음과 같이 정의했다.
템플릿 메서드 디자인 패턴의 목적은, 작업에서 알고리즘의 골격을 정의하고 일부 단계를 하위 클래스로 연기합니다. 템플릿 메서드를 사용하면 하위 클래스가 알고리즘의 구조를 변경하지 않고도 특정 단계를 재정의할 수 있습니다.


풀어서 설명하면,
부모 클래스에 알고리즘의 골격인 템플릿을 정의하고,
일부 변경되는 로직은 자식 클래스에 정의하는 것이다. 이렇게 하면 자식 클래스가 알고리즘의 전체 구조를 변경하지 않고 특정 부분만 재정의 할 수 있다.
-> 상속과 오버라이딩을 통한 다형성으로 문제를 해결 하는 것!

하지만 이때 상속에서 오는 단점을 그대로 갖게 된다.
특히 자식 클래스가 부모 클래스와 컴파일 시점에 강하게 결합되는 문제가 있다. 이것은 의존관계에 대한 문제로, 자식클래스 입장에서는 부모 클래스의 기능을 전혀 사용하지 않는다. 부모 클래스의 기능을 사용하든 사용하지 않든 간에 부모 클래스를 강하게 의존한다.
이러한 설계는 좋은 설계가 아니다.
추가로 템플릿 메서드 패턴은 상속 구조를 사용하기 때문에, 별도의 클래스나 익명 내부 클래스를 만들어야 하는 부분도 복잡하다.

템플릿 메서드 패턴과 비슷한 역할을 하면서 상속의 단점을 제거하는 디자인패턴이 바로 전략 패턴(Strategy Pattern)이다.

전략 패턴(Strategy Pattern)

전략패턴은 변하지 않는 부분을 Context라는 곳에 두고, 변하는 부분을 Strategy라는 인터페이스를 만들고 해당 인터페이스를 구현하도록 해서 문제를 해결한다.
상속이 아닌 위임으로 해결하는 것.

GOF 디자인 패턴에서 정의한 전략 패턴의 의도는 다음과 같다.
알고리즘 제품군을 정의하고 각각을 캡슐화하여 상호 교환 가능하게 만들자. 전략을 사용하면 알고리즘을 사용하는 클라이언트와 독립적으로 알고리즘을 변경할 수 있다.

  • Strategy 인터페이스

  • Context

    context는 내부에 Strategy 필드를 갖고 있어, 이부분에 Strategy 구현체를 주입하면 된다. 이 방식은 Context를 실행하는 시점에는 이미 조립이 끝났기 때문에 전략을 신경쓰지 않고 단순히 실행만 하면 된다.

전략 패턴의 핵심은 Context는 Strategy 인터페이스에만 의존한다는 것이다. 덕분에 Strategy 구현체를 변경하거나 새로 만들어도 Context 코드에는 영향을 주지 않는다.
( 스프링에서 의존관계 주입하는 방식이 전략 패턴이다! )

  • Strategy를 파라미터로 전달받는 Context

    실행때마다 전략을 유연하게 변경할 수 있다는 징점이 있다.

템플릿 콜백 패턴

위의 ContextV2는 변하지 않는 템플릿 역할을 한다. 그리고 변하는 부분은 파라미터로 넘어온 Strategy의 코드를 실행해서 처리한다.
이렇게 다른 인수로서 넘겨주는 실행 가등한 코드를 callback이라고 한다.

callback 정의
프로그래밍에서 콜백 또는 콜애프터 함수는 다른 코드의 인수로서 넘겨주는 실행 가능한 코드를 말한다. 콜백을 넘겨받는 코드는 이 콜백을 필요에 따라 즉시 실행할 수도 있고, 아니면 나중에 실행할 수도 있다.

  • ContextV2 예제에서 콜백은 Strategy

자바언어에서 콜백

  • 자바 언어에서 실행 가능한 코드를 인수로 넘기려면 객체가 필요하다. 자바8부터는 람다를 사용할 수 있다.
  • 자바8 이전에는 보통 하나의 메소드를 가진 인터페이스를 구현하고, 주로 익명 내부 클래스를 사용했다. 최근엔 주로 람다를 사용한다.

템플릿 콜백 패턴

  • 스프링 에서는 ContextV2와 같은 방식의 전략 패턴을 템플릭 콜백 패턴이라 한다.
  • 참고로 템플릿 콜백 패턴은 GOF 패턴이 아니고, 스프링 내부에서 이런 방식을 자주 사용하기 때문에, 스프링 안에서만 이렇게 부른다.
  • 스프링에서는 JdbcTemplate, RestTemplate, TransactionTemplate, RedisTemplate 처럼 다양한 템플릿 콜백 패턴이 사용된다. 스프링에서 이름에 ~Template이 있다면 템플릿 콜백 패턴으로 만들어 진 것!

0개의 댓글