Partiamo dal problema: le closure. Facciamo un paio di esempi:
this.HasMany( e => e.Advertisements )
    .KeyColumnNames.Add( "PublishQueue" )
    .Cascade.AllDeleteOrphan()
    .Access.AsCamelCaseField( Prefix.Underscore );
tratto da un mapping con Fluent NHibernate, e:
var fc = this.container.Resolve<FluentConfiguration>();
fc.Mappings( mp => mp.FluentMappings.AddFromAssemblyOf<PublicationModule>() );
tratto invece dalla configurazione di un modulo e relativo al caricamento dei mapping di cui sopra.
La differenza è evidente, se avete mai provato a scrivere un’interfaccia fluente vi siete scontrati proprio con la necessità di capire come e quando l’utilizzatore del vostro codice ha “finito”, vediamo un esempio più casalingo (nel senso che l’ho fatto io):
Worker.UsingAsArgument( ... )
    .AndExpectingAsResult<IEnumerable<...>>()
    .WhenExecutedDo( arg =>
    {
        ...
    } )
    .ButBeforeDo( arg =>
    {
        ...
    } )
    .AndAfterDo( arg =>
    {
        ...
    } )
    .Execute();
Notate la chiamata a “Execute()”? questa è una closure, è necessaria per far si che l’esecuzione asincrona inizi, ma è l’unico modo? no :-)
Dopo un massiccisimo refacroring, di cui parleremo ampiamente, giungiamo a questo:
AsyncWorker.Using( ... )
    .AndExpecting<IEnumerable<...>>()
    .Execute( cfg =>
    {
        cfg.Before = e => { ... };

        cfg.After = e => { ... };

        cfg.Async = e => { ... };
    } );
che a mio modo di vedere è decisamente, ma decisamente, meglio ;-)
Adesso la closure è implicita, il metodo Execute() prende/passa un delegato il cui scopo è configurare l’istanza del Worker, è evidente che al termine del metodo Execute tutto è pronto per essere eseguito senza la necessità di chiamare un metodo in più.
.m