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
> 

Reply via email to