简体   繁体   中英

I want to make a delegated class use properties of the class that delegates it in kotlin

I have these two classes:

class InfoDayViewModel(
    day: Day,
    application: Application
) : DayViewModel(day, application) {
    val dayDeleted = MutableLiveData<Boolean>().apply { value = false }
    fun deleteDay() {
        viewModelScope.launch {
            repository.deleteDay(day)
            dayDeleted.value = true
        }
    }
}

class InfoIdeaViewModel(
    idea: Idea,
    topic: Topic,
    application: Application
) : IdeaViewModel(idea, topic, application) {
    val ideaDeleted = MutableLiveData<Boolean>().apply { value = false }
    fun deleteIdea() {
        viewModelScope.launch {
            repository.deleteIdea(idea)
            ideaDeleted.value = true
        }
    }
}

They are very similar but I can't use a superclass because they already have superclasses. So I can create an interface like this one:

interface InfoItemViewModel {
    val itemDeleted: MutableLiveData<Boolean>
    fun deleteItem()
}

However, I will have to implement the functionality of the interfaces in both classes and I will have to repeat the code. So I can use composition and create a class that contains the implementation of the interface:

abstract class InfoItemViewModelImpl(private val coroutineScope: CoroutineScope) : InfoItemViewModel {
    override val itemDeleted = MutableLiveData<Boolean>().apply { value = false }
    override fun deleteItem() {
        coroutineScope.launch {
            deleteItemFromDatabase()
            itemDeleted.value = true
        }
    }
    abstract suspend fun deleteItemFromDatabase()
}

And I can make both classes implement this functionality like that:

class InfoIdeaViewModel(
    idea: Idea, topic: Topic, application: Application
) : IdeaViewModel(idea, topic, application), InfoItemViewModel {
    private val infoItemViewModel = object : InfoItemViewModelImpl(viewModelScope) {
        override suspend fun deleteItemFromDatabase() { repository.deleteIdea(idea) }
    }
    override val itemDeleted get() = infoItemViewModel.itemDeleted
    override fun deleteItem() { infoItemViewModel.deleteItem() }
}

class InfoDayViewModel(
    day: Day, application: Application
) : DayViewModel(day, application), InfoItemViewModel {
    private val infoItemViewModel = object : InfoItemViewModelImpl(viewModelScope) {
        override suspend fun deleteItemFromDatabase() { repository.deleteDay(day) }
    }
    override val itemDeleted get() = infoItemViewModel.itemDeleted
    override fun deleteItem() { infoItemViewModel.deleteItem() }
}

However, there is a lot of boilerplate code because in every class I have to create an object that contains the implementation and for every property and function make it use the object.

To solve this I thought about delegation like this:

class InfoDayViewModel(
    day: Day, application: Application
) : DayViewModel(day, application), InfoItemViewModel by object : InfoItemViewModelImpl(viewModelScope) {
    override suspend fun deleteItemFromDatabase() {
        repository.deleteDay(day)
    }
}

But that doesn't work. It doesn't recognize viewModelScope and it doesn't recognize repository. InfoItemViewModelImpl is created before InfoDayViewModel and that's the reason why it can't access these properties. How can I solve it?

Here's one possible workaround. It's less verbose than your solution if you're using this delegate in multiple classes. But it's not very elegant, since the age becomes a public read-write property.

interface CanRun {
    fun run(): String
    var age: Int
}

class CanRunImpl : CanRun {
    override var age by Delegates.notNull<Int>()
    override fun run(): String {
        return if (age > 10) "fast" else "slow"
    }
}

class Lion : CanRun by CanRunImpl() {
    init { age = 5 }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM