user11367277
user11367277

Reputation:

Is there a more DRY way to use the Kotlin Delegate property observable?

I am using the observable pattern to track the changes in an object. To accomplish that I used the build in observable from Kotlin. Everything is working fine for me, butin order to track some changes I have to repeat the same code for every attribute. This is my code:

class Employee(
     id: String,
     name: String,
     surname: String,
     age: Int,
     salary: Int) {


     val changes = HashMap<String, Pair<Any, Any>>()


     val id = id //Id is immutable

var name: String by Delegates.observable(name) { prop, old, new ->
    if (old != new) {
        changes.put(prop.name, Pair(old, new))
        println("${prop.name} has changed from $old to $new")
    }
}

var surname: String by Delegates.observable(surname) { prop, old, new ->
    if (old != new) {
        changes.put(prop.name, Pair(old, new))
        println("${prop.name} has changed from $old to $new")
    }
}

var age: Int by Delegates.observable(age) { prop, old, new ->
    if (old != new) {
        changes.put(prop.name, Pair(old, new))
        println("${prop.name} has changed from $old to $new")
    }
}

var salary: Int by Delegates.observable(salary) { prop, old, new ->
    if (old != new) {
        changes.put(prop.name, Pair(old, new))
        println("${prop.name} has changed from $old to $new")
    }
 }

}

As you can see I am repeating these lines of code for every attribute:

by Delegates.observable(name) { prop, old, new ->
if (old != new) {
    changes.put(prop.name, Pair(old, new))
    println("${prop.name} has changed from $old to $new")
    }
 }

Does anyone have an idea to make this code more DRY, so that I don't have to copy and paste the lines everywhere, I did look online but could not find a way to define the logic elsewhere and aply it to all attributes in the class.

Upvotes: 2

Views: 305

Answers (1)

Zoe - Save the data dump
Zoe - Save the data dump

Reputation: 28228

You don't need to explicitly re-declare a method like that. You can pass method references in Kotlin quite easily. So you can do this:

val age = Delegates.observable(age, ::handler) // Notice `::handler`
// repeat for the others...

// And this is the actual function:
fun handler(prop: KProperty<*>, old: Any, new: Any){
    if (old != new) {
        changes.put(prop.name, old to new)
        println("${prop.name} has changed from $old to $new")
    }
}

::handler passes a method reference to a the method that handles them all. You'll still need to repeat the initialization, but you don't need to create the same handler method multiple times.

Also, if id is a val, and you don't do anything with it, you can just do this:

class Employee(
     val id: String, // Adding `val` or `var` in the constructor makes it an actual variable, instead of just local to the constructor. ***NOTE:*** This only applies to primary constructors. 
     name: String,
     surname: String,
     age: Int,
     salary: Int) {

Upvotes: 0

Related Questions