我们有一个包,我们希望从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
有没有人知道一种技术,使用不同的方法(可能使用接口或其他东西?)来避免为每个子类反复重复这个参数列表?
您可以通过按顺序放置变量来跳过BaseTree(height, roots)
之类的名称,但是由于Python是动态语言,因此您无法执行Python之类的操作。
Java必须将变量传递给超类也是正常的。
FruitTree(int fruitSize, int height, boolean root) {
super(height, root);
}
基类大约有20个参数,大约有10个子类
这很可能是您的课程设计的问题。
没有设计模式来简化这个用例。
最佳解决方案:重构代码以使用更类似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中的继承结构尚未针对此功能的更多使用进行优化。
如果你的子类在构造函数中确实有那么多参数 - >没办法。你需要将它们全部传递出去。
但(大多数情况下)没有好的迹象,构造函数/函数有很多参数......
你并不孤单。这已经在gradle-slack频道上进行了讨论。也许在将来,我们将获得编译器帮助,但是现在,您需要自己传递参数。
阅读你的问题我开始尝试自己,这就是我想出的:
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))
}
基本上我在接口中定义参数,并使用委托模式扩展子类的接口。为方便起见,我添加了一些函数来生成那些也使用默认参数的接口实例。
我认为这实现了很好地重复参数列表的目标,但也有自己的开销。不确定它是否值得。