[Kotlin] invoke에 대해

Lee Yongin·2024년 3월 17일
0

자바/코틀린

목록 보기
5/5

프로젝트를 진행하다가 동료분이 UseCase의 invoke함수에 operator를 안 붙였다는 이야기를 PR에서 언급하신 걸 보았다.
예를 들어서 아래와 같이 토큰을 가져오는 UseCase가 있다고 가정하면, 밑에 있는 invoke함수에 operator가 없으면 아래와 같이 호출해야 한다

GetMemberUseCase("1").invoke()

class GetMemberUseCase(
    private val memberRepository: MemberRepository
) {
    fun invoke(id:String): Flow<MemberResponse> = flow{
    	emit(memberRepository.getMember(id))
    }
}

하지만 invoke 함수에 operator키워드를 붙이면 객체를 함수처럼 호출할 수 있다. GetMemberUseCase("1")은 내부적으로 GetMemberUseCase().invoke("1")로 컴파일된다.

GetMemberUseCase("1")

...라고 인지하고 작업을 재개했었다.
하지만 시간날 때 기초지식을 정리해야 갓발자가 될 수 있다

invoke 관례

코틀린은 자바와 다르게 관례가 있다. 관례란 미리 정해둔 이름을 사용한 메서드를 통해서 긴 식 대신더 짧고 간결한 식을 쓸 수 있게 지원하는 것이다.

인라인하는 람다를 제외한 모든 람다는 함수형 인터페이스를 구현하는 클래스로 컴파일된다.

invoke를 사용하는 이유

술어 클래스 내부와 술어가 쓰이는 주변에 복잡한 로직이 있는 경우 관심사를 분리해내는데 유용하다.

코드

data class Perfume(
	val price:String, val brand:String, val genderType:Gender, val StorageType:Storage
)

val perfume1 = Perfume(100000, "channel", Gender.WOMAN, Storage.SOLD_OUT)
val perfume2 = Perfume(140000, "gucci", Gender.MAN, Storage.FEW_LEFT)
for (perfume in listOf(perfume1, perfume2).filter(it.name == "gucci" && Gender.WOMAN && Storage.FEW_LEFT)){
	//처리
}

위의 함수의 복잡한 filter조건을 사용해서 향수를 걸러내는 코드가 있다.
filter 에 들어가는 람다식이 그렇게 어려운 내용은 아니지만,예시 코드인 걸 감안하면 이런식의 코드는 조잡하다.
람다를 여러 메서드로 나누고 뜻을 명확히 알 수 있는 이름으로 바꾸는 게 더 좋다.

리팩토링된 코드

이렇게 람다를 함수 타입 인터페이스를 구현하는 클래스로 변환하고, 그 클래스의 invoke 메서드를 오버라이드하면 깔끔한 리팩토링이 가능하다.

data class Perfume(
	val price:String, val brand:String, val genderType:Gender, val StorageType:Storage
)

class GucciBlackFridaySaleTarget():(Perfume) -> Boolean {
	operator fun invoke(perfume:Perfume):Boolean{
    	return EventCalendar.BLACK_FRIDAY == LocalDate.now() && perfume.isGucci*(
    }
    
    private fun Perfume.isGucci():Boolean{
    	return brand == "gucci"
    }
}
val perfume1 = Perfume(100000, "channel", Gender.WOMAN, Storage.SOLD_OUT)
val perfume2 = Perfume(140000, "gucci", Gender.MAN, Storage.FEW_LEFT)
val target = GucciBlackFridaySaleTarget()
for (perfume in listOf(perfume1, perfume2).filter(target)){
	//처리
}

참고자료

Kotlin in Action - DSL 만들기
https://onlyfor-me-blog.tistory.com/837

profile
f1을 좋아하는...🏆 f1처럼 빠르고 정확한 걸 좋아하는 안드로이드 개발자

0개의 댓글