2014-05-11 10 views
12

Ho appena provato a conoscere PostSharp e onestamente penso sia sorprendente.Alternativa PostSharp

Ma una cosa per me è difficile come un'iniezione pura dipendenza (non localizzatore di servizi) cannot be done in aspetti PostSharp, forse nella mia comprensione come risultato della tessitura a tempo di compilazione.

È venuto da PHP in background, Symfony ha JMSAopBundle che consente ancora di essere immesso nella dipendenza da Interceptor.

Does. Net ha alcune librerie con le stesse funzionalità?

O mi manca qualcosa con PostSharp?

+3

Se sei già interessato a Dependency Injection puro, PostSharp è completamente ridondante. Invece, è possibile selezionare un contenitore DI che supporti l'intercettazione in fase di esecuzione: http: // stackoverflow.it/a/7906547/126014 –

+1

E andrebbe anche oltre affermando che l'intercettazione del tempo di esecuzione è ridondante. – Steven

+0

@Steven Penso che sia più flessibile piuttosto che ridondante. L'intercettazione del tempo di compilazione non attenderà alcuna inizializzazione del runtime di alcunché. –

risposta

15

Non penso che manchi qualcosa qui e la limitazione è davvero il risultato dell'utilizzo della tessitura a tempo compilabile.

Anche se penso che gli strumenti di tessitura del tempo di compilazione abbiano il loro posto nello sviluppo del software, sento che sono spesso abusati. Spesso li vedo usati per correggere difetti nella progettazione dell'applicazione. Nelle applicazioni che costruisco, applico interfacce generiche a determinati concetti architettonici. Ad esempio, definisco:

  • un'interfaccia ICommandHandler<TCommand> per servizi che implementano un determinato caso d'uso;
  • un'interfaccia IQueryHandler<TQuery, TResult> per servizi che eseguono una query;
  • un'interfaccia IRepository<TEntity> come astrazione su repository;
  • un'interfaccia IValidator<TCommand> per componenti che eseguono la convalida del messaggio;
  • e così via, e così via.

Questo mi permette di creare un unico decoratore generico per tale gruppo di manufatti (ad esempio un TransactionCommandHandlerDecorator<TCommand> che consente l'esecuzione di ogni caso d'uso nella propria transazione). L'uso dei decoratori ha molti vantaggi, come ad esempio:

  • Questi decoratori generici sono completamente indipendenti dagli strumenti, poiché non vi è alcun riferimento a uno strumento di tessitura del codice oa un quadro di intercettazione. Gli aspetti PostSharp dipendono completamente da PostSharp e gli intercettatori dipendono sempre da un framework di intercettazione, ad esempio Castle.Proxy.
  • Poiché un decoratore è solo un componente normale, le dipendenze possono essere iniettate nel costruttore e possono svolgere un ruolo normale quando si compongono i grafici degli oggetti utilizzando Iniezione di dipendenza.
  • Il codice decoratore è molto pulito, poiché la mancanza di dipendenza con qualsiasi strumento di terze parti.
  • Poiché sono indipendenti dallo strumento e consentono l'iniezione di dipendenza, i decoratori possono essere facilmente testati in un'unità senza dover ricorrere a trucchi speciali.
  • Il codice di applicazione che richiede l'applicazione di problemi trasversali da applicare può anche essere testato facilmente in isolamento, poiché i decoratori non sono intessuti in fase di compilazione. Quando i decoratori vengono intrecciati in fase di compilazione, sei sempre costretto a fare uno stile di integrazione per testare il codice dell'applicazione, o devi ricorrere a trucchi di build speciali per impedire che vengano applicati al progetto di test dell'unità.
  • I decoratori possono essere applicati dinamicamente e condizionalmente in fase di esecuzione, poiché non è in corso la tessitura del codice temporale.
  • Le prestazioni sono identiche (o anche più veloci) rispetto alla tessitura del codice, poiché durante la costruzione dell'oggetto non avviene alcuna riflessione.
  • Non è necessario contrassegnare i componenti con attributi per notare che alcuni aspetti devono essere applicati. Ciò mantiene il codice dell'applicazione libero da qualsiasi conoscenza di tale preoccupazione trasversale e rende molto più facile sostituirlo. (nota che puoi usare questo approccio senza attributi con PostSharp, ma hai bisogno della versione commerciale per rimuoverlo).

Molto è stato scritto su questo tipo di progettazione dell'applicazione;

UPDATE

decoratori sono grandi, ma quello che mi piace di AOP è: qui ci sono alcuni articoli mi ha scritto è il concetto di consigli e punti di unione. C'è un modo per simulare la stessa capacità con decoratore? Potrei solo pensare al riflesso in questo momento.

Un Join Point è una "posizione ben definita all'interno di una classe in cui una preoccupazione sta per essere attaccato". Quando applichi AOP utilizzando decoratori, sarai "limitato" per unire punti che si trovano sui limiti del metodo. Se tuttavia si aderisce allo SRP, OCP e ISP, si avranno interfacce molto sottili (di solito con un singolo metodo). Quando lo fai, noterai che non c'è quasi mai una ragione per avere un punto di aggregazione in qualsiasi altro posto nelle tue classi.

Un Il consiglio è una "preoccupazione che potenzialmente cambierà l'input e/o l'output del metodo di destinazione". Quando lavori con decoratori e un design basato su messaggi (la cosa che sto promuovendo qui), il tuo consiglio deve cambiare il messaggio (o sostituire il messaggio completo con valori alterati) o cambiare il valore di uscita. Le cose non sono molto diverse rispetto alla tessitura del codice, poiché se si applica un consiglio, deve esserci qualcosa in comune tra tutto il codice a cui viene applicato il consiglio. Dato che mi piace molto il supporto in fase di compilazione, di solito definisco questa comunanza con le interfacce. Questo mi consente di scrivere un decoratore con un vincolo di tipo generico per quella particolare interfaccia, il che significa che il decoratore può essere applicato solo lì. Esempio:

public class AddUserContextCommandHandlerDecorator<TCommand> 
    : ICommandHandler<TCommand> 
    where TCommand : IUserContextCommand 
{ 
    private readonly ICommandHandler<TCommand> decoratee; 
    private readonly IUserContext userContext; 

    public AddUserContextCommandHandlerDecorator(
     ICommandHandler<TCommand> decoratee, 
     IUserContext userContext) { 
     this.decoratee = decoratee; 
     this.userContext = userContext; 
    } 

    public void Handle(TCommand command) { 
     command.UserId = this.userContext.UserId; 
     command.UserRoles = this.userContext.UserRoles; 

     this.decoratee.Handle(command); 
    } 
} 

Qui il AddUserContextCommandHandlerDecorator può essere applicato a tutti i ICommandHandler<TCommand> implementazioni in cui TCommand implementa IUserContextCommand. IUserContextCommand contiene le proprietà UserId e UserRoles. Qui vedete che il decoratore cambia il messaggio command prima di inviarlo al suo decoratee. Notare che IMO in questo caso sarebbe meglio iniettare lo IUserContext nel vero gestore di comandi stesso, invece di passare queste informazioni contestuali con il messaggio.

+0

Un blog incredibile, ma ho una domanda però. I decoratori sono fantastici, ma ciò che mi piace di AOP è il concetto di consulenza e punti di aggregazione. C'è un modo per simulare la stessa capacità con il decoratore? Potrei solo pensare al riflesso in questo momento. –

+0

@ SamuelAdam: vedere il mio aggiornamento. – Steven

Problemi correlati