인프런 강의 [스프링 핵심 원리 - 고급편] 강의를 기반으로 작성한 글 입니다.
전공자라면 템플릿 메소드를 한번쯤은 들어봤을것입니다.
저도 들어보긴 했습니다. 정처기에서도 디자인 패턴에 대한 개념이 있으니까요.
그래서 제가 정리했던 디자인 패턴을 다시 들여다봤습니다.
🤔 "?? 대체 뭐라고 적은거니? 받아라 냥냥 펀치"
사실 대부분 모든 정처기 공부하시는 학우님들이 그냥 외우기만 할 것 같습니다.
그래서 오늘은 템플릿 메소드를 알아보려합니다.
템플릿 메소드는 추상 클래스가 포함됩니다.
추상 클래스를 상속 받아서, 추상 클래스에 특정 로직을 수행하도록 구현체를 생성하면 기존의 로직에 특정 로직을 처리할 수 있습니다.
말로는 어려우니 예시를 살펴보겠습니다.
GS25에서 일하는 스토어 매니저를 생각해봅시다. It's Me
손님이 들어오면 무조건 인사해야합니다. 또 계산을 하면 인사를 하게 됩니다.
하지만 이 알바생은 계산만 하지 않아요. 손님이 오면 다른 일도 해야합니다.
최근에 피자도 굽기 시작했다^^ 편의점 알바를 하면 피자집 알바도 할 수 있잖아? 완전 럭키현준
손님이 오면 항상 하는 것이 있습니다. 첫 인사와 마지막 인사는 무조건 합니다.
그렇다면 첫 인사와 마지막 인사는 중복되기에, 하나의 메소드로 묶을 수 있습니다.
근데 그 중간에 변경되는 기능들이 있는데 인사들을 하나의 메소드로 묶을 수 있을까? 어렵겠죠?
그래서 피자나라치킨공주우체국배민 이 작업들이 들어갈 부분에 추상 메소드로 두고, 추후 구현체에 피자나라치킨공주우체국배민이 기능를 넣는 것으로 템플릿을 생성할 수 있습니다.
아래 실제 예제로 살펴보겠습니다.
저번 시간에, ThreadLocal을 이용하여 개발했었습니다.
[Spring] ThreadLocal을 이용한 동시성 관리
근데, 비즈니스 로직이 핵심 기능이 하나인데,
부가 기능 때문에 비즈니스 로직이 난잡해지는 문제점이 발견되었습니다.
그런데 살펴보니 Controller - Service - Repository 모두 동일한 패턴이 발견된 것을 알았습니다..
살펴보니 핵심 기능만 변화하고 다른 부분은 변하지 않는 것을 알 수 있었습니다.
그래서 메소드로 뽑아낼 수 있냐?
아니. try-catch
때문에 어렵습니다. 또한 핵심 기능이 중간에 들어가 있어서 그것도 어렵습니다.
이것은 위에서 말했던 템플릿 메서드 패턴으로 해결할 수 있습니다.
실제 겹치는 부분은 구현을 해두고,
중간에 변경되는 로직은 추상 메소드로 분리하여, 객체 생성 시 추상 메소드를 구현하는 방식입니다.
기존의 로직을 execute()
메소드에 넣고, 변경되는 로직은 call()
의 추상 메소드로 구현하였습니다.
그리고 메소드마다 Return Type이 다르기 때문에, Type 제너릭으로 선언해줍니다.
이렇게 call()
메소드가 추상 메소드로 선언이 되었습니다.
그러면 실제 비즈니스 로직에서는 call()
메소드를 구현해야하는데요. 이 부분 살펴보겠습니다.
인스턴스 생성시 바로 오버라이드 해서 구현하는 방법입니다.
중복 된 부분은 하나로 잘 통합되었고, 변경된 부분만 call
메소드에 오버라이드해서 사용됩니다.
이 방법이 더 복잡한데, 이러면 구현체를 다 만들어야 하므로, 사실 추천하는 방법이 아닙니다.
call()
을 보면 Controller → Service로 가는 기능입니다.
이렇게 하면 장점이, Controller에서 로직은 획기적으로 줄어들긴 합니다.
하지만 이러면 Service → Repository 때의 구현 클래스도 생성해야합니다!!
템플릿 메소드의 장단점으로 알아보겠습니다.
장점
1. 중복되는 부분을 모듈화 할 수 있음.
2. 모듈화를 하다보니, 수정 할 때 더 용이함
단점
1. 상속으로 인한 문제 발생
2. 자식 클래스가 부모 클래스와 컴파일 시점에서 강하게 결합
→ 자식 클래스는 부모 클래스의 기능을 사용하지 않지만, 부모 클래스가 수정되면 자식 클래스도 수정해야 할 수도 있음
3. 엄청난 비즈니스 로직 개선은 아닌듯 하다