본문 바로가기

웹개발 - Back 관련/Architecture

[ GOF ] 탬플릿 메서드 패턴 - Template method with Kotlin

Topic =  템플릿 메서드 패턴의 의도와 Kotlin 적용 예제 학습해보기

 

🔑Purpose

  • 변하는 것과 변하지 않는 것을 분리
    • 반복되는 공통 로직을 템플릿이라는 변하지 않는 뼈대로 정의해두고, 변하는 부분을 별도의 서브클래스로 정의하여 분리한다.

 

예시로 비교해보면서 학습하는 탬플릿 메서드 패턴 개념

 

fun main {

    fun algorithmA(){
        val startTime = System.currentTimeMillis()
        
        // 알고리즘 풀이
        
        val endTime = System.currentTimeMillis()
        val resultTime = endtime - startTime
    }
    
    fun algorithmB(){
        val startTime = System.currentTimeMillis()
        
        // 알고리즘 풀이
        
        val endTime = System.currentTimeMillis()
        val resultTime = endtime - startTime
    }
    
    ... C
    
    ... D
    
    ... F
}

 

알고리즘 풀이 시간을 확인하기 위해서 위 와같이 알고리즘 함수 내부에 resultTime을 얻는 로직을 추가했더니 다음과 같은 문제가 발생했다.

 

단일 책임 원칙 위반

  • 알고리즘 함수 내에서 시간 측정과 알고리즘 실행 두 가지 책임을 가지게 된다.

유지보수가 어렵다

  • 모든 알고리즘에 동일한 코드가 중복될 뿐더러, 시간 측정 로직이 변경될 경우 모든 알고리즘 함수에서 동일한 코드를 수정해야 한다.

 

🚨위 시간 측정 로직과 같이 변하는 부분을 사이에 두고 작동하는 코드는 별도의 함수로 빼서 사용하기가 어렵다. 이러한 문제를 Template Method 패턴은 추상 클래스( AbstractClass )와 , 구현 클래스 ( ConcreteClass ) 를 통해 해결한다.

 

AbstractClass

  • 변동되지 않는 로직인 템플릿 메서드를 구현하며, 그 탬플릿 메서드 내부에서 사용할 추상 메서드를 선언한다. 선언한 추상 메서드는 하위 클래스 ConcreteClass에서 구현된다.

ConcreteClass

  • AbstractClass에 정의된 추상 메서드를 구체적으로 구현하며, 이 추상 메서드는 템플렛 메서드에서 호출된다.

 

 

 

[ 적용 예시 ]

[ AbstractClass ]

private val logger = KotlinLogging.logger {  }

abstract class AbstractTemplate {

    fun execute(){
        val startTime = System.currentTimeMillis()

        call()

        val endTime = System.currentTimeMillis()

        logger.info { "result time = ${endTime - startTime}" }

    }

    protected abstract fun call()
}
  • Template method 의 부모 역할을 하는 abstract class를 정의한다. 추상 메서드 call() 을 제외한 execute() 내부 로직들은 해당 추상 템플릿을 상속 받아 추상 메서드를 정의한 객체가 별도의 호출 로직 없이 사용할 수 있으며, 추상 템플릿을 상속하지만, 템플릿 메서드를 사용하지 않길 바라는 로직은 override를 통해서 템플릿을 사용하지 않을 수 있다.

 

[ ConcreteClass ]

private val logger = KotlinLogging.logger {  }
class SubClassLogic1 : AbstractTemplate() {

    override fun call (){
        logger.info { "비즈니스 로직 1 실행" }
    }
}
  • AbstractClass로 정의된 추상 메서드( 변동되는 로직 )를 구체적으로 구현한다.
  • 추상 템플릿 메서드는, 상속을 사용하기 때문에 템플릿 메서드를 사용하는 객체마다 클래스로 선언하여 사용하여야 한다. 때문에 여러 클래스 파일이 생성하여 프로젝트 복잡도가 높아지는데, 익명 클래스를 통해 이 문제를 해결할 수 있다.

 

