On 15/01/2013, at 21:45, Adam Murdoch <[email protected]> wrote:
>
> On 15/01/2013, at 10:27 PM, Luke Daley wrote:
>
>> Wanted to get a thread started on ideas for how this might work.
>>
>> The problem generally is that our containers internalise construction, and
>> offer no way to parameterise it.
>>
>> sourceSets {
>> custom {}
>> }
>>
>> The use has no way of influencing what kind of thing “custom” is. The
>> immediate need for this kind of thing is with the publications container.
>> The user needs to be able to add arbitrary maven, ivy etc. publication
>> objects.
>>
>> Options:
>>
>> 1. Add via concrete types, with rigid constructor contract
>>
>> To add something of a certain type, you specify the actual type, which must
>> have a 1 string arg constructor (the item name)…
>>
>> publications {
>> custom(DefaultMavenPublication)
>> }
>>
>> class DefaultMavenPublication implements MavenPublication {
>> DefaultMavenPublication(String name) { … }
>> }
>>
>> 2. Add via concrete types, allowing constructor params
>>
>> publications {
>> custom(DefaultMavenPublication, arg1, arg2)
>> }
>>
>> class DefaultMavenPublication implements MavenPublication {
>> DefaultMavenPublication(String name, Object arg1, Object arg2) { … } //
>> arg1 & arg2 could be typed of course
>> }
>>
>> 3. Add via contract types, using rigid factories…
>>
>> publications.registerTypeFactory(new MavenPublicationFactory())
>> publications {
>> custom(MavenPublication) {}
>> }
>>
>> class MavenPublicationFactory implements
>> DomainObjectFactory<MavenPublication> {
>> MavenPublication createDomainObject(String name) {
>> instantiator.newInstance(DefaultMavenPublication, name)
>> }
>> }
>>
>> (not suggesting that as the DomainObjectFactory API, just illustrative)
>>
>> 4. Add via contract types, allowing construction params…
>>
>> publications.registerTypeFactory(new MavenPublicationFactory())
>> publications {
>> custom(MavenPublication) {}
>> }
>>
>> class MavenPublicationFactory implements
>> DomainObjectFactory<MavenPublication> {
>> MavenPublication createDomainObject(String name, Object...
>> constructionArgs) {
>> // constructionArgs doesn't necessarily need to be passed to
>> the constructor
>> instantiator.newInstance(DefaultMavenPublication, name,
>> *constructionArgs)
>> }
>> }
>>
>> 5. Add via contract types, using typed builders to parameterise
>>
>> publications.registerTypeBuilder(MavenPublicationBuilder)
>> publications {
>> custom(MavenPublication, «configure the builder by map») {
>> someMethodOnMavenPublication()
>> }
>> }
>>
>> publications.registerTypeBuilder(MavenPublicationBuilder)
>> publications {
>> custom MavenPublication), {
>> « configure builder by closure »
>> }, {
>> someMethodOnMavenPublication()
>> }
>> }
>> class MavenPublicationBuilder implements
>> DomainObjectBuilder<MavenPublication> {
>>
>> MavenPublicationBuilder(String domainObjectName, Instantiator
>> instantiator) { … }
>>
>> MavenPublication build() {
>> instantiator.newInstance(DefaultMavenPublication, name)
>> }
>> }
>>
>>
>> I think option 3 is the most desirable, if we can avoid the need to
>> parameterise what gets created at construction time. 5 offers the most
>> flexibility, but I can't think of a way to avoid making the DSL so
>> cumbersome and to avoid the complexity.
>>
>> Perhaps the two aren't mutually exclusive. We could start with 3, and then
>> add something like 5 later if it turns out to be necessary.
>>
>> 1 and 2 are non starters for me. There needs to be indirection between the
>> requested and concrete type.
>>
>> I don't like 4 because it's so loose. There's no compile time typing and no
>> good way to specify the contract.
>>
>>
>> Given those options (feel free to propose others), I'd vote for starting
>> with 3 I think.
>
> I agree. It's actually what Daz ended up implementing, too, so that works out
> well.
>
> But I don't think it's quite right yet, from a plugin author's point of view.
> There are a few things we still need to sort out, which influence how well
> these options work:
>
> 1. A public way to do dependency injection into these domain objects. This to
> me is a good option for avoiding construction parameters.
>
> 2. A public way to do DSL injection. Every object that ends up in these
> containers needs to be DSL-aware.
>
> 3. We want the code to be as declarative as possible. When things are
> statically declared we can infer things like the dependencies between things,
> we can include interesting information in the DSL reference, and we can infer
> which plugins provide which types of things for implicit plugin application,
> and so on. When opaque factories are used, we cannot do this useful stuff.
How so? I don't see much of a difference in terms of declaritiveness inherently.
>
> 4. We need to open up some of the domain object types for extension. For
> example, we want people to be able to provide their own component
> implementations, report implementations, and so on. I think the approach
> we've used for tasks actually works pretty well in practise, as far as
> extensibility goes (assuming we've moved the TaskInternal stuff off
> DefaultTask), so I would suggest generalising this approach somewhat:
>
> - An abstract contract type (eg Task)
> - One or more public implementations (eg DefaultTask) open for
> extension.
> - The public implementations have a zero-args constructor and rely on
> (field) dependency injection to receive the internal services they require.
> - Instances must be managed by Gradle - you can't use new Thing().
> - Class decoration injects whatever internal stuff is needed (eg
> TaskInternal)
> - When a type is not specified at construction time, or when a contract
> type is specified, then an appropriate implementation type is selected.
>
> I think generally we want using a factory to be a last resort for the plugin
> author. Instead we want some way for a plugin author to say 'I provide
> instances of this implementation type' and separately for a consumer
> (possibly the plugin author) to say 'please give me an instance of this type'
> and for Gradle to take care of the rest.
>
>
> --
> Adam Murdoch
> Gradle Co-founder
> http://www.gradle.org
> VP of Engineering, Gradleware Inc. - Gradle Training, Support, Consulting
> http://www.gradleware.com
>