Philosophy of what controls task initialization

I was having a conversation with Benjamin Muschko in a github issue about something completely unrelated to the issue, so he suggested I bring the conversation here (I was about to anyway).

I’ve been writing code for a very long time, but I’m very new to Gradle. I think I have a pretty good feel for how to balance coupling and cohesion in a design, but sometimes the “right” answer might not be obvious in the context of a particular framework. I think it’s also possible and highly likely that sometimes the “right” answer just doesn’t matter in real life, but I still think it’s worthwhile exploring this for a bit.

The question has to do with “who” should have responsibility to initialize a task instance. I recently wrote my first Gradle plugin. The plugin defines a single task, and the plugin uses an extension block to define the properties that are used to configure a single instance of the task, which is created and configured from the plugin’s “apply” method.

I designed the plugin with the goal of having the user only needing to “apply” the plugin and add the extension block with properties, and the plugin and single task instance would do everything necessary. It is technically possible for the build script to define another instance of the task, but in the context of builds that might use this plugin, there’s no clear reason why anyone would ever do that.

Inside the “apply” method, I thought about how best to set the task’s properties from the properties in the extension block. I could have called each setter method in the task, passing in an extension property. What I chose to do is let the task initialize itself with the extension properties it requires, by simply passing the extension block to the task in an “init()” method. The plugin’s “apply” method also does other work, like setting task dependencies and the configuration of other tasks.

Without going into an endless navel-gazing exercise here, what do you think about the tradeoff of having the task initialize itself, as opposed to having the plugin call all relevant setters? Obviously, the plugin is still “controlling” the initialization by calling the task’s “init” method, but the task hides the details of that work.

This tradeoff has similarities to dependency injection, but I think that’s only relevant if you can really extract the configuration of DI outside of the logic, which isn’t the case here.

If it matters, here’s my plugin class, and the task class is in the same package: https://github.com/davidmichaelkarr/GradleYangPlugin/blob/master/src/main/groovy/com/att/opnfv/yang/gradle/YangPlugin.groovy .