Ali
Ali

Reputation: 9994

Avoid redundant code when having inheritance in Kotlin

I have following base class :

abstract class BaseViewModel<T, R>(private val schedulerProvider: BaseSchedulerProvider) :
    ViewModel() {

    private val compositeDisposable = CompositeDisposable()

    private val _liveData = MutableLiveData<Resource<T>>()
    val liveData: LiveData<Resource<T>>
        get() = _liveData

    protected abstract val requestObservable: Observable<R>

    protected abstract fun getSuccessResult(it: R): T

    fun sendRequest() {
        _liveData.value = Resource.Loading()
        composeObservable { requestObservable }
            .subscribe({
                _liveData.postValue(Resource.Success(getSuccessResult(it)))
            }) {
                _liveData.postValue(Resource.Failure(it.localizedMessage))
                Timber.e(it)
            }.also { compositeDisposable.add(it) }
    }
}

And here is child class implementation :

class MainViewModel(
    api: PokemonService,
    schedulerProvider: BaseSchedulerProvider
) : BaseViewModel<List<Pokemon>, List<NamedResponseModel>>(schedulerProvider) {

    override val requestObservable: Observable<List<NamedResponseModel>> =
        api.getPokemonList(LIMIT).map { it.results }

    override fun getSuccessResult(it: List<NamedResponseModel>): List<Pokemon> = it.asDomainModel()

    init {
        sendRequest()
    }
}

As you see I put init block in child classes to sendRequest() which is a redundant. If I move init block to parent class, it will crash since api is null because init block of parent is called before constructor of child.

Is there any solution to move sendRequest() to parent and avoid redundant in child classes?

Source code can be found : https://github.com/AliRezaeiii/Pokemon

Upvotes: 0

Views: 238

Answers (1)

dee cue
dee cue

Reputation: 1043

I think you need to change the design of your inheritance. To get the child items to be executed in the parent's initialization, you need to pass the object to the parent constructor.

Here is an example:

abstract class Base(protected val name: String) {
    init {
        println(name)
    }
}

class CBase(private val s: String) : Base(s) {}

fun main() {
    CBase("Hello");
}

In your case, which I haven't tested yet:

abstract class BaseViewModel<T, R>(
    private val schedulerProvider: BaseSchedulerProvider,
    protected val requestObservable: Observable<R>):
    ViewModel() {

    private val compositeDisposable = CompositeDisposable()

    private val _liveData = MutableLiveData<Resource<T>>()
    val liveData: LiveData<Resource<T>>
        get() = _liveData

    protected abstract fun getSuccessResult(it: R): T

    fun sendRequest() {
        _liveData.value = Resource.Loading()
        composeObservable { requestObservable }
            .subscribe({
                _liveData.postValue(Resource.Success(getSuccessResult(it)))
            }) {
                _liveData.postValue(Resource.Failure(it.localizedMessage))
                Timber.e(it)
            }.also { compositeDisposable.add(it) }
    }

    init {
        sendRequest()
    }
}

class MainViewModel(
    api: PokemonService,
    schedulerProvider: BaseSchedulerProvider
) : BaseViewModel<List<Pokemon>, List<NamedResponseModel>>(
    schedulerProvider,
    api.getPokemonList(LIMIT).map { it.results }
) {

    override fun getSuccessResult(it: List<NamedResponseModel>): List<Pokemon> = it.asDomainModel()
}

Here, you can still access the variable requestObservable at the parent's contructor because it is initialized at the constructor parameter, not as an abstract property.

Let me know how it works for you.

Upvotes: 1

Related Questions