[ ConcreteClass 익명 클래스 ]

private val logger = KotlinLogging.logger {  }
fun main {
    fun templateMethod(){
        val logic1 = object : AbstractTemplate() {
            override fun call() {
                logger.info { "비즈니스 로직 1 실행" }
            }
        }
        logic1.execute()
    }
}
  • client 단에서 객체를 사용할 때, 익명 클래스로 선언할 수 있다.

 

📊탬플릿 메서드의 장점과 한계점 및 대안

🟩 장점

  • 부모 클래스에 알고리즘의 골격인 템플릿을 정의하고, 일부 변경되는 로직은 자식 클래스에 정의함으로 인해 공통 로직 분리가 가능해졌다.

 

🟥 단점

  • 상속을 사용하므로, 상속으로 인해 생기는 단점이 존재한다. 부모 클래스의 기능인 템플릿 메서드는 하위 클래스에서 사용하지 않는다. 이는 LSP 위반으로 부모 클래스의 로직이 필요하지 않은 상속을 강제하는 문제다.

 

🏗️ 대안1. Default Method Interface

추상 템플릿 메서드의 상속으로 인한 단점으로 Interface의 Default method 를 통해 해당 문제를 해결할 수 있다고 한다. 하지만, Kotlin의 경우 Interface에서 Default Method 를 사용할 때 AOP가 원하는대로 동작하지 않는 이슈가 발생한다고 한다.


Kotlin Default Method Interface 부분은 추가로 실습해보고 정리 할 예정...

 

@JvmDefault and how add compiler option

I need to make a default void method in a Kotlin interface. I ran into a problem and a hint said Usage of @JvmDefault is only allowed with -Xjvm-default option. Where do I need to write this Xjvm-

stackoverflow.com

 

 

🏗️ 대안2. Strategy Pattern

또, strategy pattern 을 통해 템플릿 메서드의 단점을 어느정도 해결할 수 있고, 디폴트 매서드 인터페이스의 경우 함수형 인터페이스로 간단하게 사용할 수 없어 편의성이 더 떨어진다. 다음에 strategy pattern 을 학습하면서 작동 방식을 비교하면서 다시 학습 해봐야겠다.

 

 

 

 

 

 


 

[ A. 오늘 복습한 내용 / B. 다음에 학습할 내용 ]

A. 탬플릿 메서드 복습...

 

B. Strategy 패턴

 

B. Factory Method 패턴

 

 

 


[Reference]

[ 🌟Kotlin Default Method Interface 🌟 ]

 

Kotlin에서 interface default 구현

Kotlin에서 interface default 구현 예상과는 다르다!

velog.io

 

 

JAVA 언어로 배우는 디자인 패턴 입문: 쉽게 배우는 GoF의 23가지 디자인 패턴 | 유키 히로시 | 영진

▶ 3판에서 달라진 점 ㆍ예제 프로그램을 현대 Java 언어(람다식, 확장 for문, enum형 등)로 업데이트 ㆍ이해하기 쉽게 설명하되 현대의 관점에서 예제와 설명(의존성 주입, 보안 관련 설명 등) 수정

ebook-product.kyobobook.co.kr

 

 

스프링 핵심 원리 - 고급편 강의 | 김영한 - 인프런

김영한 | 스프링의 핵심 원리와 고급 기술들을 깊이있게 학습하고, 스프링을 자신있게 사용할 수 있습니다., 핵심 디자인 패턴, 쓰레드 로컬, 스프링 AOP스프링의 3가지 핵심 고급 개념 이해하기

www.inflearn.com

 

 

템플릿 메서드 패턴

/ 디자인 패턴들 / 행동 패턴 템플릿 메서드 패턴 다음 이름으로도 불립니다: Template Method 의도 템플릿 메서드는 부모 클래스에서 알고리즘의 골격을 정의하지만, 해당 알고리즘의 구조를 변경하

refactoring.guru