kotlin, how to simplify passing parameters to base class constructor?

☆樱花仙子☆ 提交于 2021-02-07 18:16:59

问题


We have a package that we are looking to convert to kotlin from python in order to then be able to migrate systems using that package.

Within the package there are a set of classes that are all variants, or 'flavours' of a common base class.

Most of the code is in the base class which has a significant number of optional parameters. So consider:

open class BaseTree(val height:Int=10,val roots:Boolean=true, //...... lots more!!

class FruitTree(val fruitSize, height:Int=10, roots:Boolean=true,
  //  now need all possible parameters for any possible instance
   ):BaseTree(height=height, roots=roots //... yet another variation of same list

The code is not actually trees, I just thought this was a simple way to convey the idea. There are about 20 parameters to the base class, and around 10 subclasses, and each subclass effectively needs to repeat the same two variations of the parameter list from the base class. A real nightmare if the parameter list ever changes!

Those from a Java background may comment "20 parameters is too many", may miss that this is optional parameters, the language features which impacts this aspect of design. 20 required parameters would be crazy, but 10 or even 20 optional parameters is not so uncommon, check sqlalchemy Table for example.

In python, you to call a base class constructor you can have:

def __init__(self, special, *args, **kwargs):
   super().__init(*args, **kwargs)  # pass all parameters except special to base constructor

Does anyone know a technique, using a different method (perhaps using interfaces or something?) to avoid repeating this parameter list over and over for each subclass?


回答1:


You can skip the name like BaseTree(height, roots) by put the variable in order but you cannot do things like Python because Python is dynamic language.

It is normal that Java have to pass the variables to super class too.

FruitTree(int fruitSize, int height, boolean root) {
    super(height, root);
}

There are about 20 parameters to the base class, and around 10 subclasses

This is most likely a problem of your classes design.




回答2:


If your subclass really has that many parameters in the constructur -> No way around that. You need to pass them all.

But (mostly) it's no good sign, that a constructor/function has that many parameters...

You are not alone on this. That is already discussed on the gradle-slack channel. Maybe in the future, we will get compiler-help on this, but for now, you need to pass the arguments yourself.




回答3:


There is no design pattern to simplify this use case.

Best solution: Refactor the code to use a more Java like approach: using properties in place of optional parameters.

Use case explained: A widely used class or method having numerous optional parameters is simply not practical in Java, and kotlin is most evolved as way of making java code better. A python class with 5 optional parameters, translated to Java with no optional parameters, could have 5! ( and 5 factorial is 60) different Java signatures...in other words a mess.

Obviously no object should routinely be instanced with a huge parameter list, so normall python classes only evolve for classes when the majority of calls do not need to specify these optional parameters, and the optional parameters are for the exception cases. The actual use case here is the implementation of a large number of optional parameters, where it should be very rare for any individual object to be instanced using more than 3 of the optional parameter. So a class with 10 optional parameters that is used 500 times in an application, would still expect 3 of the optional parameters to be the maximum ever used in one instance. But this is simply a design approach not workable in Java, no matter how often the class is reused.

In Java, functions do hot have optional parameters, which means this case where an object is instanced in this way in a Java library simply could never happen.

Consider an object with one mandatory instance parameter, and five possible options. In Java these options would each be properties able to be set by setters, and objects would then be instanced, and the setter(s) called for setting any relevant option, but infrequently required change to the default value for that option.

The downside is that these options cannot be set from the constructor and remain immutable, but the resultant code reduces the optional parameters.

Another approach is to have a group of less 'swiss army knife' objects, with a set of specialised tools replacing the one do-it-all tool, even when the code could be seen as just slightly different nuances of the same theme.

Despite the support for Optional parameters in kotlin, The inheritance structure in kotlin is not yet optimised for heavier use of this feature.




回答4:


Reading your question I started to experiment myself and this is what I came up with:

interface TreeProperties {
    val height: Int
    val roots: Boolean
}

interface FruitTreeProperties: TreeProperties {
    val fruitSize: Int
}

fun treeProps(height: Int = 10, roots: Boolean = true) = object : TreeProperties {
    override val height = height
    override val roots = roots
}

fun TreeProperties.toFruitProperty(fruitSize: Int): FruitTreeProperties = object: FruitTreeProperties, TreeProperties by this {
    override val fruitSize = fruitSize
}

open class BaseTree(val props: TreeProperties)

open class FruitTree(props: FruitTreeProperties): BaseTree(props)

fun main(args: Array<String>){
    val largTree = FruitTree(treeProps(height = 15).toFruitProperty(fruitSize = 5))
    val rootlessTree = BaseTree(treeProps(roots = false))
}

Basically I define the parameters in an interface and extend the interface for sub-classes using the delegate pattern. For convenience I added functions to generate instances of those interface which also use default parameters.

I think this achieves the goal of repeating parameter lists quite nicely but also has its own overhead. Not sure if it is worth it.



来源:https://stackoverflow.com/questions/47029272/kotlin-how-to-simplify-passing-parameters-to-base-class-constructor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!