위임, Delegate, - by Kotlin

by lazy: lazy는 다음과 같이 lazy initialization 상황에 쓰여지는 기법

class Email {

}

fun loadEmails(name:String):List<Email> {
println("Load Emails")
return listOf(Email(),Email())
}

class Person(val name:String) {
private var _emails:List<Email>?=null
val emails:List<Email>
get() {
if (_emails==null) {
_emails= loadEmails(this.name)
}
return _emails!!
}
}

fun a() {
val p = Person("Alice")
p.emails
}
--> 

class Person1(val name: String) {
val emails by lazy { loadEmails(name) }
}

PropertyChangeSupport
변수값 변경 이벤트를 발생시키기 위한 자바빈 클래스 PropertyChangeSupport를 사용하여 구현
1번째 시도

open class PropertyChangeAware {
protected val changeSupport = PropertyChangeSupport(this)
fun addPropertyChangeListener(listener: PropertyChangeListener) {
changeSupport.addPropertyChangeListener(listener)
}

fun removePropertyChangeListener(listener: PropertyChangeListener) {
changeSupport.removePropertyChangeListener(listener)
}
}

class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware {
var age: Int = age
set(value) {
val oldValue = field
field = value
changeSupport.firePropertyChange("age", oldValue, value)
}
var salary: Int = salary
set(value) {
val old = field
field = value
changeSupport.firePropertyChange("salary", old, value)
}
}

fun a() {
val p = Person("Dmitry", 34, 2000)
p.addPropertyChangeListener(PropertyChangeListener { event ->
println("Property ${event.propertyName} changed from ${event.oldValue} to ${event.newValue}")
})
}

2번째 시도
프로퍼티 값을 저장하고 그 값이 바뀌면 자동으로 변경 통지를 전달해주는 클래스를 만듬, 로직의 중복을 상당부분 제거

open class PropertyChangeAware {
protected val changeSupport = PropertyChangeSupport(this)
fun addPropertyChangeListener(listener: PropertyChangeListener) {
changeSupport.addPropertyChangeListener(listener)
}

fun removePropertyChangeListener(listener: PropertyChangeListener) {
changeSupport.removePropertyChangeListener(listener)
}
}

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

class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware() {
val _age = ObservableProperty("age", age, changeSupport)
var age: Int
get() = _age.getValue()
set(value) {
_age.setValue(value)
}
var _salary = ObservableProperty("salary", salary, changeSupport)
var salary: Int
get() {
_salary.getValue()
}
set(value) {
_salary.setValue(value)
}
}
3번째 시도
코틀린의 by 키워드를 통한 위임을 사용한 경우

open class PropertyChangeAware {
protected val changeSupport = PropertyChangeSupport(this)
fun addPropertyChangeListener(listener: PropertyChangeListener) {
changeSupport.addPropertyChangeListener(listener)
}

fun removePropertyChangeListener(listener: PropertyChangeListener) {
changeSupport.removePropertyChangeListener(listener)
}
}

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

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 old = propValue
propValue = newValue
changeSupport.firePropertyChange(prop.name, old, newValue)
}
}

위임을 사용할 수 있는 형식을 만드려면 operator fun setValue, operator fun getValue 를 구현하면 됨

4번째 시도

open class PropertyChangeAware {
protected val changeSupport = PropertyChangeSupport(this)
fun addPropertyChangeListener(listener: PropertyChangeListener) {
changeSupport.addPropertyChangeListener(listener)
}

fun removePropertyChangeListener(listener: PropertyChangeListener) {
changeSupport.removePropertyChangeListener(listener)
}
}

class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware() {
private val observer = { prop: KProperty<*>, old: Int, value: Int ->
changeSupport.firePropertyChange(prop.name, old, value)
}
var age: Int by Delegates.observable(age, observer)
var salary: Int by Delegates.observable(salary, observer)
}

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 old = propValue
propValue = newValue
changeSupport.firePropertyChange(prop.name, old, newValue)
}
}


멤버변수 대신 맵을 갖는 클래스에서 위임 응용

class Person {
private val _attributes = hashMapOf<String, String>()
fun setAttribute(attrName: String, value: String) {
_attributes[attrName] = value
}

val name: String
get() {
return _attributes["name"]!!
}
}

fun a() {
val p = Person()
val data = mapOf("name" to "Dmitry", "company" to "JetBrains")
for ((attrName, value) in data) {
p.setAttribute(attrName, value)
}
println(p.name)
}
이를 위임 by를 사용하도록 변경한다

class Person {
private val _attributes = hashMapOf<String, String>()
fun setAttribute(attrName: String, value: String) {
_attributes[attrName] = value
}

val name: String by _attributes
}

Map과 MutableMap 에 대해 getValue, setValue 확장함수를 제공하기 때문임


덧글

댓글 입력 영역