L’AOP ormai sta diventando un paradigma di programmazione sempre più diffuso specialmente nel mondo .NET dove strumenti come PostSharp, DotSpect (Compile-time static injection) e Spring.NET, Castle Windsor, Enterprise Library Policy Injection Application Block (Dynamic Proxy) rendono sempre più semplice la gestione dei “cross-cutting concerns” che provengono dal mondo Object-Oriented. Personalmente sono stato sempre un entusiasta di PostSharp e in questo post vorrei citare un paio di aspect che ho sempre valutato positivamente nella gestione di codice “vicino” alla UI, specialmente nel mondo Windows Forms e WPF.
Il primo aspect ha a che vedere con il DataBinding. Sappiamo tutti come implementare l’interfaccia INotifyPropertyChanged sia noioso ed anche a fronte di snippet per velocizzare il processo di scrittura il risultato che otteniamo si allontana dall’idea di avere codice (almeno) esteticamente “impeccabile”. Per questo l’aspect che PostSharp introduce a riguardo è tanto semplice quanto geniale in quanto permette, tramite un banale attributo, di agganciare l’implementazione dell’interfaccia INotifyPropertyChanged ad un oggetto ed eventualmente alle sue specializzazioni. Ad esempio, se definissimo un oggetto Customer nel seguente modo…
[NotifyPropertyChanged]
public class Customer
{
public string Name { get; set; }
public string Surname { get; set; }
public string Email { get; set; }
...
}
…tramite l’attributo [NotifyPropertyChanged] impostato a livello di classe otteniamo nell’assembly di output una injection della solita implementazione dell’interfaccia INotifyPropertyChanged. L’ implementazione dell’aspect, riportata anche nel sito di PostSharp, è la seguente…
[Serializable]
[IntroduceInterface(typeof(INotifyPropertyChanged),OverrideAction = InterfaceOverrideAction.Ignore)]
[MulticastAttributeUsage(MulticastTargets.Class, Inheritance = MulticastInheritance.Strict)]
public class NotifyPropertyChangedAttribute : InstanceLevelAspect, INotifyPropertyChanged
{
[ImportMember("OnPropertyChanged", IsRequired = false)]
public Action<string> OnPropertyChangedMethod;
[IntroduceMember(Visibility = Visibility.Family,
IsVirtual = true,
OverrideAction = MemberOverrideAction.Ignore)]
public void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this.Instance, new PropertyChangedEventArgs(propertyName));
}
}
[IntroduceMember(OverrideAction = MemberOverrideAction.Ignore)]
public event PropertyChangedEventHandler PropertyChanged;
[OnLocationSetValueAdvice]
[MulticastPointcut(Targets = MulticastTargets.Property, Attributes = MulticastAttributes.Instance)]
public void OnPropertySet(LocationInterceptionArgs args)
{
if (args.Value == args.GetCurrentValue()) return;
args.ProceedSetValue();
this.OnPropertyChangedMethod.Invoke(args.Location.Name);
}
}
Il codice è abbastanza autoesplicativo. La feature degna di nota in particolare è la possibilità di estendere automaticamente questo aspect anche alle classi ereditate semplicemente impostando a livello di classe MulticastAttributeUsage.Inheritance = MulticastInheritance.Strict.
Un altro aspect che ho sempre ritenuto molto utile ha a che vedere con il thread dispatching, ovvero con la gestione dell’esecuzione di un metodo sul thread dell’interfaccia piuttosto che su un worker thread (es. in WPF controllando il thread associato al DispatcherSynchronizationContext corrente). Anche in questo ambito PostSharp permette di definire due attributi [OnGuiThread] e [OnWorkerThread] a livello di metodo, che iniettano la comune logica di controllo del SynchronizationContext in modo da forzare l’esecuzione del metodo rispettivamente sul thread dell’interfaccia o su un thread secondario (link al codice sorgente).
Come risultato, diventa molto semplice applicare questi aspect anche su codice legacy.
[OnWorkerThread]
void SaveCustomer()
{
Customer.Save();
ShowMessage("Customer Saved!");
}
[OnGuiThread]
void ShowMessage(string message)
{
MessageBox.Show(message);
}
Il pacchetto di istallazione di PostSharp (disponibile anche su NuGet) aggiunge a Visual Studio due toolbox veramente utili: l’Aspect Browser che ci permette navigare su quelli che sono gli aspect utilizzati nell’intera solutione e l’ Affected code toolbox che ci riassume tutte le classi e i metodi che sono soggetti ad aspects. Inoltre, l’intellisense viene esteso in modo da visualizzare dei summary sulle static injections che gli aspect introducono nell’assembly. Ad esempio, per quanto riguarda il precedente aspect NotifyPropertyChanged, i tooltip che otteniamo sono i seguenti:
Technorati tags:
AOP,
WPF,
GUI