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

时间:2017-10-31 06:39:04

标签: constructor kotlin base-class

我们有一个我们希望从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

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

4 个答案:

答案 0 :(得分:2)

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

Java也必须将变量传递给超类。

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

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

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

答案 1 :(得分: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中的继承结构尚未针对此功能的更多使用进行优化。

答案 2 :(得分:0)

如果你的子类在构造函数中确实有那么多参数 - >没办法解决这个问题。你需要全部通过它们。

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

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

答案 3 :(得分: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))
}

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

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