본문 바로가기
Kotlin

Delegate Pattern 위임패턴

by 히예네 2024. 8. 20.
728x90
반응형

Delegate Pattern (위임패턴)

1. 위임 패턴이란?

객체가 직접 작업을 수행하지 않고 다른 도우미 객체에게 그 작업을 맡기는 디자인 패턴. 이 도우미 객체를 위임 객체(Delegate)라고 한다.

2. 위임 패턴의 대표적 예시 - by lazy() 프로퍼티 초기화 지연

  • 지연 초기화(lazy initialization)
    • 객체의 일부분을 초기화하지 않고 남겨두었다가, 실제로 그 부분의 값이 필요할 경우 초기화한다.
  • 지연 초기화를 사용하는 경우
    • 초기화 과정에서 자원을 많이 사용하거나
    • 객체를 사용할 때 마다 꼭 초기화하지 않아도 되는 프로퍼티일 때
  • 예시
    • Person 클래스: 자신이 작성한 메일 리스트를 제공한다
      • 이메일들이 db에 저장되어있으며, 불러오면 시간이 걸린다 가정하자.
        • 그래서 이메일 프로퍼티의 값을 최초로 사용할 때 단 한번만 이메일을 db에서 가져올 것이다.
    • loadEmail() 함수를 호출 할 때, 초기화 된다.  
class Person(val name: String) {
    val emails by lazy { loadEmail(this) }
}

class Email

fun loadEmail(person: Person): List<Email> {
    println("${person.name}의 이메일을 가져옴")
    return listOf()
}
  • by 키워드의 역할
    • by 뒤에는 위임에 쓰일 객체를 사용한다.
    • lazy 함수에 의해 제공된 객체에게 초기화 작업을 위임하고 있다. 즉 lazy가 대신 그 값을 초기화해주고있다.
  • lazy 함수
    • getValue 메서드가 들어있는 객체를 반환한다.
    • 위임 관례에 따라 Delegate 클래스는 getValue ( 변경가능한 프로퍼티라면 setValue도 사용)이 필요하므로 by 키워드와 함께 많이 사용된다.

3. 위임 프로퍼티의 장점 느껴보기

❓ 이런 경우를 생각해보자

  • 어떤 객체의 프로퍼티가 바뀔때마다 리스너에게 변경 통지를 보내고싶다.
    • Person 클래스의 나이나 급여가 바뀌면 리스너에게 알려주고싶다.
  • 어떤 객체를 UI에 표시하는 경우, 그 객체가 바뀌면 자동으로 UI를 변경하고 싶다면?

→ PropertyChangeSupport나 PropertyChangeEvent를 사용해 이런 통지를 처리하는 경우가 많다.

  • PropertyChangeSupport: 리스너 목록을 관리한다.
  • PropertyChangeEvent: 이벤트가 들어오면 목록의 모든 리스너에게 이벤트를 통지한다.
open class PropertyChangeAware {
    protected val changedSupport = PropertyChangeSupport(this)
    
    fun addPropertyChangeListener(listener: PropertyChangeListener) {
        changedSupport.addPropertyChangeListener(listener)
    }  
    fun removePropertyChangeListener(listener: PropertyChangeListener){
        changedSupport.removePropertyChangeListener(listener)
    }
}

❌ 프로퍼티 변경 통지를 직접 구현하기 (field 키워드)

fun main() {
    val person = Person("Alice", 20, 100)
    person.addPropertyChangeListener(
        PropertyChangeListener { event ->
            println("Property ${event.propertyName} changed" + "from ${event.oldValue} to ${event.newValue}")
        }
    )
    person.age = 30
    person.salary = 1000
    
    //Property age changedfrom 20 to 30
		//Property salary changedfrom 100 to 1000
}

class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware() {
    var age: Int = age
        set(newValue) {
            val oldValue = field
            field = newValue
            changedSupport.firePropertyChange(
                "age", oldValue, newValue
            )
        }
    var salary: Int = salary
        set(newValue) {
            val oldValue = field
            field = newValue
            changedSupport.firePropertyChange(
                "salary", oldValue, newValue
            )
        }
}
  • field 키워드의 역할
    • getter나 setter에서만 사용가능한 내부 변수
    • 실제 설정된 값을 가르킨다.

❌ 도우미 클래스를 통해 프로퍼티 변경 통지 구현하기

class ObservableProperty(
    val propName: String, var propValue: Int,
    val changeSupport: PropertyChangeSupport
) {
    fun getValue(): Int = propValue
    fun setValue(newValue: Int) {
        val oldValue = propValue
        propValue = newValue
        changeSupport.firePropertyChange(propName, oldValue, newValue)
    }
}

class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware() {
    val _age = ObservableProperty("age", age, changedSupport)
    var age: Int
        get() = _age.getValue()
        set(value) {
            _age.setValue(value)
        }

    val _salary = ObservableProperty("salary", salary, changedSupport)
    var salary: Int
        get() = _salary.getValue()
        set(value) {
            _salary.setValue(value)
        }
}

⭕️ 위임 프로퍼티를 통해 프로퍼티 변경 통지 받기

//코틀린 관례에 맞게 수정한다. 
class ObservableProperty(var propValue: Int, val changeSupport: PropertyChangeSupport) {
    operator fun getValue(p: Person, prop: KProperty<*>): Int = propValue

    operator fun setValue(p: Person, prop: KProperty<*>, newValue: Int) {
        val oldValue = propValue
        propValue = newValue
        changeSupport.firePropertyChange(prop.name, oldValue, newValue)
    }
}

class Person(
    val name: String, age: Int, salary: Int
) : PropertyChangeAware() {
    var age: Int by ObservableProperty(age, changedSupport)
    var salary : Int by ObservableProperty(salary, changedSupport)
}

 

 

https://en.wikipedia.org/wiki/Delegation_pattern

 

Delegation pattern - Wikipedia

From Wikipedia, the free encyclopedia Design pattern in object-oriented programming In software engineering, the delegation pattern is an object-oriented design pattern that allows object composition to achieve the same code reuse as inheritance. In delega

en.wikipedia.org

https://kotlinlang.org/docs/delegation.html

 

Delegation | Kotlin

 

kotlinlang.org

https://kotlinlang.org/docs/delegated-properties.html

 

Delegated properties | Kotlin

 

kotlinlang.org

 

728x90
반응형