Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

We have a package that we are looking to convert to kotlin from python in order to then be able to migrate systems using that package.

Within the package there are a set of classes that are all variants, or 'flavours' of a common base class.

Most of the code is in the base class which has a significant number of optional parameters. So consider:

open class BaseTree(val height:Int=10,val roots:Boolean=true, //...... lots more!!

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

The code is not actually trees, I just thought this was a simple way to convey the idea. There are about 20 parameters to the base class, and around 10 subclasses, and each subclass effectively needs to repeat the same two variations of the parameter list from the base class. A real nightmare if the parameter list ever changes!

Those from a Java background may comment "20 parameters is too many", may miss that this is optional parameters, the language features which impacts this aspect of design. 20 required parameters would be crazy, but 10 or even 20 optional parameters is not so uncommon, check sqlalchemy Table for example.

In python, you to call a base class constructor you can have:

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

Does anyone know a technique, using a different method (perhaps using interfaces or something?) to avoid repeating this parameter list over and over for each subclass?

like image 409
innov8 Avatar asked Oct 31 '17 06:10

innov8


People also ask

Can we pass parameters to base class constructor?

To pass arguments to a constructor in a base class, use an expanded form of the derived class' constructor declaration, which passes arguments along to one or more base class constructors. Here, base1 through baseN are the names of the base classes inherited by the derived class.

How do you pass parameters to a constructor?

You can only define the coursebookname variable one time (which is when you specify the type). Remove the String designation from before the variable name when you pass it to the Person constructor and it should work fine. Person p1 = new Person(cousebookname); Spelling aside.

Which keyword do you use to pass parameters to the constructor of the base class?

The base keyword can be used with or without parameters. Any parameters to the constructor can be used as parameters to base , or as part of an expression.

How do you pass a context in a constructor in Kotlin?

Use a factory function to create your class. If you name the function like the class, it looks like a constructor. Define an interface with the same factory function and two objects for the scopes. Define a function that takes the scope and the initializer block.


3 Answers

There is no design pattern to simplify this use case.

Best solution: Refactor the code to use a more Java like approach: using properties in place of optional parameters.

Use case explained: A widely used class or method having numerous optional parameters is simply not practical in Java, and kotlin is most evolved as way of making java code better. A python class with 5 optional parameters, translated to Java with no optional parameters, could have 5! ( and 5 factorial is 60) different Java signatures...in other words a mess.

Obviously no object should routinely be instanced with a huge parameter list, so normall python classes only evolve for classes when the majority of calls do not need to specify these optional parameters, and the optional parameters are for the exception cases. The actual use case here is the implementation of a large number of optional parameters, where it should be very rare for any individual object to be instanced using more than 3 of the optional parameter. So a class with 10 optional parameters that is used 500 times in an application, would still expect 3 of the optional parameters to be the maximum ever used in one instance. But this is simply a design approach not workable in Java, no matter how often the class is reused.

In Java, functions do hot have optional parameters, which means this case where an object is instanced in this way in a Java library simply could never happen.

Consider an object with one mandatory instance parameter, and five possible options. In Java these options would each be properties able to be set by setters, and objects would then be instanced, and the setter(s) called for setting any relevant option, but infrequently required change to the default value for that option.

The downside is that these options cannot be set from the constructor and remain immutable, but the resultant code reduces the optional parameters.

Another approach is to have a group of less 'swiss army knife' objects, with a set of specialised tools replacing the one do-it-all tool, even when the code could be seen as just slightly different nuances of the same theme.

Despite the support for Optional parameters in kotlin, The inheritance structure in kotlin is not yet optimised for heavier use of this feature.

like image 87
innov8 Avatar answered Oct 15 '22 10:10

innov8


You can skip the name like BaseTree(height, roots) by put the variable in order but you cannot do things like Python because Python is dynamic language.

It is normal that Java have to pass the variables to super class too.

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

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

This is most likely a problem of your classes design.

like image 37
Joshua Avatar answered Oct 15 '22 10:10

Joshua


Reading your question I started to experiment myself and this is what I came up with:

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

Basically I define the parameters in an interface and extend the interface for sub-classes using the delegate pattern. For convenience I added functions to generate instances of those interface which also use default parameters.

I think this achieves the goal of repeating parameter lists quite nicely but also has its own overhead. Not sure if it is worth it.

like image 39
recke96 Avatar answered Oct 15 '22 11:10

recke96