Fluently.Design().Me e il vero problema del design di una fluent interface
tratto da un mapping con Fluent NHibernate, e:this.HasMany( e => e.Advertisements ) .KeyColumnNames.Add( "PublishQueue" ) .Cascade.AllDeleteOrphan() .Access.AsCamelCaseField( Prefix.Underscore );
tratto invece dalla configurazione di un modulo e relativo al caricamento dei mapping di cui sopra.var fc = this.container.Resolve<FluentConfiguration>(); fc.Mappings( mp => mp.FluentMappings.AddFromAssemblyOf<PublicationModule>() );
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):
Notate la chiamata a “Execute()”? questa è una closure, è necessaria per far si che l’esecuzione asincrona inizi, ma è l’unico modo? no :-)Worker.UsingAsArgument( ... ) .AndExpectingAsResult<IEnumerable<...>>() .WhenExecutedDo( arg => { ... } ) .ButBeforeDo( arg => { ... } ) .AndAfterDo( arg => { ... } ) .Execute();
Dopo un massiccisimo refacroring, di cui parleremo ampiamente, giungiamo a questo:
che a mio modo di vedere è decisamente, ma decisamente, meglio ;-)AsyncWorker.Using( ... ) .AndExpecting<IEnumerable<...>>() .Execute( cfg => { cfg.Before = e => { ... }; cfg.After = e => { ... }; cfg.Async = e => { ... }; } );
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