Neste artigo iremos mostrar o que é a Orientação a Aspectos e como ela pode nos ajudar em nosso dia-a-dia. Vamos começar contextualizando o que vem ser um aspecto, a partir de alguns conceitos conhecidos.

É de senso comum que o desenvolvimento estruturado e a orientação a objetos dizem respeito à modularidade de um sistema. De uma maneira simplista podemos dizer que são formas diferentes de se dividir um sistema em partes.

Essa divisão é importante porque ajuda a reduzir a complexidade (“dividir para conquistar”), e ajuda o ser humano a compreender um sistema de grande porte, o que seria dificultado se o mesmo sistema fosse monolítico, ou seja, sem fronteiras claras que definem suas funcionalidades.

Todo sistema de software tem interesses diferentes, sejam dados, operações, ou outros requisitos. O ideal seria que a parte dedicada a satisfazer um interesse específico estivesse concentrada e isolada em uma única localidade, facilitando sua compreensão.

O desenvolvimento estruturado realizou sua obrigação. Na sua “época” (em alguns softwares dura até hoje), a separação de interesses se dava a partir da diferença de funcionalidades oferecidas pelo software. Cada função era implementada em um único módulo, ou procedimento. Daí surgiram conceitos que ajudam a manter a separação de interesses, como o baixo acoplamento e a alta coesão. Já a orientação a objetos veio como forma de sanar uma das deficiências do desenvolvimento estruturado. Apesar de interesses relativos a funcionalidades ficarem separados, interesses relativos a dados ficavam distribuídos em diversos módulos. O paradigma OO definiu que a separação deveria acontecer em duas dimensões, primeira em termos de dados e depois em termos das funções que utilizam os dados. A Figura 1 mostra a ideia de dimensionalidade em um Sistema OO.

Separação em termos de dados (Entidades) e funcionalidades (Métodos) em um sistema OO
Figura 1. Separação em termos de dados (Entidades) e funcionalidades (Métodos) em um sistema OO

Com a orientação a objetos, as possibilidades de separação de interesses melhoraram significativamente, porém, como nem tudo é perfeito, ainda permaneceram algumas deficiências. É o que veremos a seguir.

Parsing de XML realizado pelo Apache Tomcat (fonte: aspectj.org)
Figura 2. Parsing de XML realizado pelo Apache Tomcat (fonte: aspectj.org)

Na Figura 2, podemos ver os diversos módulos do Tomcat, representados pelas barras verticais. O tamanho da barra é diretamente proporcional à quantidade de código, ou seja, quanto maior a barra, mais código dentro do módulo. A coloração vermelha determina o interesse, neste caso, o parsing de XML realizado pelo Tomcat. Podemos ver que este interesse está muito bem separado em um único módulo. O problema é que nem sempre isso acontece. Para entender como a descentralização de um interesse é prejudicial, vamos analisar outra funcionalidade importante do Tomcat, o armazenamento de registros para auditoria. Esses registros são importantes para auditarem-se ações tomadas pelo servidor, como por exemplo, a detecção de erros, violações de segurança, controle de usuários, entre outras. A Figura 3 mostra como o nosso interesse em questão, o registro de auditoria, está presente em muitos módulos.

Registro de auditoria no Apache Tomcat (fonte: aspectj.org)
Figura 3. Registro de auditoria no Apache Tomcat (fonte: aspectj.org)

Quando algo parecido com o registro de auditoria ocorrer, ou seja, um interesse atravessar a aplicação, inferindo muitos módulos, ou mais diretamente, este interesse “cortar” a aplicação, dizemos se tratar de um interesse entrecortante. O objetivo do desenvolvimento orientado a aspectos é encapsular esses interesses entrecortantes em módulos fisicamente separados do restante do código. Os módulos que abrigam esses interesses são chamados de aspectos. Se pensarmos em termos abstratos, a orientação a aspectos introduz uma terceira dimensão de decomposição. Lembrando o que vimos, a OO decompõe o sistema em objetos (dados) e métodos (funções). Por sua vez, os objetos e métodos podem ainda ser decompostos de acordo com um interesse comum. Ao se agrupar cada interesse em um módulo distinto, teremos a orientação a aspecto. A Figura 4 ilustra esse cenário.

Interesses entrecortantes: segurança e persistência
Figura 4. Interesses entrecortantes: segurança e persistência

Para ficar mais claro, vamos exemplificar com a classe Produto. Esta classe inicialmente conterá conceitos de OO. Ela pode ser vista na Figura 5.

Classe Produto, somente com conceitos OO
Figura 5. Classe Produto, somente com conceitos OO

Agora, como em qualquer aplicação, precisamos persistir esse objeto em Banco de Dados. Sem a utilização de POA (Programação Orientada a Aspectos), esse interesse seria implementado dentro da própria classe Produto pelo método salvar(), como pode ser observado na Figura 6.

Classe Produto, com um interesse entrecortante
Figura 6. Classe Produto, com um interesse entrecortante

Para esta mesma classe Produto, digamos também que precisamos de um controle de auditoria, para saber, por exemplo, uma determinada operação nesta classe. A Figura 7 ilustra mais uma vez, um interesse sendo implementado na própria classe.

Classe Produto, com dois interesses entrecortantes
Figura 7. Classe Produto, com dois interesses entrecortantes

Como podemos observar, a complexidade no código aumenta gradativamente a cada novo interesse que acrescentamos. Com a programação orientada a aspectos, deixam de estarem espalhados por todo o código, e passam a ser programadosem módulos separados (classes e aspectos, por exemplo). Após a programação,ocorreacombinaçãoentreasclasseseosaspectos,conformeindicadopelaFigura8. Issolevaàsimplificaçãodoproblema,poisoprogramador pode trabalhar com cada interesse deformaindependente.

Combinação entre a classe produto e os demais interesses
Figura 8. Combinação entre a classe produto e os demais interesses

Além dos interesses entrecortantes, existem outros conceitos importantes em POA. Vamos a eles: pontos de junção (joinpoints), pontos de corte (pointcut), e advice.

Um ponto de junção é qualquer ponto identificável pela POA durante a execução de um programa. Aspectos podem ser associados a pontos de junção e executados antes, depois, ou ao invés deles. Existem diversos pontos de junção, que podem estar presentes em métodos, construtores, acesso a campos e tratamento de exceções.

Um ponto de corte (pointcut) é uma construção sintática para se agrupar um conjunto de pontos de junção. Por exemplo, a nossa classe Produto, ela tem o método salvar(). Podemos definir um ponto de junção neste método, e criar um ponto de corte que defina, por exemplo, que qualquer método iniciado por “salvar” irá persistir o objeto associado, no nosso caso, um Produto.

O advice é uma estrutura que denota o que um aspecto deve fazer, ou seja, qual o comportamento do aspecto. Em termos mais formais, o advice designa a semântica comportamental do aspecto. Todo advice está associado a um pointcut, que define os pontos de junção. Há três tipos de advice:

  • before: executa antes do ponto de junção.
  • after: executa depois do ponto de junção.
  • around: executa "ao redor" do ponto de junção; esse tipo de advice substitui a execução do ponto de junção pela do advice, ou executa parte do advice antes do ponto e junção e outra parte depois.

Bom, pessoal, por hoje é isso, até um próximo artigo. Abraço.