Home > Software design >  Collecting the values in the interface to a map
Collecting the values in the interface to a map

Time:02-17

I have an interface that stores the flags for the features that were enabled or no and that has a function to return the current values of the properties in this map:

interface IModuleEnabler {
    val module_1_parameter_1: Boolean
    val module_1_parameter_2: Boolean
    val module_1_parameter_3: Boolean

    val module_2_parameter_1: Boolean
    val module_2_parameter_2: Boolean
    val module_2_parameter_3: Boolean
    
    fun getPropertiesMap(): Map<String, List<Pair<String, Boolean>>> {
        var propertiesMap: Map<String, List<Pair<String, Boolean>>> = mutableMapOf()
        var properties = listOf<Pair<String, Boolean>>(
                 Pair<String, Boolean>("module_1_parameter_1", module_1_parameter_1),
                 Pair<String, Boolean>("module_1_parameter_2", module_1_parameter_2),
                 Pair<String, Boolean>("module_1_parameter_3", module_1_parameter_3)
             )
        propertiesMap = propertiesMap.plus("Module1" to properties)
        
        return propertiesMap
    }
}

This interface has a default class, which is implementing it and the custom class based on default to override the default values, if need. However, the issue is that the function getPropertiesMap() has always the default values and not the overriden ones. Is there a way to force update this function to have the actual values? Link to the playground - Kotlin Playground

CodePudding user response:

This is a shortcoming of delegates in Kotlin. The functions in the delegate have no knowledge of the class that is using it as a delegate and it will ignore overridden functions in that class.

It would be better to implement your getPropertiesMap function as an extension function so it cannot be overridden. This will fix your problem, but it's better practice anyway. When you have it defined inside your interface, it is left open and practically invites users to override it. Also, you're using a kind of convoluted way of creating a map.

fun IModuleEnabler.getPropertiesMap(): Map<String, List<Pair<String, Boolean>>> {
    val properties = listOf(
        Pair("module_1_parameter_1", module_1_parameter_1),
        Pair("module_1_parameter_2", module_1_parameter_2),
        Pair("module_1_parameter_3", module_1_parameter_3)
    )
    return mapOf("Module1" to properties)
}

I would do it without all the overriding and delegation anyway. Allow the Default implementation to set the values from the constructor. The custom implementation can simply call the super-constructor with the different values it wants.

open class DefaultFeatureEnabler(
    override val module_1_parameter_1: Boolean = false,
    override val module_1_parameter_2: Boolean = false,
    override val module_1_parameter_3: Boolean = false,

    override val module_2_parameter_1: Boolean = false,
    override val module_2_parameter_2: Boolean = false,
    override val module_2_parameter_3: Boolean = false
): IModuleEnabler

class CustomFeatureEnabler: DefaultFeatureEnabler(
    module_1_parameter_3 = true,
    module_2_parameter_1 = true
)

CodePudding user response:

This is because CustomFeatureEnabler delegates getPropertiesMap to DefaultFeatureEnabler. You can fix it by overriding getPropertiesMap in classes where you use delegation

class CustomFeatureEnabler : IModuleEnabler by DefaultFeatureEnabler() {
  override val module_1_parameter_3: Boolean get() = true
    
  override val module_2_parameter_1: Boolean get() = true

  override fun getPropertiesMap() = super.getPropertiesMap() // <---
}

https://kotlinlang.org/docs/delegation.html#overriding-a-member-of-an-interface-implemented-by-delegation

  • Related