Implementando INotifyPropertyChanged com Orientação a Aspecto

Finalmente um post novo no blog, muitos assuntos novos tem me interessado ultimamente, e isso atrapalha o blog também, penso em escrever sobre tudo, e escrevo nada! Mas vamos começar a resolver isso.

Esses dias estava pesquisando sobre Programação Orientada a Aspecto, estou apenas começando os estudos, então não tenho um grande conhecimento sobre o assunto, então segue uma definição da Wikipedia.

Em ciência da computaçãoprogramação orientada a aspectos ou POA, é um paradigma de programação de computadores que permite aos desenvolvedores de software separar e organizar o código de acordo com a sua importância para a aplicação (separation of concerns). Todo o programa escrito no paradigma orientado a objetos possui código que é alheio a implementação do comportamento do objeto. Este código é todo aquele utilizado para implementar funcionalidades secundárias e que encontra-se espalhado por toda a aplicação (crosscutting concern). A POA permite que esse código seja encapsulado e modularizado.

O conceito foi criado por Gregor Kiczales e a sua equipe na Xerox PARC, a divisão de pesquisa da Xerox. Eles desenvolveram o AspectJ, a primeira e mais popular linguagem POA.

Muito bem, então além de código procedural, orientado a objetos, temos orientação a aspecto também! Não vou fazer uma introdução a programação orientada a aspect, e sobre suas inúmeras possibilidades, mas vou mostrar aqui um código que vi e gostei muito do resultado final, quem sabe isso não cria interesse também em mais devs ;).

Em .NET existem várias frameworks para Orientação a Aspecto, alguns exemplos:

E é no último, o PostSharp que encontrei exemplos interessantes, e pude entender um pouco mais sobre Orientação a Aspecto. O exemplo que vou mostrar aqui está no site deles.

Gostei do exemplo que vou mostrar, porque sempre achei horrível, implementar INotifyPropertyChanged, sempre procurei uma forma mais elegante de fazer todo aquele trabalho, e realmente gostei da solução usando Aspectos. Para quem não sabe o porque, onde e como usamos INotifyPropertyChanged veja aqui a documentação.

O exemplo é simples, não vamos mais implementar a interface INotifyPropertyChanged nas nossas classes, mas vamos criar um “atributo” que vai adicionar um comportamento na classe. Disse “atributo” porque são com atributos que informamos para as classes, métodos, propriedades, etc… os comportamentos que vamos adicionar em cada um deles.

Não explicarei cada item do código, fica para outro artigo, vamos ver o código do nosso atributo, lembrando que na classe que adicionarmos esse atributo, ela terá o comportamento de uma classe que implementa a interface INotifyPropertyChanged.

using System;
using System.ComponentModel;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Extensibility;
using PostSharp.Reflection;

namespace NotifyPropertyChanged
{
    [Serializable]
    [IntroduceInterface( typeof(INotifyPropertyChanged), OverrideAction = InterfaceOverrideAction.Ignore )]
    [MulticastAttributeUsage( MulticastTargets.Class, Inheritance = MulticastInheritance.Strict )]
    public sealed class NotifyPropertyChangedAttribute : InstanceLevelAspect, INotifyPropertyChanged
    {

        [ImportMember( "OnPropertyChanged", IsRequired = false, Order = ImportMemberOrder.AfterIntroductions)] 
        public Action 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 | MulticastAttributes.NonAbstract)]
        public void OnPropertySet( LocationInterceptionArgs args )
        {
            if ( args.Value == args.GetCurrentValue() ) return;

            args.ProceedSetValue();

            OnPropertyChangedMethod.Invoke( args.Location.Name );
        }
    }
}

Então agora na minha classe Album, só é necessário adicionar o atributo criado.

namespace NotifyPropertyChanged
{
    [NotifyPropertyChanged]
    public class Album
    {
        public EstiloMusical EstiloMusical { get; set; }

        public string Nome { get; set; }
    }
}

É um mundo novo, ainda preciso estudar mais, existem muitas vantages, mas também desvantagens, mas isso fica para depois.

Bom por hoje é isso.

Abraços.