안드로이드 개발을 하면서 by lazy {} , by remember {} 등 by 키워드를 자주 사용하고 있었다. 그런데 by 키워드가 Kotlin Delegation을 위한 문법이라는 것을 알고 있었지만, Delegation(위임)이라는 것에 대해 제대로 알고 있지 않다는 생각이 들어서 이번 기회에 알아보려고 한다.
👀 어떨 때 상속 대신 위임을 써야할까?
상속과 위임은 모두 객체지향 프로그래밍에서 사용되는 디자인 패턴으로, 두 방식 모두 클래스를 다른 클래스로부터 확장한다. 차이점을 살펴보자면 상속은 해당 클래스에 귀속되는 것이기 때문에 클래스가 다른 클래스들 사이에서 선택할 권한을 주지 않는다. 반면 위임은 객체 자신이 처리해야 할 일을 다른 클래스 인스턴스에게 위임할 수 있는 것이기 때문에 상속보다 유연하다. 그래서 여러 객체지향 도서에서 상속(is-a)보다 위임(has-a)을 사용할 것을 권장하고 있다고 한다. 책 좀 읽어야겠다…
하지만 그렇다고 상속이 나쁜 것은 아니고 각자 적절한 곳에 사용하면 되는데, 그 기준에 대해서 Ready Kim 님이 정리해주신게 있어서 가져와봤다…
- 클래스의 객체가 다른 클래스의 객체가 들어갈 자리에 쓰여야 한다면 상속해라.
- 클래스의 객체가 단순히 다른 클래스의 객체를 사용만 해야한다면 위임을 사용해라.
자세한 설명은 준비님 블로그에 잘 정리되어있고, 내가 상속과 위임에 대해 가장 쉽게 이해했던 사례만 아래에 적어뒀다.
만약 “개는 동물이다”와 같이 포함 관계(is-a)에 있는 다른 클래스로 대체할 때는 상속, Manager가 Worker를 가지고 있고(has-a) Worker에게 일을 넘기는 것처럼 그저 다른 객체의 구현을 재사용하는 경우라면 위임을 사용하는게 좋다.
위임 구현해보기(Delegation Interface)
일반적인 방법으로 인터페이스-클래스를 구성하면…
2개의 인터페이스가 있다고 가정해보자
interface Interface1 {
fun func1()
}
interface Interface2 {
fun func2()
}
그리고 이 두 인터페이스를 구현하는 클래스는 이렇게 작성될 것이고, 다이어그램으로 표현하면 아래와 같다.
class Class: Interface1, Interface2 {
override fun func1() {
println("func1")
}
override fun func2() {
println("func2")
}
}
fun main() {
val obj = Class()
obj.func1()
obj.func2()
}
위임으로 구현하면
그렇다면 위임(Delegation)을 사용해서 구현하면 어떨까?
우선 다이어그램을 먼저 보자. 더이상 각 인터페이스를 Class에서 구현하지 않아도 된다. 즉 각 인터페이스의 구현을 인스턴스에 위임하게 된다.
func1과 func2의 구현이 Class에서 Interface1Impl와 Interface2Impl로 이동되었다. 이렇게하면 Class가 인터페이스 구현을 안해도 돼서 더 가벼워지게 된다.
interface Interface1 {
fun func1()
}
interface Interface2 {
fun func2()
}
class Interface1Impl: Interface1 {
override fun func1() {
println("func1")
}
}
class Interface2Impl: Interface2 {
override fun func2() {
println("func2")
}
}
class Class: Interface1 by Interface1Impl(), Interface2 by Interface2Impl() {
}
Class에서 인터페이스를 한번 더 재정의할 수도 있다
class Class : Interface1 by Interface1Imp (), Interface2 by Interface2Imp() {
override fun fun1 () {
println( "override fun1" )
}
}
👨🍳 간단히 활용해보기
Delegation에 대해 귀여운 그림과 함께 설명해주는 아티클이 있어서 가져와봤다.
https://typealias.com/start/kotlin-delegation/
식당에서 음식을 주문하는 과정을 예시로 들었는데, 아티클 전체를 정리하긴 넘 많고.. 그 상황을 참고해서 위임 패턴을 한 번 더 연습해보려고 한다.
enum class Entree { TOSSED_SALAD, SALMON_ON_RICE }
enum class Beverage { WATER, SODA }
interface KitchenStaff{
fun prepareEntree(name: String): Entree?
fun prepareBeverage(name: String): Beverage?
}
class Chef: KitchenStaff {
override fun prepareEntree(name: String): Entree? {
return when(name) {
"Tossed Salad" -> Entree.TOSSED_SALAD
"Salmon on Rice" -> Entree.SALMON_ON_RICE
else -> null
}
}
override fun prepareBeverage(name: String): Beverage? {
return when(name) {
"Water" -> Beverage.WATER
"Soda" -> Beverage.SODA
else -> null
}
}
}
class Waiter: KitchenStaff by Chef()
class DelegationTest() {
@Test
fun `웨이터가 일을 잘 시키는지 테스트`() {
val waiter = Waiter()
// The customer ordered Water
val beverage = waiter.prepareBeverage("Water")
assertEquals(beverage, Beverage.WATER)
// The customer ordered Tossed Salad
val entree = waiter.prepareEntree("Tossed Salad")
assertEquals(entree, Entree.TOSSED_SALAD)
}
}
...
오늘은 여기까지하고 by lazy {} , by remember {} 등이 구현되는 방식.. Delegated Properties에 대해서는 다음에 이어서 알아봐야겠당 >_o
'Kotlin' 카테고리의 다른 글
Kotlinx Serialization의 JsonBuilder Properties 알아보기 (0) | 2023.02.23 |
---|