kotlin,如何简化将参数传递给 base class 构造函数?

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

我们有一个包,我们希望将其从 python 转换为 kotlin,以便能够使用该包迁移系统。

包中有一组 class 都是变体,或者 'flavours' 共同基础 class。

大部分代码都在基础 class 中,其中有大量可选参数。所以考虑:

open class BaseTree(val height:Int=10,val roots:Boolean=true, //......还有很多!!

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

代码实际上不是树,我只是认为这是传达想法的一种简单方式。 base class 大约有 20 个参数,大约有 10 个 subclasses,每个 subclass 实际上需要从 base [=] 重复参数列表的相同两个变体40=]。如果参数列表发生变化,那将是一场真正的噩梦!

有Java背景的人可能会评论“20个参数太多了”,可能会忽略这是可选参数,语言特性这会影响设计的这一方面。 20 个必需参数会很疯狂,但是 10 个甚至 20 个可选参数并不少见,例如检查 sqlalchemy Table

在 python 中,你调用一个基础 class 构造函数你可以有:

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

有没有人知道一种技术,使用不同的方法(可能使用接口或其他方法?)来避免为每个子class 一遍又一遍地重复此参数列表?

如果你的子类在构造函数中真的有那么多参数 -> 没办法。你需要全部通过。

但是(大多数情况下)这不是一个好兆头,constructor/function 有那么多参数...

你并不孤单。 gradle-slack 频道已经对此进行了讨论。也许在未来,我们会得到编译器的帮助,但现在,你需要自己传递参数。

您可以通过按顺序排列变量来跳过 BaseTree(height, roots) 这样的名称,但是您不能像 Python 这样的操作,因为 Python 是动态语言。

Java 也要把变量传递给 super class 是正常的。

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

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

这很可能是您的 classes 设计的问题。

没有设计模式来简化这个用例。

最佳解决方案:重构代码以使用更 Java 的方法:使用属性代替可选参数。

用例解释:广泛使用的 class 或具有大量可选参数的方法在 Java 中根本不实用,并且 kotlin 最先进的是使 java 代码更好的方法.带有 5 个可选参数的 python class,转换为没有可选参数的 Java,可能有 5 个! (5 个阶乘是 60)不同的 Java 签名……换句话说就是一团糟。

显然,任何对象都不应例行地使用庞大的参数列表进行实例化,因此通常 python classes 仅在大多数调用不需要指定的情况下演化为 classes这些可选参数,可选参数是针对异常情况的。这里的实际用例是大量可选参数的实现,其中使用超过 3 个可选参数实例化任何单个对象的情况应该非常少见。因此,具有 10 个可选参数的 class 在一个应用程序中使用了 500 次,仍然期望 3 个可选参数是在一个实例中使用过的最大值。但这只是一种在 Java 中行不通的设计方法,无论 class 被重复使用的频率如何。

在 Java 中,函数确实有可选参数,这意味着在 Java 库中以这种方式实例化对象的情况永远不会发生。

考虑一个具有一个强制实例参数和五个可能选项的对象。在 Java 中,这些选项每个都是可以由 setters 设置的属性,然后对象将被实例化,并且 setter(s) 要求设置任何相关选项,但很少需要更改该选项的默认值。

缺点是这些选项不能从构造函数中设置并保持不变,但生成的代码减少了可选参数。

另一种方法是拥有一组更少的 'swiss army knife' 个对象,用一组专门的工具代替一个全能的工具,即使代码可以被视为与相同的主题。

尽管 kotlin 中支持可选参数,但 kotlin 中的继承结构尚未针对大量使用此功能进行优化。

阅读你的问题后,我开始自己做实验,这就是我想出的:

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))
}

基本上,我在接口中定义参数,并使用委托模式为子 类 扩展接口。为了方便起见,我添加了函数来生成那些也使用默认参数的接口的实例。

我认为这很好地实现了重复参数列表的目标,但也有其自身的开销。不确定是否值得。