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.

  • http://brenoferreira.wordpress.com Breno Ferreira

    Grande Althmann,

    Muito legal o post!!! Me deixou com vontade de aprender mais sobre o assunto, pois aparentemente, deixa seu código mais limpo e elegante.

    Só uma nota: na declaração do delegate OnPropertyChangedMethod, acho que o tipo é Action, não?

    Abraços

    Breno

    • Márcio Fábio Althmann

      Opa vou validar Breno, e valeu pelo comentário!
      Pois é cara, eu gostei muito, justamente essa parte de “elegância”, vou fazer mais testes e vou postando ;)

      Abraços.

  • http://plataformanuvem.wordpress.com/ Fernando Correia

    Muito bom, Márcio. Eu considero AOP uma forma bastante eficaz de separar responsabilidades das classes, mas sempre achei que as soluções que existiam traziam mais complexidade do que tiravam. Mas hoje em dia há formas bastante limpas de usar AOP basicamente apenas declarando anotações ou atributos sobre as classes e métodos. O Java EE 6 faz isso de forma bem limpa com os “interceptors”.

  • http://araimundo.blogspot.com Ari C. Raimundo

    Olá Fábio,

    Muito legal o post. Particularmente também detesto ter que implementar INotifyPropertyChanged.

    Só uma dica, a maneira correta (mais segura) de disparar o evento seria:

    var propertyChangedEvent = this.PropertyChanged;
    if (propertyChangedEvent != null)
    propertyChangedEvent(this.Instance, new PropertyChangedEventArgs( propertyName );

    Mais informações no link abaixo:

    http://blogs.msdn.com/b/ericlippert/archive/2009/04/29/events-and-races.aspx

    Abraços.

  • http://www.sharpcrafters.com Britt King

    Thanks for AOP post, Marcio. We’ve been pushing pretty hard for the past year or so to raise awareness in the .NET community of the benefits of developing with aspects. Thanks for your help.
    -Britt
    @brittrking