kotlin,如何简化传递参数到基类构造函数?

问题描述 投票:2回答:4

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

在包中有一组类,它们都是变体,或者是公共基类的“风味”。

大多数代码都在基类中,它具有大量可选参数。所以考虑:

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

代码实际上不是树,我只是认为这是传达这个想法的简单方法。基类有大约20个参数,大约有10个子类,每个子类实际上需要从基类重复参数列表的相同两个变量。如果参数列表发生变化,真是个噩梦!

那些来自Java背景的人可能会评论“20个参数太多”,可能会错过这是可选参数,这些语言特征会影响设计的这个方面。 20个必需参数会很疯狂,但10个甚至20个可选参数并不少见,例如检查sqlalchemy Table

在python中,您可以调用基类构造函数:

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

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

constructor kotlin base-class
4个回答
2
投票

您可以通过按顺序放置变量来跳过BaseTree(height, roots)之类的名称,但是由于Python是动态语言,因此您无法执行Python之类的操作。

Java必须将变量传递给超类也是正常的。

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

基类大约有20个参数,大约有10个子类

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


1
投票

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

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

使用案例解释:广泛使用的类或方法具有许多可选参数在Java中根本不实用,而kotlin最常被演化为更好地编写Java代码的方法。一个带有5个可选参数的python类,转换为Java,没有可选参数,可以有5个! (和5阶乘是60)不同的Java签名......换句话说是一团糟。

显然,没有对象应该通过一个巨大的参数列表来实例化,因此当大多数调用不需要指定这些可选参数时,normall python类只会针对类进行演变,并且可选参数用于异常情况。这里的实际用例是大量可选参数的实现,其中使用多于3个可选参数对任何单个对象进行实例化的情况应该非常少见。因此,在应用程序中使用了500次可选参数的类仍将期望3个可选参数是在一个实例中使用的最大值。但这只是一种在Java中无法工作的设计方法,无论该类重用的频率如何。

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

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

缺点是这些选项不能从构造函数设置并保持不可变,但结果代码会减少可选参数。

另一种方法是使用一组较少的“瑞士军刀”物体,用一套专用工具代替一个全能工具,即使代码可以被视为同一主题的细微差别。

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


0
投票

如果你的子类在构造函数中确实有那么多参数 - >没办法。你需要将它们全部传递出去。

但(大多数情况下)没有好的迹象,构造函数/函数有很多参数......

你并不孤单。这已经在gradle-slack频道上进行了讨论。也许在将来,我们将获得编译器帮助,但是现在,您需要自己传递参数。


0
投票

阅读你的问题我开始尝试自己,这就是我想出的:

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.