我们有一个我们希望从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
有没有人知道一种技术,使用不同的方法(可能使用接口或其他东西?)来避免为每个子类反复重复这个参数列表?
答案 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))
}
基本上,我在接口中定义参数,并使用委托模式将接口扩展为子类。为了方便起见,我添加了一些函数来生成也使用默认参数的那些接口的实例。
我认为这可以很好地实现重复参数列表的目标,但也有其自身的开销。不确定是否值得。