Closed aoli-al closed 8 months ago
Another solution is to use composition instead of inheritance.
What exactly would that solution look like?
Similar to this https://github.com/ajalt/clikt/issues/453#issuecomment-1718994678
If I can create a data object
data class Configuration(val @Option("clazz", type=String...) clazz: String, val: report: String, val targetArgs: AlgoOption) { // if we can configure the options through argument annotations.
init(args: CliKt) { // If CliKt can generate this constructor automatically...
init(args.clazz, args.report, args.algo)
}
}
and construct the data class through clikt in my main program
val config: Configuration = parser.parse(args)
Then I can just construct my Configuration
object in unit tests freely.
val config = Configuration("", ...)
Thanks for the example. There are lots of annotation based CLI libraries out there, and I touched on the disadvantages of them in the docs. They require reflection and code generation, they're less type safe, and are harder to customize than Clikt. But feel free to use one if you prefer; Clikt will never be an annotation based library.
It seems that the folks who have issues with Clikt's inheritance are trying to use a CliktCommand as their data model. Clikt is UI, and like all UI, coupling it to your data layer can make it harder to test. You probably wouldn't define your data model in HTML fields on DOM nodes, or on properties on an Android View.
It's easy to make a plain data class from a command:
fun MyCommand.toModel() = MyModel(
foo=foo,
bar=bar,
//...
)
val model = MyCommand().apply{ main(argv) }.toModel()
I see, Thanks for your explanation. It makes sense to me.
Hi, thanks for maintaining such a great package!
I have a small
Configuration
class is used across my application. I coupled this with ClicktCommand so that I can construct myConfiguration
object right from user commands.However, this becomes painful while writing unit tests. In order to construct a
Configuration
object, the only way is to callparse
manually and pass strings. This is not ideal because I can't get many type information while writing this code. One potential solution is to also add setters forargument()
andoption()
as well so that I can just create aval config = Configuration()
and then config them manually in my unit testconfig.algorithm = Algo1()
. Another solution is to use composition instead of inheritance. I saw several discussion here #371 and #453. It seems that this scenario is not discussed.