본문 바로가기
개발&프로그래밍

[Kotlin] 위임(Delegation)

by 재아군 2024. 8. 17.

Kotlin의 위임(Delegation)

Kotlin의 위임(Delegation) 기능은 구현 상속의 강력한 대안으로, 보일러플레이트 코드 없이 유연하고 재사용 가능한 코드를 작성할 수 있게 해줍니다. 이 가이드에서는 Kotlin의 위임 기능의 다양한 측면을 상세히 살펴보겠습니다.

 

 

Kotlin의 위임(Delegation)

 

 

위임 기본 (Delegation Basics)

Kotlin에서는 by 키워드를 사용하여 인터페이스의 구현을 다른 객체에 위임할 수 있습니다. 이를 통해 컴포지션을 쉽게 구현할 수 있습니다.

interface Base {
    fun print()
}

class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}

class Derived(b: Base) : Base by b

fun main() {
    val base = BaseImpl(10)
    Derived(base).print() // 출력: 10
}

 

이 예제에서 Derived 클래스는 Base 인터페이스의 구현을 b 객체에 위임합니다. 컴파일러는 Base의 모든 메서드를 b로 포워딩하는 코드를 자동으로 생성합니다.

 

 

위임된 인터페이스 멤버 오버라이딩 (Overriding Members of Delegated Interface)

위임을 사용하더라도 인터페이스의 메서드를 오버라이드할 수 있습니다. 오버라이드된 메서드는 위임 객체의 구현 대신 사용됩니다.

interface Base {
    fun printMessage()
    fun printMessageLine()
}

class BaseImpl(val x: Int) : Base {
    override fun printMessage() { print(x) }
    override fun printMessageLine() { println(x) }
}

class Derived(b: Base) : Base by b {
    override fun printMessage() { print("abc") }
}

fun main() {
    val base = BaseImpl(10)
    Derived(base).printMessage() // 출력: abc
    Derived(base).printMessageLine() // 출력: 10
}

 

이 예제에서 Derived 클래스는 printMessage()를 오버라이드하여 자체 구현을 제공하지만, printMessageLine()은 여전히 위임 객체의 구현을 사용합니다.

 

 

위임 객체의 동작 (Behavior of Delegated Object)

중요한 점은 오버라이드된 멤버가 위임 객체의 다른 메서드에서 호출되지 않는다는 것입니다. 위임 객체는 자신의 구현만을 사용합니다.

interface Base {
    val message: String
    fun print()
}

class BaseImpl(x: Int) : Base {
    override val message = "BaseImpl: x = $x"
    override fun print() { println(message) }
}

class Derived(b: Base) : Base by b {
    override val message = "Message of Derived"
}

fun main() {
    val b = BaseImpl(10)
    val derived = Derived(b)
    derived.print() // 출력: BaseImpl: x = 10
    println(derived.message) // 출력: Message of Derived
}

이 예제에서 Derivedmessage 프로퍼티는 오버라이드되지만, BaseImplprint() 메서드에서는 이 오버라이드된 값이 아닌 자신의 message 값을 사용합니다.

 

 

위임의 장점과 사용 사례

  1. 코드 재사용: 상속 없이 기존 구현을 재사용할 수 있습니다.
  2. 유연성: 런타임에 동적으로 구현을 변경할 수 있습니다.
  3. 캡슐화: 내부 구현을 숨기면서 인터페이스를 노출할 수 있습니다.
  4. 다중 상속 문제 해결: Java의 다중 상속 제한을 우회할 수 있습니다.

 

 

실제 사용 사례

  1. 데코레이터 패턴: 객체의 책임을 동적으로 추가할 수 있습니다.
interface Coffee {
    fun getCost(): Double
    fun getDescription(): String
}

class SimpleCoffee : Coffee {
    override fun getCost() = 1.0
    override fun getDescription() = "Simple coffee"
}

class MilkDecorator(private val coffee: Coffee) : Coffee by coffee {
    override fun getCost() = coffee.getCost() + 0.5
    override fun getDescription() = "${coffee.getDescription()}, milk"
}

 

 

   2. 전략 패턴: 알고리즘을 동적으로 교체할 수 있습니다.

 

interface SortStrategy {
    fun sort(list: List<Int>): List<Int>
}

class Sorter(private val strategy: SortStrategy) : SortStrategy by strategy

// 사용
val bubbleSort: SortStrategy = BubbleSortStrategy()
val quickSort: SortStrategy = QuickSortStrategy()
val sorter = Sorter(bubbleSort)
// 나중에 전략을 변경할 수 있음
// sorter = Sorter(quickSort)

 

 

   3. 프록시 패턴: 객체에 대한 접근을 제어할 수 있습니다.

 

interface Image {
    fun display()
}

class RealImage(private val filename: String) : Image {
    override fun display() = println("Displaying $filename")
}

class ProxyImage(private val filename: String) : Image {
    private var realImage: RealImage? = null

    override fun display() {
        if (realImage == null) {
            realImage = RealImage(filename)
        }
        realImage?.display()
    }
}

 

 

 

Kotlin의 위임 기능은 객체 지향 설계의 유연성을 크게 향상시킵니다. 이를 통해 코드 재사용성을 높이고, 더 모듈화된 구조를 만들 수 있습니다. 특히 상속으로 인한 문제(예: 취약한 기반 클래스 문제)를 피하면서도 코드를 효과적으로 구성할 수 있습니다.

하지만 위임을 과도하게 사용하면 코드의 복잡성이 증가할 수 있으므로, 적절한 상황에서 신중하게 사용해야 합니다. 특히 큰 프로젝트에서는 위임 관계를 명확히 문서화하고 관리하는 것이 중요합니다.

 

위임은 특히 레거시 코드를 리팩토링하거나, 테스트하기 어려운 코드를 개선할 때 유용한 도구가 될 수 있습니다. 또한 프레임워크나 라이브러리 설계 시 확장성과 유연성을 제공하는 데 큰 도움이 됩니다.

 

 

이 블로그의 다른글 : 

2024.08.17 - [개발&프로그래밍] - [Kotlin] 객체 표현식과 선언(object expressions and object declarations)

 

[Kotlin] 객체 표현식과 선언(object expressions and object declarations)

Kotlin의 객체 표현식과 선언Kotlin에서 객체 표현식(object expressions)과 객체 선언(object declarations)은 클래스를 상속하거나 인터페이스를 구현하는 익명 객체를 생성하는 강력한 기능입니다. 이 가이

observerlife.tistory.com

 

2024.08.13 - [개발&프로그래밍] - [kotlin] 인라인 값 클래스(Inline Value Classes)

 

[kotlin] 인라인 값 클래스(Inline Value Classes)

Kotlin의 인라인 값 클래스(Inline Value Classes)Kotlin의 인라인 값 클래스(Inline Value Classes)는 성능 최적화와 타입 안전성을 동시에 제공하는 강력한 기능입니다.이 문서에서 인라인 값 클래스의 다양한

observerlife.tistory.com

2024.08.13 - [개발&프로그래밍] - [Kotlin] 열거형 클래스(Enum class)

 

[Kotlin] 열거형 클래스(Enum class)

Kotlin의 열거형 클래스Kotlin의 열거형 클래스(enum class)는 고정된 상수 집합을 표현하는 강력한 도구입니다.이 문서에서는 Kotlin 열거형 클래스의 다양한 사용법을 살펴보겠습니다.   기본 사용

observerlife.tistory.com

2024.08.13 - [개발&프로그래밍] - [Kotlin] 중첩 클래스와 내부 클래스

 

[Kotlin] 중첩 클래스와 내부 클래스

Kotlin의 중첩 클래스와 내부 클래스Kotlin에서 클래스 내부에 다른 클래스를 정의하는 방법은 코드의 구조화와 캡슐화를 위한 강력한 도구입니다. 이 문서에서는 중첩 클래스와 내부 클래스의 개

observerlife.tistory.com

2024.08.13 - [개발&프로그래밍] - [kotlin] 제네릭: in, out, where

 

[kotlin] 제네릭: in, out, where

Kotlin의 제네릭: in, out, whereKotlin의 제네릭은 타입 안전성과 코드 재사용성을 높이는 강력한 기능입니다.이 가이드에서는 Kotlin 제네릭의 다양한 측면을 상세히 살펴보겠습니다.     제네릭 기

observerlife.tistory.com

2024.08.12 - [개발&프로그래밍] - [kotlin] Sealed classes & interfaces

 

[kotlin] Sealed classes & interfaces

Kotlin의 Sealed 클래스와 인터페이스Kotlin의 sealed 클래스와 인터페이스는 클래스 계층 구조의 제어된 상속을 제공하는 강력한 기능입니다.이 가이드에서는 sealed 클래스와 인터페이스의 다양한 측

observerlife.tistory.com

2024.08.12 - [개발&프로그래밍] - [kotlin] 데이터 클래스(Data classes)

 

[kotlin] 데이터 클래스(Data classes)

Kotlin의 데이터 클래스Kotlin의 데이터 클래스(Data Classes)는 주로 데이터를 보유하는 용도로 사용되는 특별한 클래스입니다.이 가이드에서는 Kotlin 데이터 클래스의 다양한 측면을 상세히 살펴보겠

observerlife.tistory.com

2024.08.12 - [개발&프로그래밍] - [kotlin] 확장 기능(Extensions)

 

[kotlin] 확장 기능(Extensions)

Kotlin의 확장 기능Kotlin의 확장(Extensions) 기능은 기존 클래스나 인터페이스에 새로운 기능을 추가할 수 있게 해주는 강력한 도구입니다.이 가이드에서는 Kotlin 확장의 다양한 측면을 상세히 살펴

observerlife.tistory.com

 

 

 

 

 

 

 


Kotlin, 위임, Delegation, 인터페이스, 코드재사용, 디자인패턴, 객체지향프로그래밍, 컴포지션, 데코레이터패턴, 전략패턴, 프록시패턴, 코틀린문법, 안드로이드개발, 서버개발, 리팩토링

댓글