Programação Orientada a Aspectos - Técnicas
e paradigmas para a modularização de
software.
Dissertação de Mestrado apresentada por
Luís Filipe Félix de Sá
Sob orientação do Prof. Doutor Ramiro Gonçalves e do
Prof. Doutor Carlos Rabadão
Universidade de Trás-os-Montes e Alto Douro
Departamento de Engenharias
2008
II
"Controlling complexity is the essence of computer programming."
(Brian Kernigan)
III
Dissertação apresentada por Luís Filipe Félix de Sá à
Universidade de Trás-os-Montes e Alto Douro para cumprimento dos
requisitos necessários à obtenção do grau de Mestre em Informática, sob
a orientação do Prof. Doutor Ramiro Manuel Ramos Moreira Gonçalves,
Professor Auxiliar do Departamento de Engenharias da Universidade de
Trás-os-Montes e Alto Douro e co-orientada pelo Prof. Doutor Carlos
Manuel da Silva Rabadão, Professor Adjunto do Departamento de
Engenharia Informática da Escola Superior de Tecnologia e Gestão do
Instituto Politécnico de Leiria.
IV
Aos meus Pais.
V
AGRADECIMENTOS
A todos os meus amigos e colegas Francisco Silva, Marta Pimenta, Diana Santos, Isabel
Marcelino, Andreia Penedo, Ana Oliveira, Rúben Pedro, Rodrigo Selada, Bruno Alves, Márcia,
Tiago Conde pela amizade, dedicação e apoio constantes.
Aos meus orientadores Professor Doutor Ramiro Gonçalves e Professor Doutor Carlos
Rabadão e Co-Orientador Professor Marco Monteiro, pela partilha de conhecimentos e
experiência.
A todos o meu muito obrigado!
Luís Filipe Félix de Sá
VI
RESUMO
O desenvolvimento de software é uma tarefa complexa, onde se decompõe um problema
localizando todas as suas preocupações. As tecnologias existentes para a implementação destas
são limitadas devido à sua inadequação para as modularizar. Assim, deu-se o aparecimento de
uma nova tecnologia, a Programação Orientada a Aspectos. Esta tecnologia não vem substituir
mas sim estender as tecnologias existentes, melhorando-as, permitindo novas formas de
modularidade, diminuindo o tempo de desenvolvimento e aumentando a reutilização de software.
VII
ABSTRACT
Software development is a complex task, where a problem is decomposed identifying all its
concerns. The current technologies for implementing them are unable to modularize all of them.
Therefore, a new technology was developed, Aspect Oriented Programming. This technology
doesn’t come to replace rather to improve current technologies, with new ways to achieve
software modularity, resulting in a decrease of developing time and increased software reuse.
VIII
INDICE GERAL
AGRADECIMENTOS ................................................................................................................ V
RESUMO ..................................................................................................................................... VI
ABSTRACT ............................................................................................................................... VII
INDICE GERAL ...................................................................................................................... VIII
LISTA DE TABELAS .............................................................................................................. XII
LISTA DE FIGURAS .............................................................................................................. XIII
ACRÓNIMOS ............................................................................................................................XV
1.
2.
INTRODUÇÃO .................................................................................................................... 1
1.1
ENQUADRAMENTO TECNOLÓGICO ......................................................... 2
1.2
OBJECTIVOS E CONTRIBUTOS FUNDAMENTAIS .................................. 4
1.3
ESTRUTURAÇÃO DA DISSERTAÇÃO ......................................................... 5
TÉCNICAS E PARADIGMAS DA PROGRAMAÇÃO................................................... 7
2.1
PARADIGMA DE PROGRAMAÇÃO.............................................................. 7
2.2
LINGUAGEM DE PROGRAMAÇÃO ............................................................. 8
2.3
FORMAS DE SEPARAÇÃO.............................................................................. 8
2.3.1
Abstracção ........................................................................................................ 9
2.3.2
Ocultação de informação .............................................................................. 10
IX
2.3.3
Encapsulamento ............................................................................................. 10
2.4
PROGRAMAÇÃO ESTRUTURADA ............................................................. 10
2.5
PROGRAMAÇÃO ORIENTADA A OBJECTOS ......................................... 13
2.5.1
Linguagens POO ............................................................................................ 14
2.6
PROGRAMAÇÃO ORIENTADA A COMPONENTES ............................... 15
2.7
PROGRAMAÇÃO PÓS-OBJECTOS ............................................................. 18
2.8
META-PROGRAMMING .................................................................................. 21
2.9
SUBJECT-ORIENTED PROGRAMMING ...................................................... 22
2.10
PROGRAMAÇÃO ADAPTATIVA ................................................................. 24
2.11
FILTROS DE COMPOSIÇÃO ........................................................................ 25
2.12
PROGRAMAÇÃO ORIENTADA A ASPECTOS. ........................................ 26
2.12.1 AspectJ .......................................................................................................... 28
3.
2.13
SEPARAÇÃO MULTIDIMENSIONAL DE CONCERNS ............................ 33
2.14
DESENVOLVIMENTO DE SOFTWARE ORIENTADO A ASPECTOS.. 34
2.15
COMUNIDADES E SITUAÇÃO EM PORTUGAL ...................................... 35
2.16
DESAFIOS, PROBLEMAS E OPORTUNIDADES ...................................... 36
PROGRAMAÇÃO ORIENTADA A ASPECTOS .......................................................... 39
3.1
COMPORTAMENTOS GLOBAIS ................................................................. 39
3.2
ANÁLISE DO PROBLEMA ............................................................................ 41
3.3
SISTEMA MODULAR ..................................................................................... 46
3.4
SISTEMA POA .................................................................................................. 47
3.5
METODOLOGIA DSOA.................................................................................. 49
3.6
ANATOMIA DA LINGUAGEM POA ............................................................ 50
X
3.7
ESPECIFICAÇÃO DAS LINGUAGENS ....................................................... 51
3.7.1
Software concerns .......................................................................................... 51
3.7.2
Regras de combinação ................................................................................... 51
3.8
COMPOSIÇÃO DE SISTEMAS...................................................................... 52
3.8.1
Weaving ........................................................................................................... 52
3.8.2
Weaver ............................................................................................................. 53
3.9
BENEFÍCIOS DA POA .................................................................................... 57
3.10
DESENVOLVIMENTO DE SOFTWARE ORIENTADO A ASPECTOS.. 59
3.10.1 Identificação de concerns ............................................................................. 60
3.10.2 Implementação .............................................................................................. 60
3.11
DEVENVOLVIMENTO UTILIZANDO ASPECTJ...................................... 61
3.11.1 Análise ........................................................................................................... 62
3.11.2 @AspectJ ....................................................................................................... 62
3.11.3 Ferramentas de Desenvolvimento ............................................................... 63
3.12
POSTSHARP ..................................................................................................... 63
3.12.1 PostSharp LAOS .......................................................................................... 65
3.12.2 Análise da Tecnologia .................................................................................. 67
3.12.3 PostSharp Core ............................................................................................. 69
3.12.4 AspectJ vs. Postsharp LAOS ....................................................................... 69
3.12.5 Conclusões ..................................................................................................... 73
3.13
MODULARIZAÇÃO DE PADRÕES DE DESENHO .................................. 74
3.13.1 Exemplo Observer Pattern. ......................................................................... 76
3.13.2 Análise dos Estudos ...................................................................................... 77
XI
3.14
MODULARIZAÇÃO DE EXCEPÇÕES ........................................................ 78
3.15
FRAMEWORKS ............................................................................................... 81
3.16
MÉTRICAS ........................................................................................................ 81
4.
CONSIDERAÇÕES FINAIS ............................................................................................. 86
5.
BIBLIOGRAFIA ................................................................................................................ 90
6.
ANEXOS ............................................................................................................................. 95
XII
LISTA DE TABELAS
Tabela 1 – Cronograma temporal da investigação sobre separação de concerns ......................... 20
Tabela 2 - comparação da nomenclatura do AspectJ e Postsharp Laos. ....................................... 65
Tabela 3 – Comparação de pointcuts entre PostSharp Laos e AspectJ......................................... 71
Tabela 4 - Comparação de pointcuts entre PostSharp Laos e AspectJ ......................................... 71
Tabela 5 – comparação de tipos .................................................................................................... 71
Tabela 6 – Resumo das comparações dos padrões de desenho. ................................................... 77
Tabela 7 – Evolução da utilização de POO e POA, segundo as métricas de Sant’Anna.............. 83
Tabela 8 - Comparação de métricas entre Java e AspectJ primeira parte. .................................... 85
Tabela 9 - Comparação de métricas entre Java e AspectJ segunda parte. .................................... 85
XIII
LISTA DE FIGURAS
Figura 1 - Módulos com acesso concorrente aos dados. ............................................................... 12
Figura 2 - Hierarquia de classes .................................................................................................... 13
Figura 3 – Espalhamento de Concerns. ........................................................................................ 19
Figura 4 - Meta-programming. ..................................................................................................... 22
Figura 5 - Subject-oriented programming. ................................................................................... 23
Figura 6 - Filtros de composição. ................................................................................................. 26
Figura 7 – Diagrama do exemplo.................................................................................................. 30
Figura 8 - Interacção entre Módulos. ............................................................................................ 40
Figura 9 - Sobreposição de unidades elementares. ....................................................................... 40
Figura 10 - Comportamentos Globais do Sistema ........................................................................ 41
Figura 11 - Metodologia POA. ..................................................................................................... 50
Figura 12 – Weaver de código fonte. ............................................................................................ 53
Figura 13 - Weaver de código intermédio..................................................................................... 54
Figura 14 - Comunicação normal entre objectos. ......................................................................... 55
Figura 15 - Comunicação utilizando uma proxy........................................................................... 56
Figura 16 – Adição de comportamentos adicionais através de proxys. ........................................ 57
XIV
Figura 17 - Sistema de Desenvolvimento de Software Orientado a Aspectos. ............................ 59
Figura 18 – Incisão do estudo de Early-Aspects. .......................................................................... 60
Figura 19 – Processo de integração do Postsharp. ........................................................................ 64
Figura 20 – Padrão de Desenho Observer implementado em POO.............................................. 75
Figura 21 – Padrão de desenho Observer implementado em POA .............................................. 76
Figura 22 - Modelo de Qualidade. ................................................................................................ 83
XV
ACRÓNIMOS
Ao longo desta dissertação vão ser utilizadas algumas abreviaturas e designações que
apenas serão apresentadas quando forem utilizadas pela primeira vez:
POA
Programação Orientada a Aspectos
POO
Programação Orientada a Objectos
DSOA
Desenvolvimento de Software Orientado a Aspectos
POC
Programação Orientada a Componentes
GOF
Padrões de Desenho Gang-of-Four
JVM
Java Virtual Machine
IDE
Intregrated Development Envionment
CME
Concern Manipulation Envionment
1
1. INTRODUÇÃO
Em 1997 foi proposto um novo paradigma de programação, a Programação Orientada a
Aspectos (POA), cujo principal objectivo era apresentar uma extensão aos paradigmas existentes
até à data, retendo os pontos onde estes demonstraram a sua eficiência, o desenvolvimento das
funcionalidades base de um sistema. Apresentando uma nova forma de modularidade onde os
antigos paradigmas manifestavam reais fragilidades, esta nova extensão incide especialmente nos
comportamentos transversais ao sistema (crosscutting concerns) [Kiczales, Lamping et al. 1997].
Este novo paradigma propõe a decomposição dos sistemas pelas suas variadas
preocupações dividindo o software em concerns (preocupações) base (funcionalidades base do
sistema) e transversais (funcionalidades globais ao sistema). É introduzido um novo conceito, o
aspecto, que consiste numa unidade modular que possui uma visão global do sistema, o que lhe
permite fazer a coordenação e comunicação entre concerns. Para estes, este processo é
transparente. A transferência da coordenação e comunicação entre diferentes tipos de concerns
para os aspectos, permite-os libertar de questões globais ao sistema tornando-os independentes.
A composição de concerns é efectuada através de um compilador, Weaver, cujo resultado
é um sistema onde existe uma clara separação e abstracção entre todos os concerns que definem
o comportamento do sistema. O sistema torna-se claro de ler, escrever e manter, as
2
funcionalidades adicionais são simples de acoplar ao sistema e o desenho inicial é mais
consistente com a sua implementação concreta.
Outras tecnologias com objectivos semelhantes foram desenvolvidas: Programação
Adaptativa [Lieberherr 1996], Filtros de Composição [Mehmet Aksit 1994], Subject Oriented
Programming [Harrison and Ossher 1993], Separação Multidimensional de Concerns [Tarr,
Ossher et al. 1999] e Programação Gerativa [Czarnecki, Eisenecker et al.].
A separação avançada de concerns1 2, a Programação Pós-Objectos [Elrad, Filman et al.
2001; Hayes 2003] e o Desenvolvimento de Software Orientado a Aspectos3
4
(DSOA) são as
áreas de investigação em que a POA se enquadra.
1.1
ENQUADRAMENTO TECNOLÓGICO
A separação de concerns é um princípio fundamental que determina a resolução de um
concern de cada vez [Dijkstra 1976]. Em engenharia de software, o princípio da sua separação
está normalmente relacionado com a decomposição e modularização de sistemas [Parnas 1972]:
os sistemas de software complexos devem ser decompostos em unidades modulares menores e
claramente separadas, cada uma lidando com um único concern. Sendo de destacar como
benefícios a sua melhor compreensibilidade e um maior potencial para a evolução e reutilização
em outros sistemas de software complexos. Os mecanismos de composição e abstracção que
sucessivas gerações de linguagens de programação oferecem, evoluíram para permitir a
1
http://www.cs.ubc.ca/~kdvolder/Workshops/OOPSLA2001/ASoC.html (Acedido, Julho 2008)
http://www.research.ibm.com/hyperspace/workshops/icse2001/index.htm (Acedido, Julho 2008)
3
http://www.aosd.net/ (Acedido, Julho 2008)
4
http://www.aosd-europe.net/ (Acedido, Julho 2008)
2
3
resolução de problemas do mundo real de forma mais natural, assim como, para possibilitar o
alcance da separação de concerns ao nível do código-fonte. A Programação Orientada a Objectos
(POO) tem sido a tecnologia de programação dominante sendo os seus benefícios amplamente
reconhecidos. No entanto, a POO possui algumas limitações no tratamento de concerns,
nomeadamente no que respeita aos requisitos que envolvam restrições globais e propriedades
sistémicas, como é exemplo: a sincronização, a persistência, o tratamento de erros, entre outros.
Estas limitações necessitam de uma tecnologia que permita uma rotura, ou seja, algo que esteja
realmente para além dos objectos [Booch 2001].
Relativamente à POA, esta é uma tecnologia em evolução que oferece suporte a um novo
tipo de separação de concerns no nível do código-fonte. É vista como sendo evolucionária
porque considera as melhorias reconhecidas da sua separação, proporcionadas pelas tecnologias
anteriores (principalmente, mas não apenas, a POO) enquanto oferece suporte a novos
mecanismos para lidar com alguns concerns especiais que não são tratados adequadamente por
essas tecnologias. A ideia central por trás da POA é que os mecanismos de abstracção e
composição das tecnologias existentes não são suficientes para separar alguns concerns especiais
encontrados em sistemas mais complexos. Denominados de crosscutting concerns, pois afectam
a modularidade de vários elementos “penetrando" os seus limites, estes, sem os meios
apropriados para a separação e a modularização, tendem a ficar espalhados e entrelaçados com
outros concerns. Como consequências naturais reporte-se uma menor compreensibilidade, uma
menor capacidade de evolução e uma menor reutilização dos artefactos do código. Esta
tecnologia utiliza aspectos para alcançar a modularização de crosscutting concerns e fornece
mecanismos para a sua combinação em pontos bem definidos.
4
A POA e a maioria dos trabalhos existentes sobre a separação avançada de concerns
tratam do fenómeno de crosscutting no nível de implementação, tendo recentemente muitos
grupos de pesquisa começado a discutir a função dos aspectos em outras actividades do processo
de desenvolvimento de software. Deste modo, o Desenvolvimento de Software Orientado a
Aspectos (DSOA) revela-se uma área emergente cujo objectivo, é promover a separação
avançada de concerns ao longo de todo o ciclo de vida do desenvolvimento de software. O
Design Orientado a Aspectos (DOA) é assim, uma parte crítica do DSOA que se concentra em
notações, técnicas e ferramentas para a identificação, a estruturação, a representação e a gestão
de crosscutting concerns no projecto e arquitectura de software, oferecendo um linhagem desde
os requisitos até a implementação com POA.
1.2
OBJECTIVOS E CONTRIBUTOS FUNDAMENTAIS
O principal objectivo deste projecto de investigação é apresentar um documento, com
vista a descrever paradigmas e técnicas que se propõem a melhorar a separação de concerns. São
expostas as desvantagens da sua não separação, como é possível alcançar essa separação
enumerando as suas vantagens e desvantagens. Este trabalho propõe-se ainda a detalhar
princípios, metodologias e práticas de algumas linguagens e tecnologias POA, recorrendo à
exemplificação prática, a exemplos de aplicações dessas tecnologias e à apresentação de estudos
de investigação sobre análise de resultados empíricos, utilizando métricas bem conhecidas e
documentando as principais ferramentas, linguagens e a sua forma de utilização.
5
1.3
ESTRUTURAÇÃO DA DISSERTAÇÃO
Inicia-se este trabalho com uma pequena apresentação deste estudo, seguindo-se o seu
enquadramento, bem como, os objectivos definidos para o seu prosseguimento. Desta forma no
Capitulo 2 apresenta-se uma breve evolução das várias técnicas e paradigmas que contribuíram
para o aparecimento e desenvolvimento do Paradigma Orientado a Aspectos. Descrevendo-se as
técnicas e tecnologias que com mais ou menos complexidade permitem alcançar uma melhor
modularidade nos sistemas de software. No Capítulo 3 é desenvolvido de forma prática o
Paradigma Orientado a Aspectos, linguagens de programação e aplicações práticas do
paradigma. Apresenta-se o Paradigma Orientado a Aspectos, o que é, que problemas se
pretendem resolver e como pode ser implementado. É apresentada a linguagem AspectJ e, de
seguida, a tecnologia PostSharp. Algumas aplicações da Programação Orientada a Aspectos
nomeadamente a modularização de padrões de desenho, a modularização de excepções e estudos
que apresentam métricas para possibilitar a quantificação empírica de programas POA também
são apresentadas e descritas. Finalmente no 4º e último Capítulo exibem-se as principais
conclusões deste projecto de investigação.
7
2. TÉCNICAS E PARADIGMAS DA PROGRAMAÇÃO
No âmbito do objectivo deste trabalho de projecto, procurou-se aprofundar os conhecimentos
relativos às diversas técnicas e paradigmas que possibilitam resolver o problema da separação de
concerns. Para este fim tornou-se necessário a análise de uma variadíssima fonte bibliográfica
alusiva a esta temática, possibilitando-nos assim, expor uma visão mais holística do estudo aqui
em análise.
2.1
PARADIGMA DE PROGRAMAÇÃO
Um paradigma da programação pode ser definido como um “…conjunto de teorias e
métodos que representam uma forma particular de se tentar organizar o conhecimento numa dada
área” [Kuhn 1970], ou como, “modelos ou exemplos, e abordagens organizacionais, que
permitem conceptualizar o que se deve entender por computações, bem como se devem
estruturar e organizar as tarefas que se pretendem ver realizadas por um computador” [Floyd
1979]. Um paradigma da programação permite disponibilizar ao programador uma visão e uma
forma de raciocinar de modo a que este consiga desenvolver e executar um programa de
computador, podendo-se definir este último, como uma colecção de instruções na forma de
8
código e de variáveis que possibilitam a um computador manipular uma determinada
informação. No âmbito deste projecto entende-se paradigma segundo a visão de Floyd.
2.2
LINGUAGEM DE PROGRAMAÇÃO
Uma linguagem de programação possibilita que um programador especifique
precisamente sobre quais dados, um computador vai actuar, como estes deverão ser armazenados
ou transmitidos e que acções devem ser tomadas sob as mais variadas circunstâncias. Assim,
linguagem de programação é entendida como um método normalizado para expressar instruções
para um computador, ou seja, é um conjunto de regras sintácticas e semânticas usadas para
definir um programa de computador.
2.3
FORMAS DE SEPARAÇÃO
A evolução das linguagens de programação permitiu abstrair os programadores dos
pormenores das plataformas e das máquinas em que os programas são executados, possibilitando
mecanismos superiores de composição e abstracção, onde estes se ajustam aos problemas de uma
forma mais natural, numa visão centrada no problema. Esta visão permite uma separação natural
do problema por todos os seus concerns [Dijkstra 1976].
A separação de um sistema de software em módulos aumenta a flexibilidade, a
compreensibilidade e diminui o tempo de desenvolvimento. Um módulo pode ser assim
entendido como um pedaço de software independente, que faz parte de um programa de
9
computador, funcionando como uma caixa negra onde do exterior só é possível interagir com a
sua interface (forma de ligação com o interior) [Parnas 1972].
Nesta continuidade, os módulos independentes podem ser desenvolvidos separadamente
com informação mínima sobre outros módulos. As equipas de desenvolvimento que são
atribuídas a cada módulo focam-se na sua especialidade (segurança, base de dados, etc.), o que
provoca uma diminuição do tempo de desenvolvimento. Os módulos internamente podem ser
redesenhados e implementados novamente para acomodar as alterações necessárias, sem que isto
afecte o sistema, tornando-o flexível à evolução. A possibilidade de analisar e desenhar o sistema
módulo a módulo torna-o mais compreensível. A sua separação pode ser alcançada através de
vários conceitos tais como abstracção, ocultação de informação e encapsulamento como se
poderá verificar nas secções seguintes.
2.3.1
Abstracção
Estabelecida como um processo que extrai detalhes importantes de um item e suprime os
não importantes, a abstracção também pode ser vista como um modelo ou visão que representa
um item - por exemplo, as pessoas podem dizer “diga-me só os pontos-chave”, este facto
representa uma forma de abstracção. Podemos ver abstracção como uma técnica que dita que
informação é mais importante que outra, mas não especifica como lidar com a menos importante
[Berard 2002].
10
2.3.2
Ocultação de informação
O conceito de ocultação de informação para a construção de módulos é comum ser
confundido com abstracção. Como anteriormente referido, a abstracção permite identificar a
informação e posteriormente ocultar a menos importante, utilizando para este efeito técnicas de
ocultação de informação disponibilizadas pelas linguagens de programação (funções, subrotinas,
classes, etc.) [Berard 2002].
2.3.3
Encapsulamento
Designa-se por encapsulamento o agrupamento de um ou mais itens, não sendo definido a
forma como ele é delimitado. Este processo não é o mesmo que o da ocultação de informação.
Embora a informação esteja encapsulada, esta pode não estar escondida - por exemplo, uma
estrutura encapsula uma determinada quantidade de informação mas esta pode ainda ser cedida,
embora possam ser definidas regras para restringir esse acesso. Dito isto, o encapsulamento
permite agrupar informação e definir qual ocultar ou qual tornar visível [Berard 2002].
2.4
PROGRAMAÇÃO ESTRUTURADA
Tendo-se iniciado para efectuar cálculos numéricos, os programas de computador
começaram por ser desenvolvidos em código máquina através da introdução de novas linguagens
de programação, entre elas o Fortran [Backus, Beeber et al. 1957], tornando possível elaborar
sistemas de software mais complexos sem recorrer a este intrincado código.
11
Com o aumento da complexidade do processo de desenvolvimento de software
levantaram-se problemas de fiabilidade e adaptabilidade, sugerindo-se assim, a eliminação das
instruções goto das linguagens de programação de alto nível, substituindo-as por estruturas de
sequência, de divisão e de iteração [Dijkstra 1968]. A utilização das instruções goto pode ter
efeitos devastadores uma vez que podem surgir problemas devido ao facto de esta instrução
alterar o fluxo do programa. Todavia, o seu comportamento não pode ser avaliado ao longo da
fase de implementação utilizando um conjunto de manipulações matemáticas e lógicas que
permitam demonstrar a existência de erros [Dijkstra 1976].
Enumeram-se assim, como principais Linguagens de Programação Estruturada: Fortran
[Backus, Beeber et al. 1957], Pascal [Wirth 1971] e C [Kernighan and Ritchie 1988].
As principais características da Programação Estruturada consistem em:
•
design descendente: decompõe-se o problema por etapas ou estruturas
hierárquicas;
•
recursos abstractos (simplicidade): consiste em decompor acções complexas
noutras mais simples, capazes de serem resolvidas com maior facilidade, ou seja,
as acções complexas são abstraídas em subrotinas ou em funções;
•
estruturas básicas:
o estrutura sequencial: a cada acção segue-se outra acção sequencialmente,
isto é, a saída de uma acção é a entrada em outra acção;
o estruturas de divisão: avaliam condições lógicas e, em função do resultado
das mesmas, realizam uma acção ou outra;
o estruturas de iteração: são sequências de instruções que se repetem um
determinado número de vezes.
12
Para melhor alcançar a separação de concerns foi sugerida a introdução de critérios para a
decomposição do sistema em módulos de maneira a permitir uma melhor flexibilidade e
compreensibilidade do sistema, definindo quais os métodos mais efectivos da decomposição em
módulos utilizando técnicas de abstracção e ocultação de informação fornecidas pela
Programação Estruturada [Parnas 1972].
Todavia é de referir a existência de problemas na Programação Estruturada, sendo de
destacar: o código espalhado das invocações aos vários módulos independentes, bem como, o
facto de os módulos abstraírem os procedimentos, as instruções, mas não conseguirem abstrair o
acesso aos dados (figura 1) resultando tal situação em programas maiores, menos flexíveis e
mais difíceis de desenvolver e de reutilizar ao longo do tempo.
Figura 1 - Módulos com acesso concorrente aos dados.
(Adaptada de [Booch 1994])
13
2.5
PROGRAMAÇÃO ORIENTADA A OBJECTOS
Nesta visão, o programa descreve estruturas e comportamentos de objectos e classes de
objectos. Um objecto encapsula dados passivos e disponibiliza operações activas sobre esses
dados, a estrutura dos dados do objecto define o seu estado e os métodos definem o seu
comportamento. A execução de um programa orientado a objectos é a troca de mensagens entre
objectos que modificam o seu estado [Booch 1994].
Na POO o problema é decomposto em classes e em objectos pertencentes ao domínio do
problema, onde os dados e os procedimentos já não estão separados e os detalhes da sua
interacção são encapsulados em objectos [Booch 1994].
Alguns fundamentos da POO são a herança e o polimorfismo, onde a primeira possibilita
aos objectos herdar propriedades, permitindo a reutilização de comportamentos, de outros
objectos. A existência de hierarquia de objectos (figura 2) facilita a compreensão do problema e,
através do polimorfismo, permite-se o tratamento desses objectos de forma igual. O
encapsulamento faculta assim a modificação do conteúdo interno dos objectos sem que essas
alterações afectem outros objectos.
Figura 2 - Hierarquia de classes
14
Em suma, alguns princípios usados na POO são:
o Princípio open closed: especifica que o desenho e desenvolvimento do código
deve permitir a extensão de novas funcionalidades (open) e minimizar alterações
aos módulos existentes (closed). O sistema abre-se a novas funcionalidades mas
deve estar encerrado a alterações que provocam incompatibilidades a módulos
existentes;
o Princípio da substituição de Liskov [Liskov and Wing 2001]: possibilita substituir
referências de classes base por uma outra classe que derive desta sem afectar o
normal funcionamento do módulo;
o Princípios da inversão da dependência: módulos hierarquicamente superiores não
devem depender de módulos hierarquicamente inferiores. Todos devem depender
de abstracções e esta última não deve depender de detalhes. Enquanto que os
detalhes devem depender de abstracções.
2.5.1
Linguagens POO
Remontando à história da Linguagens POO dá-se especial destaque aos inícios dos anos
60 com o aparecimento da linguagem de programação Simula 67, criada por Ole-Johan Dahl e
Kristen Nygaard. Nos anos 70 inicia-se o desenvolvimento do Smalltalk na Xerox PARC, alguns
anos depois Bjarne Stroustrup cria o C++ e finalmente na década de 90 foi criado na empresa
Sun o Java por James Gosling.
15
2.6
PROGRAMAÇÃO ORIENTADA A COMPONENTES
A Programação Orientada a Componentes (POC) permite elaborar programas a partir de
componentes de software pré-fabricados contidos em blocos de código executáveis. Os
componentes têm de seguir padrões específicos predefinidos, incluindo a inclusão de uma
interface e uma gestão de versões para garantir uma boa reutilização.
O objectivo da POC, passa assim por desenvolver software combinando componentes
pré-fabricados. Enquanto a POO dá ênfase a classes e objectos, a POC enfatiza interfaces e
composição. Os clientes dos componentes não necessitam de possuir conhecimentos de como é
que um componente é implementado, enquanto as interfaces de ligação continuam inalteradas, os
clientes não serão afectados por alterações na implementação do componente [Szyperski 1998].
Brown sumaria algumas definições do que é um componente [Brown and Wallnau 1998]:
“A component is a nontrivial, nearly independent, and replaceable part of a system that
fulfills a clear function in the context of a well-defined architecture. A component conforms to
and provides the physical realization of a set of interfaces.” (Philippe Krutchen, Rational
Software)
“A runtime software component is a dynamically bindable package of one or more
programs managed as a unit and accessed through documented interfaces that can be
discovered at runtime.” (Gartner Group)
“A software component is a unit of composition with contractually specified interfaces
and explicit context dependencies only. A software component can be deployed independently
and is subject to third-party composition.” (Clemens Szyperski)
16
“A business component represents the software implementation of an “autonomous”
business concept or business process. It consists of the software artifacts necessary to express,
implement, and deploy the concept as a reusable element of a larger business system.” (Wojtek
Kozaczynski, SSA)
“A software component is a piece of self-contained, self-deployable computer code with
well-defined functionality and can be assembled with other components through its interface.”
[Brown and Wallnau 1998]
Um componente de software é um bloco de código executável, pronto a usar, com
funcionalidades bem definidas, que pode ser combinado com outros componentes através de uma
interface.
Deste modo, um componente não é um objecto, mas disponibiliza recursos para os
instanciar. Muitas vezes, um único componente disponibiliza interfaces para várias classes interrelacionadas, daí a POC focar-se na arquitectura e encapsulamento (packging) ao invés da
intercooperação entre objectos[Szyperski, Bosch et al. 1999].
Alguns dos seus princípios são [Löwy 2003]:
o Separações da interface da implementação: alterações na implementação não
devem ser reflectidas na interface de ligação com os clientes;
o Compatibilidade entre binários: na POC os binários, geralmente não estão
compilados com o código da aplicação, mas sim separadamente e são invocados
pela aplicação. É permitido que os componentes possam ser substituídos por
novas versões, deste modo deverá existir compatibilidade dos binários entre as
várias versões;
17
o Independência da linguagem: a escolha de uma linguagem de programação não
deve impedir o uso de um componente;
o Transparência da localização: os componentes podem existir no mesmo processo
do programa ou noutro local. Para os componentes, o lugar onde estes são
executados deve ser transparente;
o Gestão de concorrência: como não é possível prever o uso que os componentes
irão ter por parte dos clientes, dever-se-á implementá-los de forma a prever o pior
caso possível. Para tal, os componentes devem implementar mecanismos de
sincronização de acesso;
o Controlo de versões: novas versões de componentes não deverão afectar as
aplicações dos clientes existentes. Deve existir uma tecnologia que permita a
evolução dos componentes mas que ao mesmo tempo permita o acesso às suas
versões mais antigas;
o Segurança com base em componentes: os componentes devem apresentar
garantias aos clientes de que estes não são comprometidos por ataques;
Exemplos de algumas tecnologias que aplicam este paradigma:
COM5 - Component Object Model
J2EE6 - Java 2 Platform, Enterprise Edition
CORBA7 - Common Object Request Bro-ker: Architecture and Specifications
COM+8 - Component Object Model Plus
5
6
7
http://www.microsoft.com/com/default.mspx (Acedido em Junho 2008)
http://java.sun.com/j2ee/overview.html (Acedido em Junho 2008)
http://www.omg.org/gettingstarted/history_of_corba.htm (Acedido em Junho 2008)
18
DCOM9 - Distributed Component Object Model
ActiveX10
.NET Framework11
2.7
PROGRAMAÇÃO PÓS-OBJECTOS
A POO apesar dos seus benefícios, apresenta algumas lacunas principalmente no que
concerne aos requisitos globais e às propriedades sistémicas. Não é possível neste tipo de
concerns decompô-los e organizá-los num único local. Assim estes tendem a estar espalhados e
entrelaçados com outros concerns[Elrad, Filman et al. 2001], tais como: a sincronização, as
restrições em tempo real, a persistência e a recuperação de falhas [Hürsch and Lopes 1995].
Embora seja possível identificá-los na fase conceptual, poucas linguagens de
programação, permitem mantê-los separados na fase de implementação. Por exemplo, é
pretendido que num programa, quando existir uma actualização de uma linha ou de um ponto,
essa alteração seja reflectida imediatamente no ecrã invocando um método (figura 3). Aponta-se
a existência de diversas alternativas que permitem este comportamento, nomeadamente, a
introdução de código no local onde são feitas as alterações dos valores. Para cada classe é
introduzido o código necessário nos métodos para actualizar o ecrã. Este código adicional
secundário tem como objectivo implementar um concern que é global ao sistema. É intrusivo nas
classes, para além de se repetir de igual forma por várias classes.
8
http://www.microsoft.com/com/default.mspx (Acedido em Junho 2008)
http://www.microsoft.com/com/default.mspx (Acedido em Junho 2008)
10
http://www.microsoft.com/com/default.mspx (Acedido em Junho 2008)
11
http://www.microsoft.com/net/ (Acedido em Junho 2008)
9
19
Figura 3 – Espalhamento de Concerns.
(Adaptada de [Kiczales, Hilsdale et al. 2001])
Programar vários concerns ao mesmo tempo e ao mesmo nível, implica que o
programador tenha de saber necessariamente todos os detalhes das áreas a desenvolver. Não
existe uma forma de tornar as áreas independentes e separar as tarefas. Neste âmbito, nos anos 90
surgiram várias tentativas de simplificar a crescente complexidade do software, essencialmente a
adição de novas necessidades a um projecto existente [Lopes 2002].
Desta forma, a POA surgiu com o ideal de que um sistema de software é superiormente
programado especificando separadamente concerns, propriedades ou áreas de interesse do
sistema e a descrição dos relacionamentos entre eles [Elrad, Filman et al. 2001].
Todavia é com Gregor Kiczales liderando uma equipa no centro de pesquisa de Palo Alto
(PARC, subsidiária da Xerox na Califórnia nos Estados Unidos da América) que se desenvolve o
conceito de POA. Este grupo começou por efectuar pesquisas na área da reflexão [Smith 1982] e
meta-object protocols [Kiczales, des Rivieres et al. 1991], desenvolvendo algumas ferramentas e
20
linguagens orientadas a aspectos tais como: AML, RG [Mendhekar, Kiczales et al. 1997] e DJ
[Lopes 1997] que culminaram na linguagem AspectJ [Kiczales, Hilsdale et al. 2001; Kiczales,
Hilsdale et al. 2001] que hoje em dia é considerada a linguagem padrão da POA.
Porém, outra tecnologia foi apresentada em 1999 no Centro de Pesquisa de T.J. Watson
da empresa IBM, Hypersapces [Ossher and Tarr 1999; Tarr, Ossher et al. 1999; Ossher and Tarr
2001] como uma evolução de subject-oriented programming [Harrison and Ossher 1993].
Na tabela 1, aponta-se as principais datas que marcaram o aparecimento de tecnologias
sobre separação de concerns.
Ano
1972
1976
1970s
1980s
1992
1993
1995
1996
1997
1999
2000
2001
2002
2002- …
Evento
Parnas, decomposição de sistemas em módulos.
Dijkstra, Separação de concerns.
Programação Estruturada
Programação Orientada a Objectos
Adaptive Programming (Lieberherr, Northeastern University)
Composition Filters (Aksit, Twente University)
Subject-Oriented Programming (Harrison e Ossher, IBM)
Artigo - Separation of Concerns (HÄurch e Lopes)
Programação Orientada a Aspectos (Kiczales, Xerox PARC)
Primeira Workshop sobre Programação Orientada a Aspectos (ECOOP 97)
Artigo - Programação orientada a Aspectos (Kiczales etal.)
Linguagem de Programação AspectJ
Tese - D: A Language Framework for Distributed Programming (Lopes)
Artigo - Multi-dimensional Separation of Concerns (Tarr, Harrison e Ossher,
IBM)
Linguagem de Programação Hyper/J
Primeira Workshop sobre Advanced Separation of Concerns (OOPSLA )
Artigo Aspect-Oriented Programming is Quantification and Obliviousness
(Filman e Friedman)
Edição Especial da ACM sobre Programação orientada a Aspectos.
Primeira conferência Internacional sobre Desenvolvimento de Software
Orientado a Aspectos (DSOA)
Evolução nas várias subáreas da DSOA
Tabela 1 – Cronograma temporal da investigação sobre separação de concerns
21
2.8
META-PROGRAMMING
Este paradigma tem como base a reflexão computacional [Smith 1985; Maes 1987] que
permite que um sistema mantenha informação sobre si e use essa informação para alterar o seu
comportamento [Maes 1987]. No paradigma meta-programing [Okamura and Ishikawa 1994 ],
um sistema está dividido em dois níveis: base-level e meta-level (figura 4).
Os construtores básicos da linguagem de programação, classes ou invocação de objectos,
são descritos no base-level e podem ser estendidos ou redefinidos pela meta-programming. Cada
objecto é associado com um meta-object através de um meta-link. O meta-object é responsável
pela semântica das operações no objecto base. A interface entre o programa, base-level, e as
camadas de nível superior é efectuada por meta-object protocols.
Este último estabelece interfaces para a linguagem de programação que permitem ao
programador modificar o comportamento da linguagem de programação e a implementação. Os
objectos base-level são responsáveis pelos algoritmos básicos do programa e os seus
comportamentos são melhorados e adaptados através de meta-level objects de acordo com
requisitos específicos. Através da captura de mensagens enviadas e recebidas para os objectos,
meta-level objects, estes conseguem efectuar o trabalho de concerns [Hürsch and Lopes 1995].
22
Figura 4 - Meta-programming.
(Adaptada de [Okamura and Ishikawa 1994 ])
2.9
SUBJECT-ORIENTED PROGRAMMING
Subject-Oriented programming é uma extensão para a POO possibilitar a composição de
diferentes visões subjectivas do mesmo objecto, facultando o desenvolvimento de aplicações
separadamente, bem como, efectuar posteriormente a sua composição.
Estes objectos contêm assim, propriedades e comportamentos específicos que ao longo
do tempo e com a evolução necessitam de ser alterados. Estas propriedades e comportamentos
são subjectivas ao tipo de objectivo a que se destinam, o mesmo objecto pode ter representações
diferentes dependendo do tipo de objectivo com que é idealizado e da entidade que o idealiza
(figura 5).
23
Deste modo, a Subject-oriented programimng pretende facilitar o desenvolvimento e
evolução de aplicações que colaboram entre si. Esta colaboração é alcançada partilhando
subjects (objectos) que contribuem para a execução de operações. O desenvolvimento das
aplicações é efectuado em separado e sem dependência entre aplicações, permitindo estender
sem modificar o original [Harrison and Ossher 1993].
A composição combina hierarquias de classes a fim de produzir novos subjects que
incorporam funcionalidades de subjects existentes. A Subject-Oriented programming oferece
ferramentas de composição a fim de integrar os sujeitos [Kaplan, Ossher et al. 1996]. Esta
tecnologia evoluiu para se tornar a Separação Multidimensional de Concerns (secção 2.13).
Figura 5 - Subject-oriented programming.
(adaptada de [Harrison and Ossher 1993])
24
2.10
PROGRAMAÇÃO ADAPTATIVA
A Programação Adaptativa [Lieberherr 1996; Lopes 1997] é um modelo de programação
com base em padrões de código, onde estes podem ser classificados em diferentes categorias,
cada um capturando um tipo diferente de concern. Posteriormente os padrões de código são
implementados como componentes de software de alto nível que interagem com os outros
componentes de forma muito solta através da resolução de nomes [Lopes 2002].
Perante problemas como a elaboração de uma nova operação ou regra de negócio num
sistema, é necessário colocar todo o código num só método, numa só classe, ou adopta-se a
divisão do código da operação por várias classes. Porém o problema deste último método
relaciona-se com a dependência na estrutura das classes, o que vai adicionar complexidade nos
métodos e dificultar a evolução. A Programação Adaptativa resolve esta questão utilizando
padrões de propagação, abstraindo o comportamento do concern num local e a estrutura das
classes noutro. O comportamento é expresso a um alto nível utilizando uma estratégia transversal
[Lieberherr, Orleans et al. 2001].
Nesta continuidade, a Programação Adaptativa pode ser definida em dois blocos: um
bloco de estrutura implementado por um grafo do dicionário de classes, que contém a relação
entre classes e um segundo bloco comportamental, implementado por um padrão de propagação
que é depois composto com o grafo e que assim possibilita a interacção de forma flexível entre
ambos.
25
2.11
FILTROS DE COMPOSIÇÃO
Filtros de composição são uma extensão à POO através da adição de filtros ortogonais e
modulares que permitem aumentar a adaptabilidade e a reutilização dos objectos [Aksit, Wakita
et al. 1993; Bergmans and Aksit 2001]. Assim, são elaborados filtros que filtram as mensagens
enviadas e recebidas pelos objectos e efectuam acções através da definição de condições para a
aceitação ou rejeição de uma mensagem.
Esta tecnologia consiste principalmente no interface e na implementação. Na parte de
interface faz-se o tratamento das mensagens de chegada e de saída, composto de um ou mais
filtros de entrada e saída, e opcionalmente, de objectos internos e externos e de declarações de
métodos (figura 6). Os filtros são controlados por condições, nomes de filtros, nome das
declarações de métodos e nomes das condições que são visíveis para os objectos clientes apesar
da sua implementação ser invisível. Na parte de implementação estão as definições de métodos,
instâncias de variáveis, definições de condições e opcionalmente operações de inicialização. Esta
parte é totalmente encapsulada no objecto, possibilitando através deste, a alteração de mensagens
e o seu redireccionamento para objectos internos ou externos. Assim pode ser alcançada a
separação de concerns definindo um filtro por cada concern.
26
Figura 6 - Filtros de composição.
(adaptada de [Aksit, Wakita et al. 1993])
2.12
PROGRAMAÇÃO ORIENTADA A ASPECTOS.
A origem do termo POA é resumida por Cristina Lopes, “Não me consigo lembrar da
data exacta quando decidimos chamar ao nosso trabalho ‘Programação Orientada a Aspectos’,
mas lembro-me que o termo foi sugerido por Chris Maeda […]. No meu portátil a primeira
referência a ‘POA’ ocorre nos fins de Novembro de 1995. Em Janeiro de 1996, o meu portátil
indica que estávamos a usar ‘Open Implementation’ e ‘POA’ ao mesmo tempo, embora para
partes diferentes do trabalho do grupo. Em Junho de 1996 enviámos a proposta para o DARPA
intitulada ‘Programação Orientada a Aspectos’, no fim de 1996 desaparecem todas as referências
a ‘Open Implementation’ do meu portátil.” [Lopes 2002].
27
Gregor Kiczales como já referido, liderava uma equipa de investigação em “Open
implementaions” [Kiczales 1996] e “Metaobject Protocol” [Kiczales, des Rivieres et al. 1991], à
qual no verão de 1995 se junta Cristina Lopes, acrescentando conhecimentos de programação
adaptativa [Lopes 1996]. Em 1997, Gregor Kiczales apresenta o seu trabalho em “Programação
Orientada a Aspectos” na conferência ECOOP’97, expondo os primeiros termos, conceitos e
análise dos problemas que se tentavam solucionar [Kiczales, Lamping et al. 1997].
Gregor Kiczales afirma que “[…] O objectivo deste trabalho é tornar possível que os
programas consigam claramente capturar todos os aspectos importantes do comportamento do
sistema […]” [Kiczales, Lamping et al. 1997].
As linguagens de programação com base num só tipo de abstracção são inadequadas para
sistemas complexos. Diferentes tipos de comportamentos do sistema tendem a ter a sua própria
“forma”, enquanto um tipo de abstracção pode ser bom para capturar um tipo de comportamento
do sistema, é insuficiente num outro tipo de comportamento [Kiczales, Lamping et al. 1997].
As Linguagens de POO conseguem capturar os comportamentos dos objectos mas falham
em comportamentos secundários, comportamentos estruturais e comportamentos invariantes.
Estes comportamentos não são possíveis de isolar num só local e dispersam-se pelos módulos do
sistema, ou seja, são transversais aos módulos. Nesta mistura de comportamentos o resultado é
código espalhado que leva à perda de modularidade e a uma possível perda de abstracção.
Para combater estas fragilidades a POA apresenta uma nova forma de modularização que
permite a implementação de cada aspecto do sistema separadamente na sua forma natural, e a sua
composição usando uma ferramenta “Aspect Weaver”. As componentes funcionais continuam a
ser implementadas nos paradigmas existentes enquanto as componentes não funcionais são
28
implementadas numa linguagem de aspectos. Criando-se uma solução de abrangência geral e
independente do tipo de comportamento do sistema, funcionando como uma extensão.
Assim, a POA define Crosscutting concern como um comportamento do sistema que
atravessa vários módulos deste. Este é dividido em concerns base, comportamentos primários e
crosscutting concern, comportamentos secundários e é adicionada uma nova unidade modular, o
aspecto. A implementação de concerns é feita independentemente e a coordenação e
comunicação entre eles é relegada para os aspectos.
O programador, detentor da compreensão do sistema, fica responsável por explicitamente
definir os aspectos, utilizando uma linguagem que deva permitir um nível abstracto adequado e
com a localidade apropriada [Kiczales, Lamping et al. 1997].
2.12.1 AspectJ
Uma implementação do paradigma é o AspectJ12 [Kiczales, Hilsdale et al. 2001;
Kiczales, Hilsdale et al. 2001] que funciona com uma extensão à linguagem POO Java13,
adicionando-lhe conceitos práticos de POA e permitindo a implementação modular de
crosscutting concerns. Acrescentando um novo conceito, o join point e alguns construtores:
pointcuts, advice, inter-type declarations e aspects.
AspectJ foi desenvolvido com a compatibilidade com a linguagem Java em mente, isto é,
os programas em Java têm de ser válidos em AspectJ e os programas AspectJ deverão ser válidos
na Java virtual machine (JVM) padrão [Lindholm and Yellin 1999]. As ferramentas de suporte
12
13
http://www.eclipse.org/aspectj/ (Acedido, Julho 2008)
http://java.sun.com/ (Acedido, Julho 2008)
29
do Java foram alteradas para suportar a linguagem, tais como os Ambientes Integrados de
Desenvolvimento (IDEs), bem como, as ferramentas de documentação. Assim, os programadores
podem utilizar a extensão AspectJ de forma tão natural como o Java.
Como já referido, o AspectJ adiciona: pointcuts e advices que permitem afectar
dinamicamente o fluxo normal do programa, inter-type declarations que alteram estaticamente a
hierarquia de classes do programa e aspectos que encapsulam todos os novos construtores.
Outra adição a enumerar é o conceito de Join Point que define um ponto no fluxo do
programa. Estes são utilizados por pointcuts para expor os locais de intercepção onde é
necessária a adição de comportamentos transversais. Por sua vez, advice é o bloco de código que
vai ser executado quando o join point for alcançado.
Na sua conjuntura existem dois tipos de comportamentos possíveis: os comportamentos
dinâmicos que avaliam condições definidas nos pointcuts em tempo de execução e os
comportamentos estáticos onde as avaliações são feitas em tempo de compilação.
Todavia acrescenta-se que os aspectos são as unidades modulares para a declaração dos
crosscutting concerns, são similares às classes do Java mas incluem os construtores pointcut,
advice e inter-type declaration.
Nos próximos subcapítulos expõe-se uma breve introdução da linguagem, tendo como
base a guia de programação14.
14
http://www.eclipse.org/aspectj/doc/released/progguide/index.html (Acedido, Julho 2008)
30
Join Point
2.12.1.1
Join Point é um ponto bem definido no fluxo do programa. Este modelo permite capturar:
2.12.1.2
•
Chamadas a métodos;
•
Execução de métodos;
•
Criação de objectos;
•
Execução do construtor do objecto;
•
Inicialização de objectos;
•
Pré inicialização de objectos;
•
Referências e atribuição a variáveis;
•
Tratamento de excepções.
PointCuts
Tendo por base que os pointcuts permitem seleccionar e agrupar join points, e utilizando
a figura 7 como exemplo de referência, pretende-se seleccionar todas as chamadas ao método
setX com argumento int e retorno void da classe Point.
Figura 7 – Diagrama do exemplo.
31
Para se criar um Pointcut, utiliza-se o operador pointcut e declara-se o seu nome, a sua
interface e o join point a capturar. No exemplo 1, expõe-se a utilização da primitiva call
(identifica um join point do tipo chamada a método) para a captura de chamadas ao método setX
da classe Point.
pointcut move(): call(void Point.setX(int))
Exemplo 1 – declaração de um pointcut.
De modo a agregar vários join points num só pointcut, aplicam-se os operadores lógicos
‘ou’ definido na linguagem com os símbolos ‘||’, o operador ‘e’ definido na linguagem com os
símbolos ‘&&’ e o operador de negação definido na linguagem com o símbolo ‘!’.
Para facilitar a atribuição de join points é possível definir o nome do fluxo (método,
variável, excepção, etc.) a capturar, utilizando wildcards para os expressar. Denominando este
tipo de pointcut, de Property-Based Primitive PointCut, este permite que numa só declaração se
agrupem um conjunto de join points, pois estes seguem uma determinada sintaxe no seu nome.
No exemplo 2 captura-se todos os métodos da classe Figure cujo nome comece por make
com qualquer tipo de parâmetros mas só os métodos declarados com retorno void.
call(void Figure.make*(..))
Exemplo 2 - Pointcut utilizando WildCards.
2.12.1.3
Advice
Pointcuts seleccionam join points mas não exercem qualquer acção. Porém com o auxílio
dos advices possibilita-se implementar o comportamento transversal associando assim, um
pointcut a um bloco de código.
32
O bloco de código de um advice que está associado a um ou mais pointcuts pode ser
executado em três momentos diferentes: ‘antes’ (exemplo 3), ‘depois’ (exemplo 4) ou ‘ao invés
de’ (exemplo 5).
before():Pointcut{ Bloco de Código }
Exemplo 3 – Advice do tipo ‘antes’.
after():Pointcut{ Bloco de Código }
Exemplo 4 - Advice do tipo ‘depois’.
around():Pointcut{ Bloco de Código }
Exemplo 5 - Advice do tipo ‘ao invés de’.
2.12.1.4
Inter-type declarations
Inter-Type declarations permitem adicionar comportamentos a classes e alterar as suas
hierarquias, declarar membros de classes que se propagam por múltiplas classes ou alterar a
relação de herança entre classes. Por exemplo, permite adicionar a uma classe atributos,
métodos, ou implementar um padrão de desenho. No exemplo 6 é mostrado como adicionar
métodos e variáveis necessárias à implementação do padrão de desenho Observer que
posteriormente são compostos pelo Weaver com a classe Point.
aspect PointObserving {
private Vector Point.observers = new Vector();
public static void addObserver(Point p, Screen s) {
p.observers.add(s);
}
public static void removeObserver(Point p, Screen s) {
p.observers.remove(s);
}
...
}
Exemplo 6 – Implementação do padrão de desenho Observer.
33
2.12.1.5
Aspectos
Como já mencionado, os aspectos encapsulam os pointcuts, advices e inter-type
declarations, para além de poderem conter métodos e atributos. São as unidades modulares onde
todo o comportamento relativo a um ou mais comportamentos transversais é implementado e fica
localizado (exemplo 7).
aspect OmeuAspecto{
//Dentro deste bloco é possível declarar
//Métodos e declaração de atributos iguais ao java
//pointcuts e advices e inter-type declarations referentes ao AspectJ
}
Exemplo 7 – declaração de aspectos.
2.13
SEPARAÇÃO MULTIDIMENSIONAL DE CONCERNS
Separação multidimensional de concerns é um paradigma para modelar e implementar
software artifacts, suportando a separação simultânea de concerns através da decomposição e
composição de software artifacts ao longo de múltiplas dimensões, quebrando a “tirania da
decomposição dominante”, sendo uma evolução da subject oriented programming [Tarr, Ossher
et al. 1999].
Durante o desenho de um sistema são identificados requisitos e funcionalidades que na
fase de implementação só são possíveis de decompor de uma forma: por objectos.
Funcionalidades que afectem vários objectos vão espalhar-se por diversos outros implicando a
evolução e compreensão do sistema. Futuras alterações são intrusivas no sistema ao invés de
aditivas. Para alcançar a decomposição por concerns este paradigma utiliza Hyperslices.
34
Relativamente ao hyperslice, tido como um conjunto de módulos convencionais escritos
num qualquer formalismo, têm a finalidade de encapsular concerns numa dimensão fora da
dimensão dominante. Os módulos desta dimensão contêm todas as unidades que pertencem ou
tratam do concern em causa.
Sendo bem elaborada a separação de concerns, pode fornecer a muitos engenheiros de
software diversos benefícios, entre eles: a complexidade reduzida, a reutilização e a evolução. A
escolha dos limites da separação dos concerns depende dos requisitos do sistema e dos tipos de
decomposição e composição que um dado formalismo suporta. As metodologias predominantes e
formalismos disponíveis só suportam separação de concerns ortogonal, ao longo de uma única
dimensão de decomposição e composição [Ossher and Tarr 2000].
Esta tecnologia evolucionou para Concern Manipulation Evironment (CME) [Harrison,
Ossher et al. 2005; Harrison, Ossher et al. 2005] suportando a extracção e composição de
concerns durante o ciclo de vida de um sistema de software, disponibilizando um conjunto de
ferramentas para programadores. CME é assim, um projecto Eclipse15 Open Source, que já não
está em desenvolvimento e encontra-se arquivado16.
2.14
DESENVOLVIMENTO DE SOFTWARE ORIENTADO A ASPECTOS
Para além de modularizar crosscutting concerns é necessário primeiramente, identificálos numa fase anterior à implementação. A POA tornou-se assim parte de uma área maior, o
15
16
http://www.eclipse.org (Acedido, Julho 2008)
http://www.eclipse.org/technology/archived.php (Acedido, Junho 2008)
35
Desenvolvimento de Software Orientado a Aspectos (DSOA), existindo duas grandes áreas nela
inserida, a Early-Aspects17 [Rashid, Sawyer et al. 2002] e a POA.
A investigação na área Early-Aspects tem como objectivo identificar aspectos
crosscutting numa fase inicial da elaboração do sistema, e determinar o impacto dos aspectos
identificados em fases posteriores. A Early-Aspects foca-se na engenharia de requisitos. Para
atingir um consenso nesta área é introduzido um modelo para a engenharia de requisitos,
primeiro são identificados e especificados os concerns seguindo-se, a partir destes, a
identificação dos potenciais aspectos que são especificados e atribuídos de uma prioridade.
Finalmente é definida a dimensão em que se enquadram os aspectos.
Os concerns que influenciem ou interajam com mais de um requisito são chamados
aspectos candidatos, prosseguindo-se uma detalhada especificação dos aspectos candidatos, o
que permite uma refinação de aspectos mais sucinta e que tipo de interacções e conflitos, entre
aspectos existe. Para resolver conflitos entre aspectos é preciso atribuir-lhes prioridades.
2.15
COMUNIDADES E SITUAÇÃO EM PORTUGAL
Existe uma associação de DSOA, a nível Mundial18 e Europeu19, que organiza todos os
anos uma conferência Internacional20 com Workshops21 sobre esta temática. Em Portugal existe
17
http://www.early-aspects.net/ (Acedido, Julho 2008)
http://www.aosd.net/aosa.php (Acedido, Julho 2008)
19
http://www.aosd-europe.net/ (Acedido, Julho 2008)
20
http://aosd.net/ (Acedido, Julho 2008)
18
21
http://portal.acm.org/browse_dl.cfm?linked=1&part=series&idx=SERIES10702&coll=portal&dl=ACM&CFID=948
5317&CFTOKEN=42857112 (Acedido, Julho 2008)
36
um grupo de pesquisa na área de early-aspects22, sediada na Universidade Nova de Lisboa sob a
liderança da Professora Doutora Ana Moreira.
2.16
DESAFIOS, PROBLEMAS E OPORTUNIDADES
A POA teve uma fase de evolução e amadurecimento desde que foi apresentada em 1997
até 2002, ano em que foi incorporada como uma subárea da DSOA. O desenvolvimento da
investigação académica está na sua maioria focado na subárea early-aspects, desenvolvendo-se
ferramentas para a identificação de aspectos na fase de desenho e como estes se deverão
relacionar com o resto do sistema. Deste modo, estão a ser elaboradas ferramentas, que permitem
a criação visual de aspectos de uma forma similar com o que é feito com UML para a POO.
Na fase de implementação estão a ser elaboradas ferramentas que permitem visualizar a
interacção dos aspectos com o resto do sistema e efectuar a depuração do código, aproveitando
também o novo conceito de modularização através de bytcode weaving para criar sistemas mais
modulares, onde existe adição ou remoção, em tempo de execução, de funcionalidades e
requisitos consoante as necessidades.
Alguns paradigmas apresentados posteriormente evoluíram e agora fazem parte
integrante da POA, tais como o Adpatative programing [Lieberherr, Orleans et al. 2001].
Como tecnologia emergente, a POA tem a potencialidade de facilitar e diminuir o tempo
de desenvolvimento de um sistema de software. Para o sucesso da tecnologia é necessário uma
22
http://aosd.di.fct.unl.pt/aosd-group/ (Acedido, Julho 2008)
37
maior adaptação ao nível comercial. Para tal, é essencial uma melhor integração com as
tecnologias existentes, para que assim exista uma transição natural sentida como uma evolução.
Esta evolução não pode ser só das linguagens de programação existentes, tem de envolver todas
as ferramentas que são utilizadas com essa linguagem, entre outros, editores, documentação e
depuradores.
A constante transformação do mundo actual determina que o que é uma necessidade hoje,
pode ser substituída por uma outra necessidade completamente diferente amanhã. O que
influencia as empresas a estar em constante evolução e alterarem as suas regras de negócio. Toda
esta conjuntura de factos em constante mudança, evidência a necessidade de uma nova forma de
se conseguir desenvolver software que permita em tempo útil, acompanhar estas mudanças. A
POA possibilita este facto através da adição de funcionalidades e regras de negócio, que não são
intrusivas à evolução e/ou modificação dos sistemas.
Devido à nova forma de modularidade é fundamental a existência de alguma coordenação
das alterações que se possam efectuar no sistema, para que o seu comportamento seja previsível,
ou seja, as alterações não devem conduzir a aspectos que deixam de interceptar pontos no fluxo
ou estes fluxos deixem de ser o ponto de intercepção pretendido, tornando o comportamento do
sistema imprevisível.
38
39
3. PROGRAMAÇÃO ORIENTADA A ASPECTOS
Nos pontos que se seguem tentar-se-á abordar os problemas inerentes à POA, bem como, as suas
primordiais vantagens e desvantagem que influenciaram a busca de soluções por parte da POA.
Assim, incidir-se-á numa primeira análise do que se entende por um sistema POA, seguindo-se a
exposição das tecnologias existentes, aplicações e estudos quantitativos inerentes a esta área de
investigação.
3.1
COMPORTAMENTOS GLOBAIS
A motivação do paradigma da Programação Orientado a Aspectos, é ajudar a modularizar
múltiplos aspectos de um sistema de software, aplicando-lhe o princípio da separação de
concerns. O sistema é composto por módulos independentes, raciocinando modularmente e sem
as dificuldades e complexidades apresentadas por outras tecnologias de separação de concerns
semelhantes. A partir da figura 8 mostra-se dois módulos da mesma aplicação, cada um dos
módulos implementa uma determinada funcionalidade necessária pela aplicação, contudo existe
ainda uma dependência explícita de entre os dois módulos. O módulo A necessita de conter
referências ao módulo B, nas várias unidades que o compõem, deste modo os dois módulos não
são totalmente independentes.
40
Figura 8 - Interacção entre Módulos.
Na figura 9 pode verificar-se a existência de uma sobreposição de uma unidade elementar
B1 pertencente ao módulo B a três unidades do módulo A. Esta dependência é em certo modo,
criada pela forma como se estruturou o programa e por condicionantes introduzidas pelo
paradigma que se está a utilizar na implementação, por exemplo, a hierarquia de classes no
paradigma orientado a objectos. Esta dependência entre módulos vai condicionar a sua
independência e reutilização para futuros projectos.
Figura 9 - Sobreposição de unidades elementares.
Existem comportamentos num sistema de software que se repetem sistematicamente em
vários módulos do sistema, não sendo possíveis de se situarem num só local pois são uma
41
necessidade global do sistema, criada pela necessidade do sistema em ter um certo
comportamento específico (figura 10).
Figura 10 - Comportamentos Globais do Sistema
3.2
ANÁLISE DO PROBLEMA
O primeiro problema que se pode identificar diz respeito ao desenho da arquitectura de
um sistema de software. O engenheiro de software, quando lhe é atribuída a tarefa de criar um
novo sistema de software, depara-se com uma série de dilemas que afectam a criação do sistema,
nomeadamente o tempo de que se dispõe para criar o sistema e a evolução dos requisitos do
sistema. Não existem certezas de que os requisitos não vão sendo alterados durante o
desenvolvimento do sistema ou sua posteriori.
42
Dependendo dos factores anteriores criam-se sistemas que são mais rápidos de produzir,
ou seja, têm um tempo reduzido para sair para o mercado, mas são difíceis de evoluir
posteriormente e nem sempre apresentam garantias de qualidade. Por outro lado uma
arquitectura do sistema mais cuidada permite uma melhor evolução do sistema, com maior
qualidade, mas implicando maior tempo de desenvolvimento e gastos adicionais.
Definir um meio-termo não é fácil, mas focando-se apenas nos requisitos presentes e
desenvolvendo um sistema com base em tecnologias que permitam a acomodação modular de
novos requisitos, ou as suas alterações, obtém-se assim uma solução que permitirá a elaboração
de um sistema de melhor qualidade, em menos tempo e com menor custos.
A tarefa de gerir projectos é contudo um factor crítico, devido à falta de modularidade
implicando deste modo mais comunicação entre equipas, reuniões e debates o que encurta o
tempo de desenvolvimento.
Não obstante, outro problema inerente é o facto de que a tecnologia utilizada para a
implementação produz um impacto negativo na modularização, ou seja, muitas vezes aquando do
desenho do sistema para a fase de implementação perde-se modularidade. A tecnologia usada na
implementação não permite implementar modularmente todas as preocupações desenhadas e
identificadas e algumas acabam por se sobrepor a outras (exemplo 8).
43
public class SomeBusinessClass extends OtherBusinessClass {
"Dados do módulo"
"Stream de Registo"
"Flag de consistência de dados"
(1)
"Métodos redefinidos da super classe"
public void performSomeOperation(<informação da Operação>)
{
"Garantir autenticidade"
"Garantir que a informação satisfaça contrato"
"Bloquear o objecto para garantir consistência"
(2)
"Garantir que a cache está actualizada"
"Fazer o registo do início da operação"
"Realizar Operação"
"Fazer o registo do final da operação"
(3)
"Desbloquear o objecto"
}
public void save(<persistência de dados>)
{
"..."
}
(4)
public void load(<persistência de dados>)
{
"..."
}
}
Exemplo 8 – Intrusão em Classes.
44
No exemplo 8 as secções 1, 2, 3 e 4 não fazem parte da lógica primária da classe, são
preocupações referentes a funcionalidades globais do sistema existindo uma invasão de código
na classe, de funcionalidades que não contribuem para a implementação do seu objectivo
primário. Este problema de modularização resulta dos comportamentos que são transversais aos
objectos, são globais ao sistema e produzem espalhamento (scaterring) e intrusão (tangling) de
código [Kiczales, Lamping et al. 1997]. Sendo o espalhamento um efeito causado por um
comportamento transversal, que adiciona código extra em vários módulos do sistema de
software, para implementar um determinado comportamento. E intrusão o efeito de ter vários
comportamentos transversais na mesma secção de código, por exemplo num método, o que gera
o denominado código esparguete.
A falta de modularização no código produz efeitos negativos no desenvolvimento e
evolução dos sistemas de software, tais como [Laddad 2003]:
•
Baixa produtividade: o desenvolvimento de um módulo deixa de ser
exclusivamente incidente numa só área específica para se alargar a outras. O
programador tem de possuir conhecimento de todas essas áreas, aumentando
assim o tempo que demora a desenvolvê-las. As revisões feitas no módulo têm de
ser efectuadas com todos os especialistas das áreas abrangidas e quanto maior o
grupo de pessoas, mais difícil se torna estabelecer um horário que permita a sua
reunião;
•
Baixa qualidade: o aumento do código necessário para implementar os módulos,
devido ao aumento das áreas de foco de cada programador, aumenta a
probabilidade da existência de erros e de secções elaboradas com menor rigor;
45
•
Baixa reutilização: as alterações feitas nos módulos, para introduzir os
comportamentos transversais, são invasivas para a reutilização do módulo noutro
projecto. Ter-se-á assim, de remover todos esses comportamentos adicionais das
classes;
•
Baixa evolução: a adição ou alteração de comportamentos poderá levar à
reorganização ou modificação de vários módulos;
•
Difícil compreensão: a leitura e compreensão de um módulo implica possuir um
conhecimento global de todas as áreas que este implementa, ou seja, o sistema
tem de ser compreendido na sua globalidade;
•
Problemas de depuração: a depuração tem de ter em conta todas as áreas que o
módulo implementa.
Neste contexto, um exemplo de intrusão são os padrões de desenho. Embora estes
melhorem a modularidade dos sistemas, algumas classes necessitam de ser modificadas para
acomodarem as alterações. Com a POA os padrões de desenho são desenvolvidos separadamente
e as alterações necessárias, nas classes, são implementados em aspectos, sendo posteriormente
compostos com as classes (secção 3.15).
46
3.3
SISTEMA MODULAR
A POA visa permitir modularizar, na sua forma natural, software concerns que não são
adequadamente decompostos pelos paradigmas mais comuns, em especial o paradigma orientado
a objectos. Como já exposto, um concern é uma funcionalidade ou requisito do sistema de
software, e pode variar desde noções de alto nível como segurança e qualidade de serviço, até
noções de baixo nível como caching e buffering. Os concerns podem ser funcionais ou não
funcionais [Elrad, Filman et al. 2001].
Um módulo pode ser definido de várias formas, embora seja geralmente um componente
de um sistema de software que opera independentemente de outros módulos.
Assim, a modularização de todos os concerns de um sistema de software permite uma
nova forma de raciocínio, de análise e implementação do sistema por módulos.
Contudo o código de um concern torna-se modular se for possível [Kiczales and Mezini
2005]:
•
Ser localizado textualmente;
•
Existir uma interface de interacção bem definida com o resto do sistema;
•
A interface ser uma abstracção da implementação, sendo possível alterar a
implementação sem violar a interface;
•
O módulo ser automaticamente composto, por um compilador (Weaver), loader,
linker, em várias configurações, com outros módulos para produzir um sistema.
Raciocinar modularmente permite tomar decisões em relação aos módulos focando-se
apenas e só na sua implementação. É necessário analisar previamente o sistema na sua
47
globalidade e a POA assim o exige de maneira a permitir, depois de completa a análise global, o
raciocínio modular.
Desta forma, a POA permite estruturar o desenho e o código do sistema de acordo com o
que os programadores pensam do sistema, possibilitando assim a decomposição e composição de
software concerns, em especial a modularização de crosscutting concerns [Elrad, Aksit et al.
2001].
Crosscutting concerns são aspectos de um sistema de software que afectam outros
concerns, ou seja, são concerns que são transversais a outros concerns [Kiczales and Mezini
2005].
A POA permite a implementação modular de crosscutting concerns, junto com os
benefícios prévios da POO, funcionando como uma extensão do paradigma POO. A base do
sistema de software contínua a ser implementada usando POO. Crosscutting concerns são
igualmente implementados separadamente e encapsulados em unidades modulares chamadas
aspectos. Com a ajuda de ferramentas os módulos crosscutting são combinados (Weaving) com
os outros módulos base do sistema para gerar o programa final.
3.4
SISTEMA POA
Para alguns autores, a POA pode ser entendida como o desejo de se fazer declarações
quantificadas sobre o comportamento de programas, e conseguir com que essa quantificação
estruture programas escritos por programadores alheios a essa quantificação [Filman and
Friedman 2000].
48
Segundo a opinião dos autores Filman and Friedman existem duas propriedades
necessárias para uma linguagem de programação ser considerada POA a ‘quantificação’ e a
‘alheabilidade’ [Filman and Friedman 2000].
A quantificação é uma atribuição na forma no programa P, quando se disputar qualquer
condição C, efectua-se a acção A em P. Definindo as seguintes preocupações para o programador
e/ou engenheiro de software:
•
Quantificação: que tipo de condições C se podem especificar?
•
Interface: como é que as acções A interagem com o programa base e entre eles?
•
Entrelaçamento: como irá o sistema interligar a execução das acções base do
programa P com as acções A?
A quantificação pode ser estática, quantificação sobre programas na forma de texto, ou
dinâmica que associa aspectos a eventos que acontecem durante a execução do programa.
Quanto à ‘alheabilidade’, esta estipula que ao examinar o bloco de código de um módulo
base, não se deve conseguir afirmar se o código de um aspecto irá ser executado. Não deve
existir acomodação do módulo base.
Em suma, um sistema POA será um sistema que permite quantificar acções e através de
uma interface interagir com o programa base. A combinação das várias componentes é feita
através da forma de texto ou de eventos, sendo o sistema base alheio a todo este processo.
49
3.5
METODOLOGIA DSOA
O desenvolvimento de um software orientado a aspectos é geralmente feito em três
etapas:
1. Decomposição aspectual: são identificados os requisitos do sistema, apontando os
requisitos funcionais, denominados base concerns, e requisitos não funcionais, os
crosscutting concerns;
2. Implementação de concerns: procede-se à implementação independente dos
vários concerns. A implementação de cada concern é feita separadamente
recorrendo a um paradigma de programação, sendo o paradigma mais utilizado a
POO;
3. Recomposição aspectual: definem-se as regras de composição dos vários módulos
do sistema previamente implementados. Estas regras são implementadas em
unidades modulares chamadas aspectos. O processo de composição Weaver
utiliza-as em conjunto com os módulos para produzir o sistema final. Estas regras
são definidas numa linguagem de programação que implementa o paradigma
orientado a aspectos.
A partir da figura 11 é possível visualizar a interacção das várias etapas.
50
Figura 11 - Metodologia POA.
(Adaptada de [Laddad 2003])
3.6
ANATOMIA DA LINGUAGEM POA
O Paradigma Orientado a Aspectos permite especificar, separadamente, como um módulo
deve reagir a um evento que ocorre em outro módulo. Esta ideia permite a separação dos
módulos já que estes não interagem directamente. Uma linguagem que implemente o paradigma
tem de possuir:
•
Uma especificação que descreva os construtores e a sintaxe que irá ser utilizada
para combinar os crosscutting concerns com a lógica dos base concerns;
•
Verificação da aderência do código à especificação da linguagem e gerar o
programa, o que é geralmente feito por um compilador ou interpretador.
51
3.7
ESPECIFICAÇÃO DAS LINGUAGENS
Uma implementação do Paradigma Orientado a Aspectos tem de possuir uma linguagem
que permita a implementação dos software concerns e uma linguagem que permita definir regras
de combinação dos vários softwares concerns.
3.7.1
Software concerns
Para a implementação de software concerns em módulos são normalmente utilizadas
linguagens orientadas a objectos tais como: Java, C++ e C#.
3.7.2
Regras de combinação
As regras de combinação definem como interligar os software concerns previamente
implementados, de forma a criar o sistema final. Alguns aspectos da combinação de concerns a
levar em conta na especificação da linguagem são:
•
Correspondência da linguagem: modo com o qual se descreve quais entidades
serão compostas entre si. A correspondência pode ser implícita (determinada por
regras da linguagem) ou explícita (descrita pelo programador);
•
Semântica composicional: descreve o que deve acontecer com os elementos que
correspondem. A linguagem pode definir diversas semânticas que em geral são
escolhidas pelo programador;
52
•
Tempo de ligação: diz respeito ao momento em que a correspondência tem início.
Pode ser estático (em tempo de compilação) ou dinâmico (em tempo de
execução).
3.8
COMPOSIÇÃO DE SISTEMAS
A implementação da linguagem permite combinar os software concerns, utilizando as
regras de combinação e converter o resultado da combinação em código executável. O processo
de combinação é chamado weaving e o executante, compilador deste processo, é denominado
Weaver.
3.8.1
Weaving
É o processo de composição do sistema de software concerns com aspectos. As regras de
composição estão definidas nas unidades modulares, aspectos. Esta separação faculta que as
alterações das regras permitam gerar novas formas de combinação e assim novos sistemas de
software sem alteração dos software concerns.
As regras lógicas possibilitam à linguagem a geração de eventos virtuais e responder com
uma acção. Esta acção é associada a um crosscutting concern.
53
3.8.2
Weaver
Weaver é o processo que efectua a combinação dos concerns. Dependendo da
implementação do Weaver este pode permitir a combinação de código fonte, a combinação de
código intermédio [Hilsdale and Hugunin 2004] (bytecode23), ou estar incluído na própria
maquina virtual. Por exemplo, a JVM24 ao executar os programas em código intermédio, permite
despontar eventos próprios para a introdução de comportamentos adicionais.
Na combinação do código fonte (figura 12), o dos módulos é entrosado pelo Weaver com
o dos aspectos, na linguagem de POA. O resultado é posteriormente fornecido ao compilador da
linguagem de programação base. Neste método não é possível combinar bibliotecas que estejam
pré-compiladas, já que não é possível ter acesso ao seu código fonte.
Figura 12 – Weaver de código fonte.
Na combinação de código intermédio (figura 13), todos os concerns são previamente précompilados pelo compilador do paradigma base, para código intermédio e os aspectos são pré-
23
24
http://www-128.ibm.com/developerworks/ibm/library/it-haggar_bytecode/ (Acedido em Julho 2008)
http://dev2dev.bea.com/pub/a/2005/08/jvm_aop_2.html?page=1 (Acedido em Julho 2008)
54
processados pelo compilador de aspectos e combinados com o código intermédio dos software
concerns, para gerar o código executável ou código intermédio final. Neste método já é possível
importar bibliotecas pré-compiladas.
Figura 13 - Weaver de código intermédio.
3.8.2.1 Proxy Dinâmica
O processo de weaving em alguns Weavers é baseado em Proxies dinâmicos25. Um Proxy
dinâmico encapsula um objecto. Invocações em métodos desse objecto são efectuadas no proxy
que posteriormente invoca os métodos no objecto. Isto permite que o proxy permita executar
tarefas (advices), ‘antes’, ‘depois’ ou ao ‘invés de’.
Numa situação normal, a classe do objecto é alheia a qualquer alteração não sendo
executada nesta classe qualquer tipo de alteração (exemplo 9).
25
http://java.sun.com/j2se/1.4.2/docs/api/java/lang/reflect/Proxy.html (Acedido em Julho 2008)
55
public class SimplePojo implements Pojo {
public void foo() {
//invocação directa
this.bar();
}
public void bar() {
//logica
}
}
Exemplo 9- classe a ser interceptada.
Do ponto de vista do objecto a comunicação entre objectos é directa (figura 14).
Figura 14 - Comunicação normal entre objectos.
Normalmente para se invocar um método num objecto instancia-se o objecto e invoca-se
o método directamente (exemplo 10).
public class Main {
public static void main(String[] args) {
Pojo pojo = new SimplePojo();
// invocação directa na referencia do objecto
pojo.foo();
}
}
Exemplo 10 – Invocação de métodos na classe normalmente.
Para adicionar um Proxy é necessário alterar o código que instancia o objecto e adicionar
toda a lógica referente ao proxy. Estas alterações são efectuadas automaticamente pelas
56
ferramentas de compilação POA. No exemplo 11 é possível verificar o tipo de alterações
necessárias para invocar um método num objecto. O código da classe ProxyFactory não é
exemplificado.
public class Main {
public static void main(String[] args) {
ProxyFactory factory = new ProxyFactory(new SimplePojo());
factory.addInterface(Pojo.class);
factory.addAdvice(new RetryAdvice());
Pojo pojo = (Pojo) factory.getProxy();
// chamada ao método mas no proxy!
pojo.foo();
}
}
Exemplo 11 - Invocação de métodos utilizando uma proxy.
Ao ser inserido o padrão de desenho Proxy, o programa primeiro invoca o método no
proxy e este posteriormente invoca o método no objecto (figura 15).
Figura 15 - Comunicação utilizando uma proxy.
Desta forma é possível adicionar comportamentos adicionais (figura 16).
57
Código da Invocação
Pojo.foo()
Proxy
1) Foo() na Proxy
Advices
2) Execução de Advices
Advice
Objecto
3) Foo() no Objecto
Figura 16 – Adição de comportamentos adicionais através de proxys.
Esta solução acrescenta lógica adicional. Assim, a execução do programa vai ter mais
instruções o que vai afectar a performance do programa, para além de que só permite a captura
de fluxo dinâmicos.
3.9
BENEFÍCIOS DA POA
Como benefícios da utilização do Paradigma Orientado a Aspectos pode-se enumerar:
•
Código mais limpo: a separação modular de todos os softwares concerns permite
efectuar a análise e leitura do código escrito módulo a módulo. Assim, ao ser
58
necessário, só ter em consideração uma área de cada vez é mais fácil de se
perceber, editar e estender o código escrito;
•
Modularização superior: para além das formas normais de modularização da
linguagem base é possível modularizar e localizar textualmente, segundo a forma
natural os crosscutting concerns;
•
Fácil de evoluir: a nova forma de modularização em conjunto com a definição de
regras de composição permite criar novos sistemas, com o mínimo ou mesmo
nenhumas alterações dos módulos de software. A adição de módulos ou
redefinição de regras para alterar, editar, ou melhorar um sistema de software é
simplificado e as alterações facilmente localizáveis;
•
Facilidade de adicionar requisitos de última hora: a separação de software
concerns permite a alteração de código fonte, com o mínimo de repercussões ao
nível da estrutura do sistema de software. Modificações, como por exemplo, para
melhorar a performance ou adicionar/alterar regras de negócio, são facilmente
acopladas ao sistema;
•
Mais código reutilizável: a eliminação do efeito de intrusão e espalhamento
permite que os módulos sejam mais facilmente acoplados a outros sistemas de
software;
•
Rápido de implementar: depois da análise e da definição de interfaces é mais fácil
dividir por equipas e implementar os módulos paralelamente, não sendo
necessárias reuniões para alterações de necessidades locais;
59
•
Redução de custos: a possibilidade de desenvolver o sistema mais rapidamente,
distribuir tarefas paralelas e aumentar a reutilização tem como efeito a redução
dos custos.
3.10
DESENVOLVIMENTO DE SOFTWARE ORIENTADO A ASPECTOS
Um paradigma de programação por si só não é suficiente para a elaboração de um
sistema, é preciso possuir ferramentas e tecnologias que facilitem o desenho e posteriormente o
desenvolvimento do sistema. Assim, a POA evoluiu para o Desenvolvimento de Software
Orientado a Aspectos26 (figura17).
Figura 17 - Sistema de Desenvolvimento de Software Orientado a Aspectos.
26
http://www.aosd.net/ (Acedido em Junho 2008)
60
3.10.1 Identificação de concerns
Nesta etapa é feita a identificação e separação de software concerns que permitirá a
separação de tarefas e reduzir a complexidade do desenho e da implementação.
Nesta área em particular surge o termo Early-aspects27 que refere a identificação de
aspectos ao nível da arquitectura do sistema (figura 18). A investigação nesta área prende-se com
o facto de que é necessário fazer uma identificação realista dos interesses do sistema e como os
aspectos vão afectar o sistema, bem como, a sua evolução futura.
Figura 18 – Incisão do estudo de Early-Aspects.
3.10.2 Implementação
A subárea encarregue da implementação dos sistemas é a POA. Para tal, existe uma
ampla panóplia de linguagens de programação que estende as linguagens mais populares como
27
http://www.early-aspects.net/ (Acedido em Junho 2008)
61
Java, C/C++, SmallTalk. Tornando-se a linguagem padrão e uma das mais populares dentro da
comunidade, o AspectJ estende da POO Java.
3.11
DEVENVOLVIMENTO UTILIZANDO ASPECTJ
AspectJ [Kiczales and Hilsdale 2001] é uma extensão à linguagem de programação Java
e implementa conceitos práticos do paradigma orientado a aspectos. Tem suporte para dois tipos
de implementações de crosscutting concerns:
•
Dinâmicos: permitem acrescentar comportamentos adicionais em pontos bem
definidos no programa;
•
Estáticos: permitem alterar a estrutura de classes e/ou interfaces.
A linguagem foi desenvolvida pelo grupo de Gregor Kiczales e actualmente o
desenvolvimento da linguagem está a cargo do projecto Eclipse, que disponibiliza no website da
plataforma, ferramentas de ajuda ao desenvolvimento em POA.
Benefícios da linguagem AspectJ:
•
Generalista;
•
Fácil de aprender;
•
Fácil de associar;
•
Adopção por fases.
62
3.11.1 Análise
A análise à linguagem AspectJ, que descreve os vários tipos de PointCut, tipos de
Advices, inter-type-Declarations e exemplos de utilização, pode verificar-se no anexo 1.
3.11.2 @AspectJ
Na versão 1.5 da linguagem de programação AspectJ foi introduzido um novo estilo de
declarar aspectos, advices e pointcuts, mas mantendo contudo a mesma semântica (anexo 1). A
este estilo de desenvolvimento foi chamado @aspectJ28, sendo semelhante às anotações do Java
5 e idêntico à aplicação de atributos da Framework .NET.
Este estilo permite desenvolver aspectos como se de classes normais de Java se tratassem,
posteriormente anotadas, utilizando a semântica do AspectJ. Os métodos dessas classes quando
anotados tornam-se Advices. Assim, torna-se possível desenvolver programas de POA sem
alterar as ferramentas de desenvolvimento, contudo é necessário incorporar manualmente o
método de Weaving no processo de compilação do projecto, através por exemplo, de tarefas
ANT29. Se não existir integração do processo de compilação o projecto/programa compilado tem
o comportamento normal POO.
28
29
http://www.eclipse.org/aspectj/doc/released/adk15notebook/ataspectj.html (Acedido em Junho de 2008)
http://ant.apache.org/resources.html (Acedido em Junho 2008)
63
Neste âmbito, antes de se poder desenvolver programas POA utilizando este estilo, é
necessário instalar primeiro a linguagem AspectJ30 e importar as bibliotecas, ficheiros jar, ao
projecto Java.
A análise a este estilo de programação POA encontra-se no anexo 2.
3.11.3 Ferramentas de Desenvolvimento
Para facilitar e complementar a utilização das tecnologias POA surgiram e/ou foram
adaptadas ferramentas que auxiliam os programadores. Estas encontram-se enumeradas no anexo
3.
3.12
POSTSHARP
PostSharp é uma tecnologia POA para a Framework .NET da Microsoft. AspectJ é só
utilizável em projectos Java, não existindo soluções para outras populares linguagens e
tecnologias. É ao encontro desta necessidade para a Framework .NET que o PostSharp foi
criado.
Postsharp31 é uma plataforma de código aberto, que permite transformar e analisar
assemblies da tecnologia .NET da Microsoft. Uma assembly é um conjunto de módulos,
30
31
http://www.eclipse.org/aspectj/downloads.php (Acedido em Junho 2008)
http://www.postsharp.org/ (Acedido, Julho 2008)
64
unidades unitárias, e com um cabeçalho que indica os módulos que compõem a assembly. Cada
módulo é armazenado no formato portable executable (PE), código intermédio, MSIL.
Postsharp Laos (Lightweight Aspect-Oriented System), é por sua vez, um weaver que
permite criar atributos para a framework .Net como se de aspectos se tratassem, sendo aplicados
a classes, métodos ou variáveis. Os atributos são metadados que descrevem, ou anotam,
elementos específicos tais como classes, métodos ou propriedades. Os metadados são
adicionados à assembly e podem ser acedidos através da API Reflection da framework .NET
A tecnologia consiste, deste modo, num conjunto de assemblies que se registam na
Framework .NET e um post-compiler para compor o sistema, o qual se integra no processo de
compilação do Visual Studio, MSBuild.
Postsharp Laos é um plug-in (figura 19) de uma plataforma maior: o Postsharp Core. Esta
plataforma é uma infra-estrutura genérica, para a análise e transformação de programas, como
AOP weavers, análise estática e optimizadores, podendo ser utilizada por investigadores e/ou
programadores para se abstraírem dos problemas da infra-estrutura e se focarem na sua área de
investigação.
Figura 19 – Processo de integração do Postsharp.
(Adaptada do ficheiro de ajuda do Postsharp)
65
3.12.1 PostSharp LAOS
O plug-in Postharp LAOS [Fraiteur 2008]
permite de forma simples implementar
funcionalidades práticas do Paradigma Orientado a Aspectos num projecto. NET Framework.
A nomenclatura utilizada no Postsharp Laos foi adaptada para ser facilmente interpretada
pela comunidade da Framework. NET, facilitando a sua utilização aos programadores menos
familiarizados com os conceitos da POA. Na tabela 4 apresenta-se uma comparação entre os
termos técnicos utilizados na linguagem de programação AspectJ e os utilizados no PostSharp
Laos.
AspectJ
Join Point
Pointcut
Advice
Aspecto
PostSharp Laos
Meta-evento
Atribuição de um atributo a um tipo (classe, método ou
propriedade).
Meta-Handler
Conjunto coerente de advices aplicados a um evento de um
elemento alvo predefinido, geralmente encapsulados num atributo.
Tabela 2 - comparação da nomenclatura do AspectJ e Postsharp Laos.
Neste contexto, o PostSharp Laos disponibiliza classes que são consideradas aspectos e
os métodos destas podem ser vistos como advices. O programador estende as classes e redefine
os métodos de acordo com o comportamento pretendido. Como estas classes derivam da classe
System.Atribute todas as classes que derivem destas são atributos, e ao serem aplicadas a um
tipo, classe, método ou propriedade, definem um Pointcut. Do ponto de vista do programador do
módulo base um aspecto não é mais que um atributo .NET.
66
É disponibilizado pela Plug-in um Namespace32, Postsharp.Laos, que contem um
conjunto de classes que permitem implementar os atributos de acordo com o tipo de
comportamento pretendido. Sendo assim, é possível capturar chamadas a métodos, declarar
excepções, capturar acesso a variáveis e implementar métodos.
Os atributos são aplicados a tipos. Um tipo pode ser um método, uma classe ou uma
estrutura. Se um atributo for aplicado a uma classe e se este é do tipo advice para capturar
chamadas a métodos, então fica aplicado a todos os métodos da classe caso não seja definido
outro tipo de comportamento nas definições de atribuição do atributo.
3.12.1.1
Características do PostSharp Laos
•
Aspectos reutilizáveis: os atributos são classes e como tal são reutilizáveis entre
projectos;
•
Aspectos não invasivos: não se quebra o encapsulamento, os aspectos são
aplicados sem alterar o bloco de código a que estão aplicados. O PostSharp Laos
envolve o bloco de código como uma caixa dentro de outra caixa;
•
Aspectos parametrizáveis: podem ser adicionados parâmetros que são
correctamente instanciados na fase de inicialização e posteriormente serializados
para a assembly.
32
http://msdn.microsoft.com/en-us/library/ms973231.aspx (Acedido em Julho 2008)
67
3.12.1.2
Limitações do PostSharp Laos
•
Performance: o PostSharp Laos sofre das mesmas limitações dos weavers com
base em proxys dinâmicos (capitulo 3.8.2.1), influenciando negativamente na
performance dos programas;
•
Portabilidade: correntemente o PostSharp só é suportado para a plataforma. Net
não o sendo nas plataformas. NET Compact Framework e Mono;
•
Expressividade limitada: embora existam várias maneiras de expressar pointcuts,
estas comparadas com outras ferramentas POA, mostram que PostSharp Laos é
bastante limitado;
•
Conjunto de advices limitados: PostSharp Laos embora possibilite um conjunto
prático de advices, não oferece alguns tipos úteis, como por exemplo a introdução
de variáveis.
3.12.2 Análise da Tecnologia
No anexo 4 é descrita a forma de utilização das classes que o PostSharp Laos disponibiliza e um
pequeno exemplo de utilização.
3.12.2.1
Ciclo de vida de um Aspecto Postsharp LAOS
Desde a compilação até à execução, os aspectos desenvolvidos em PostSharp LAOS
passam por várias fases:
1. Instanciação: nesta fase o weaver do PostSharp Laos instancia cada aspecto que
está aplicado a um tipo;
68
2. Validação: verifica que a atribuição dos aspectos é feita correctamente, por
exemplo, não permitir aplicar aspectos de chamada de métodos a variáveis. Esta
validação é feita, em primeira instância, pelo weaver e numa segunda fase através
da invocação de um método, que pode ser implementado em cada classe
disponibilizada pelos PostSharp Laos, denominado de CompileTimeValidate,
permitindo ao programador estabelecer regras (exemplo 12);
public override bool CompileTimeValidate(object element)
{
//implementação das validações
}
Exemplo 12 - Método CompileTimeValidate.
3. Inicialização: consiste na invocação do método CompileTimeInitialize presente
em cada classe disponibilizada pelo PostSharp Laos (exemplo 13) e que permite
efectuar alguns tipos de cálculos com os dados compilados até esta fase, para
assim não os efectuar em tempo de execução;
public override void CompileTimeInitialize(object element)
{
//implementação
//atribuir variaveis com resultados pre calculados em compile time
}
Exemplo 13 – Método CompileTimeInitialize.
4. Serialização: todas as instâncias dos aspectos são serializadas como metadados na
assembly;
69
5. Uso em tempo de execução: os atributos são lidos, serializados e utilizados no
programa;
3.12.3 PostSharp Core
PostSharp Core é a base em que os plugins como PostSharp Laos são desenvolvidos,
trabalhando ao mais baixo nível. O PostSharp Core compõe-se por:
• Um modelo de objectos em bytecode, que permite ao PostSharp ler e escrever
assemblies .NET framework;
•
Infra-estrutura da plataforma, onde todas as tarefas a desempenhar pelos plugins
ficam definidas em ficheiros XML;
•
Weaver de baixo nível, que consiste num weaver que injecta instruções em
assemblies e é a base para outros weavers de alto nível, como o do PostSharp
Laos.
3.12.4 AspectJ vs. Postsharp LAOS
Enquanto o AspectJ é uma extensão da linguagem de programação, por sua vez, o
PostSharp é um componente que se adiciona à Framework .NET. Para tal é necessário ter uma
compreensão extensiva da tecnologia para desenvolver plugins. Utilizar esses mesmos plugins é
uma tarefa simples para programadores habituais da plataforma. De seguida são apresentadas as
principais diferenças entre o AspectJ e o PostSharp Laos:
70
•
O AspectJ não possui restrições de licenças enquanto o PostSharp usufrui de
vários tipos de licenças, dependendo porém da biblioteca em questão33;
•
O AspectJ é uma extensão à linguagem de programação java. O PostSharp Laos
adiciona à framework .NET namespaces e um compilador adicional;
•
O AspectJ permite operadores lógicos em pointcuts (‘&&’, ‘||’, ‘!’). O PostSharp
Laos permite a composição de aspectos de forma mais limitada;
•
O AspectJ tem definições próprias para a implementação de aspectos. No
PostSharp Laos a definição de aspectos é através de atributos para a plataforma
.Net;
•
Em AspectJ os advices podem ser executados antes, depois e invés de. PostSharp
Laos só permite este tipo de comportamento em certos aspectos;
•
3.12.4.1
AspectJ e PostSharp integram-se transparentemente nas respectivas tecnologias.
Definição de Join Points
O PostSharp permite criar atributos personalizados, sendo o local onde estes são
aplicados definidos pelo programador, mas este está restringido às definições permitidas pelo
plugin. Caso se esteja a utilizar a biblioteca Core, é possível definir qualquer tipo de Join Points.
No AspectJ existe vários tipos de Join Points.
Nas tabelas 7, 8 e 9 pode-se observar quais são as soluções apresentadas pelas duas
tecnologias para os vários tipos de comportamentos.
33
http://www.postsharp.org/about/license/ (Acedido em Setembro de 2008)
71
PointCut
AOP
Invocação
Inicialização
Acesso
AspectJ
call/execution
get /
set
Postsharp
Laos
OnMethodBoundar
yAspect/
OnMethodInvocati
onAspect
Staticinitialization/
initialization
N/A
OnFieldAcces
sAspect
Tratamento de
Excepções
handler
OnExceptionAs
pect
Tabela 3 – Comparação de pointcuts entre PostSharp Laos e AspectJ
PointCut (continuação)
AOP
AspectJ
Controlo de fluxo
Cflow/
cflowbelow
Containment
Within/
withincode
Condições
if
Postsharp
Laos
N/A
N/A
N/A
contexto
this/
target/
args
Parametros
Tabela 4 - Comparação de pointcuts entre PostSharp Laos e AspectJ
Advices
AOP
AspectJ
Postsharp Laos
Advice
Antes, Depois, ao invés de
Antes, Depois, ao invés de (depende do tipo de pointcut)
Tabela 5 – comparação de tipos
3.12.4.2
Definição de Aspectos
Em AspectJ, os aspectos são desenvolvidos através de código Java encapsulando tanto o
código normal do advice como a definição de pointcuts (exemplo 14).
72
public class HelloWorld {
public void say ( String msg ) {
System.out.println ( msg );
}
public static void main ( String[ ] args ) {
new HelloWorld ( ).say ( "Hello, world! " );
}
}
public aspect HelloWorldAspect
{
pointcut sayPoint ( ):
execution (void HelloWorld.say ( String ));
after( ): sayPoint( ) {
System.out.println( "Over!" );
}
}
Exemplo 14 – exemplos de um aspecto em AspectJ.
Por sua vez, em PostSharp Laos, os aspectos são desenvolvidos como atributos .NET
(exemplo 15), ficando isolados nestes, tendo a sua atribuição que ser declarada fora do atributo e
no tipo ao qual se pretende capturar o comportamento (exemplo 16).
using PostSharp.Laos;
…
namespace TraceSample
{
[Serializable]
class TraceAttribute: OnMethodBoundaryAspect
{
public override void OnExit(MethodExecutionEventArgs eventArgs)
{
Console.WriteLine("On exit of {0}.", this.methodName);
}
}
}
Exemplo 15 – Exemplo de um atributo com base em classes do PostSharp Laos.
73
class Program
{
static void Main(string[] args)
{
sayHello("World");
Console.ReadKey();
}
[Trace]
static void sayHello(string name)
{
Console.WriteLine("Hello, {0}.", name);
}
}
Exemplo 16 – Exemplo de aplicação de um atributo e um método.
3.12.5 Conclusões
Na conclusão da comparação entre o AspectJ e o Postsharp LAOS verifica-se que no
PostSharp LAOS todo o entrelaçamento é feito em tempo de compilação. As limitações
apresentadas pelo PostSharp LAOS ao nível de join points, point cuts e advices, podem ser
combatidas utilizando o PostSharp Core. Esta ultima solução é complexa e requer vastos
conhecimentos técnicos e aprofundados da tecnologia PostSharp e da Framework .Net.
Existem alguns projectos que correntemente utilizam a tecnologia PostSharp com
sucesso. O PostSharp4EF34 é um exemplo de que as limitações que o Postsharp Laos apresenta
podem ser combatidas utilizando o PostSharp Core desenvolvendo plugins próprios.
Por sua vez, o AspectJ possui um vasto leque de join points e mais tempo de existência
consistindo nas razões pelas quais lhe é conferida uma maior solidez. Contudo possui uma
34
http://www.codeplex.com/efcontrib (Acedido em Setembro de 2008)
74
linguagem própria para a definição de pointcuts e advices que adiciona alguma complexidade,
embora ao mesmo tempo torne a tecnologia mais flexível. O PostSharp é relativamente novo mas
não possui linguagem própria, sendo mais simples de usar, embora mais limitado.
3.13
MODULARIZAÇÃO DE PADRÕES DE DESENHO
Padrões de desenho Gang-of-Four [Gamma, Helm et al. 1995] (GOF) oferecem soluções
flexíveis para problemas comuns no desenvolvimento de software orientado a objectos. Cada
padrão é composto por um conjunto de partes que incluem: a intenção, a aplicabilidade, a
estrutura da solução e um exemplo de implementação.
Na figura 20, pode-se observar a implementação do padrão de desenho Observer. Este
padrão de desenho permite que uma ou mais classes se registem para receber notificações e que
outras classes enviem notificações a essas classes. Para permitir este comportamento é necessário
adicionar métodos às classes que participam no padrão, ou seja, as classes que se registam e as
classes que são notificadas.
75
Figura 20 – Padrão de Desenho Observer implementado em POO
A maioria dos padrões de desenho GOF envolve estruturas transversais na relação do
padrão de desenho com as classes que participam nele. A implementação dos padrões de desenho
tem efeitos no código do sistema, pois estes influenciam a sua estrutura. A sua implementação
mistura-se com o código das classes, o que implica a perda de modularidade, dificultando deste
modo, a tarefa de distinguir o sistema do padrão de desenho. Adicionar um padrão de desenho é
uma tarefa invasiva e difícil de reverter. Enquanto que o padrão de desenho é reutilizável, a sua
implementação num sistema não o é.
Hannemann e Kiczales [Hannemann and Kiczales 2002] elaboraram um estudo, que
compara a implementação de 23 padrões de desenho, desenvolvidos em POA na linguagem de
programação AspectJ e em POO utilizando a linguagem de programação Java. As principais
76
ilações alcançadas pelos autores evidenciaram que 74 % dos padrões de desenho implementados
utilizando POA, eram mais modulares e que 52% dos padrões se tornaram reutilizáveis noutros
projectos.
Sant’Anna [Garcia, Sant'Anna et al. 2005] complementando o estudo anterior adicionou
dados quantificados, utilizando métricas que permitem medir a separação de concerns, coesão e
tamanho do código. As principais conclusões apontaram que nestes padrões de desenho, em
particular a utilização de POA, ajuda a separação de concern, embora em alguns casos o
acréscimo de operações aumente a complexidade interna do sistema e existam mais linhas de
código por padrão de desenho. Na figura 21 é possível observar o desenho do sistema para a
implementação do padrão de desenho Observer recorrendo a POA.
Figura 21 – Padrão de desenho Observer implementado em POA
3.13.1 Exemplo Observer Pattern.
No Anexo 6 é apresentada uma possível implementação para o sistema desenhado na figura 21.
77
3.13.2 Análise dos Estudos
Hannemann e Kiczales [Hannemann and Kiczales 2002] analisaram 23 padrões de
desenho sendo os resultados apresentados na tabela 9.
Propriedades Modulares
Padrão de Desenho
Localizável
Reutilizável
Composição Transparente
Removível
Implementações idênticas
Façade
Abstract Factory
Não
não
Não
não
Bridge
não
não
Não
não
Builder
não
não
Não
não
Factory Method
não
não
Não
não
Interpreter
não
não
n/a
não
Template Method
(sim)
não
Não
(sim)
Adapter
sim
não
Sim
sim
State
(sim)
não
n/a
(sim)
Decorator
sim
não
Sim
sim
Proxy
(sim)
não
(sim)
(sim)
Visitor
(sim)
sim
Sim
(sim)
Command
(sim)
sim
Sim
sim
Composite
sim
sim
Sim
(sim)
Iterator
sim
sim
Sim
sim
Flyweight
sim
sim
Sim
sim
Memento
sim
sim
Sim
sim
Strategy
sim
sim
Sim
sim
Mediator
sim
sim
Sim
sim
Chain of Responsibility
sim
sim
Sim
sim
Prototype
sim
sim
(sim)
sim
Singleton
sim
sim
n/a
sim
Observer
sim
sim
Sim
sim
Tabela 6 – Resumo das comparações dos padrões de desenho.
A maioria dos padrões de desenho tornam-se localizáveis e removíveis sendo a sua
composição transparente no sistema, implementando assim, vantagens na utilização da POA.
Embora aparentemente existam vantagens para o programador, é necessário quantificá-las
utilizando métricas que permitam verificar que existem melhorias ao nível da coesão, ligação e
78
tamanho do código desenvolvido. No estudo levado a cabo por Sant’Anna [Garcia, Sant'Anna et
al. 2005], utilizando métricas bem conhecidas, não foram obtidas conclusões gerais. Para estes
padrões de desenho em concreto e efectuando a comparação entre as duas linguagens AspectJ e
Java, o uso de aspectos ajudou a melhorar a ligação e coesão de alguns padrões de desenho,
enquanto outros padrões aumentam a complexidade e desenho do sistema.
3.14
MODULARIZAÇÃO DE EXCEPÇÕES
Os mecanismos de excepção permitem separar, de forma modular, o código normal do
código para tratar situações excepcionais. Isto é, quando existe um acontecimento que não é
planeado, é necessário criar código que o permita tratar e se possível continuar o programa de
forma normal. Em POO este comportamento é alcançado através da utilização de excepções.
Algumas considerações em relação a POA para a modularização do tratamento de
excepções foram elaboradas por Filho [Castor Filho, Cacho et al. 2006]:
•
Que atributos a POA promove e melhora para além da separação de concerns, por
exemplo: melhora a ligação entre módulos, a coesão, o tamanho e o número de
linhas de código, dos módulos?
•
O tratamento de excepções em aspectos é reutilizável em sistemas de software de
produção?
•
Quando é benéfico utilizar aspectos para o tratamento de excepções e quando não
o é?
79
•
Como é que os aspectos de tratamento de excepções afectam outras
implementações de aspectos de outros concerns.
A modularização de excepções utilizando POA consiste assim, em mover os blocos de
código referente ao tratamento das excepções para aspectos, mantendo o código normal no
método. No exemplo 17 é mostrada a estrutura típica de um método que trata uma excepção.
public class C {
void m() {
try {
//código normal
}catch (Exception E) {
//tratamento da Excepção
}
}
}
Exemplo 17 – Código típico num método para o tratamento de uma excepção.
Por sua vez, a modularização para aspectos consiste em primeiro manter no método o
código normal e remover o tratamento da excepção (exemplo 18).
public class C {
void m() {
//código normal
}
}
Exemplo 18 – Código POO sem tratamento de excepções.
Deste modo, move-se todo o código referente ao tratamento para um aspecto, definindo
um pointcut para interceptar esse método, um advice que permita executar (proceed) o código
normal do referido método e em caso de ocorrer um erro tratar o mesmo dentro do advice
(exemplo 19).
80
public aspect A {
pointcut pcd() : execution(void C.m());
void around() : pcd() {
try { proceed(); }
catch(Exception e) {
//tratamento da excepção
}
}
declare soft : Exception : pcd();
}
Exemplo 19 – Tratamento de Excepções.
No método mantém-se o código normal e nos aspectos o código que trata das possíveis
excepções, utiliza-se um advice para cada bloco try, englobando se possível no mesmo advice os
vários blocos catch referentes a esse bloco try, utilizando-se o tipo de advice invés.
No advice e no bloco try o operador proceed permite a execução do join point original.
No caso de ocorrer uma excepção, os blocos catch definidos no advice tratam a excepção. Para
evitar os erros em tempo de compilação, uma vez que o Java exige o tratamento obrigatório da
maioria das excepções, utiliza-se o enfraquecimento de excepções (Anexo 1) para ultrapassar
essa limitação.
Castor Filho [Castor Filho, Garcia et al. 2005; Castor Filho, Cacho et al. 2006] testou a
viabilidade da utilização da POA para modularizar o tratamento das excepções, quantificando os
resultados utilizando métricas bem definidas, nomeadamente ligação, coesão e tamanho dos
módulos.
A análise dos testes efectuados no(s) artigo(s) sugere a utilização da POA para a
modularização de excepções. Contudo, embora melhore a separação de concerns, tem
desvantagens. Se o código para o tratamento da excepção não é uniforme e é extremamente
dependente do contexto, ou muito complexo, a modularização recorrendo a POA pode ser mais
prejudicial do que benéfica. Para um uso efectivo da POA é necessário um planeamento a priori
81
que tem de ser incorporado no processo de planeamento e desenvolvimento de software, para
assim conseguir prever o impacto da modularização.
3.15
FRAMEWORKS
Para além de linguagens de POA, existem frameworks que implementam o paradigma da
POA de modo a facilitar a adição modular de funcionalidades. No anexo 5 referenciam-se duas
Frameworks que utilizam a POA para oferecer novas formas de modularidade.
3.16
MÉTRICAS
As métricas de software são o método mais efectivo para fornecer evidências empíricas,
pois podem contribuir para o melhor entendimento das diferentes dimensões da complexidade do
software [Briand, El Emam et al. 1995].
A separação de concerns de forma modular é vantajosa na maneira de pensar e
implementar um software complexo, mas tem de existir resultados quantificados que
demonstrem quais são as boas práticas de desenho e de implementação na utilização do
Paradigma Orientado a Aspectos.
Sant’Anna [Sant'Anna, Garcia et al. 2003] utiliza um conjunto de métricas para analisar o
desempenho da POA em relação a POO. Essas métricas, segundo ele, devem satisfazer os
seguintes requisitos:
82
•
Quantificar atributos de software bem conhecidos, tais como: a separação de
concerns, ligação, coesão e tamanho;
•
Depender o mais possível das métricas tradicionais e de extensões das métricas de
POO para POA;
•
Capturar as diferentes dimensões da ligação e coesão do software orientado a
aspectos;
•
Suportar a identificação dos benefícios e desvantagens da utilização de aspectos
num projecto de software, quando comparado com uma solução orientada a
objectos para o mesmo problema.
É proposto pelo autor um conjunto de métricas e um modelo (figura 35), que é composto
por três diferentes elementos, e aos quais estão interligadas as métricas definidas.
Os atributos do modelo são:
•
Qualidades: são os atributos que primariamente se desejam num sistema software;
•
Factores: são as qualidades de atributos secundários que influenciam os primeiros
atributos definidos;
•
Atributos Internos: estão ligados a princípios bem definidos da engenharia de
software, sendo essenciais para alcançar a qualidade dos factores.
83
Figura 22 - Modelo de Qualidade.
Neste âmbito, é elaborado pelos autores um conjunto de seis cenários evidenciando
comparações entre POO e POA utilizando as várias métricas (tabela 10).
Resultados dos Cenários
Evolução
Reutilização
Entidades
Alteradas
Operações
alteradas
Entidades
Adicionada
s
Operações
Adicionada
s
Relações
alteradas
Relações
Adicionada
s
LOCs
adicionados
LOCs
alterados
Entidades
copiadsa
LOCs
copiados
O
AO
O
AO
O
AO
O
AO
O
AO
O
AO
O
AO
O
AO
O
AO
O
AO
S1
1
1
3
3
5
5
2
3
0
0
15
15
101
98
1
1
-
-
-
-
S2
0
0
2
2
4
4
0
0
0
0
10
10
84
86
0
0
-
-
-
-
S3
0
0
2
2
4
4
0
0
0
0
10
10
84
86
0
0
0
0
0
0
S4
0
0
2
3
8
8
0
0
0
0
29
25
188
67
0
8
0
0
0
0
S5
1
1
2
1
0
0
1
1
0
0
4
2
16
14
0
0
0
0
6
6
S6
0
0
0
0
0
0
0
0
0
0
0
0
15
15
0
0
-
-
-
-
S7
5
1
0
0
0
0
0
0
5
2
1
1
0
0
5
1
0
0
40
0
Tabela 7 – Evolução da utilização de POO e POA, segundo as métricas de Sant’Anna.
Noutro artigo, Ceccato [Ceccato and Tonella 2004] evolui as métricas usadas em POO e
introduz novas, sendo que estas ajudam, na opinião do autor, a avaliar o efeito da POA:
84
•
WOM (Weighted Operations in Module): Número de operações num determinado
módulo;
•
DIT (Depth of Inheritance Tree): Comprimento do caminho mais longo para um
determinado módulo até a raiz da classe/aspecto da hierarquia;
•
NOC (Number Of Children): Número de subclasses ou sub-aspectos que um
determinado módulo possui;
•
CAE (Coupling on Advice Execution): Número de aspectos que contêm advices
que sejam executados no disputar da execução de uma determinada operação num
dado módulo;
•
CIM (Coupling on Intercepted Modules): Número de módulos ou interfaces
explicitamente definidas num pointcut de um dado aspecto;
•
CMC (Coupling on Method Call): Número de módulos ou interfaces que
declaram métodos que são possíveis de ser invocados por um dado módulo;
•
CFA (Coupling on Field Access): Número de módulos ou interfaces que declaram
atributos que são acedidos por um dado módulo;
•
RFM (Response For a Module): Métodos e advices potencialmente executados
em reposta a uma mensagem recebida por um dado módulo;
•
LCO (Lack of Cohesion in Operations): Par de operações que trabalham em
atributos de classes diferentes menos pares de operações que trabalham em
atributos comuns (zero se negativo);
•
CDA (Crosscutting Degree of an Aspect): número de módulos afectados por um
pointcut e pela introdução de um dado aspecto.
85
Para testar as métricas desenvolveram uma ferramenta, usando o exemplo de padrões de
desenho observer (anexo 6) [Hannemann and Kiczales 2002]. Os resultados (tabela 8 e 9)
comparam a implementação AspectJ com a implementação em Java.
Versão
Java
AspectJ
WOM
DIT
3
1
NOC
1
2
CAE
0
0
CIM
0
0
0
2
Tabela 8 - Comparação de métricas entre Java e AspectJ primeira parte.
Versão
Java
AspectJ
CMC
CFA
2
1
RFM
0
0
LCO
7
2
CDA
1-12
0
0
3
Tabela 9 - Comparação de métricas entre Java e AspectJ segunda parte.
Nos resultados é possível visualizar uma melhoria dos valores no global devido ao uso de
um aspecto abstracto, mas o DIT aumentou, já que existem aspectos que herdam de outros
aspectos.
86
4. CONSIDERAÇÕES FINAIS
Nesta dissertação procurou-se abordar de forma prática a linguagem de programação
AspectJ, a tecnologia Postsharp e as suas aplicações recorrendo a estudos científicos que
permitem validar a aplicabilidade das tecnologias POA.
Assim, as principais contribuições deste trabalho consistem na síntese da história das
tecnologias que envolvem separação de concerns, descrição e análise dos estilos da linguagem de
programação AspectJ e Postsharp. Este trabalho é ainda enriquecido com a apresentação de
pesquisas no campo da modularização de padrões de desenho, modularização de excepções e
métricas de comparação. A abordagem seguida foi da apresentação de exemplos práticos e
alusões a aplicações reais de situações onde estes se podem aplicar.
A tecnologia POA permite adicionar mecanismos para melhorar a implementação de
crosscutting concerns através de vários tipos de aproximações, código fonte, código intermédio e
máquinas virtuais. Estas tecnologias visam alcançar a separação de concerns permitindo
implementar vários tipos de crosscutting concerns modularmente. As tecnologias foram
evoluindo e presentemente existem ferramentas que já possuem integração nos mais variados
tipos de ambientes de desenvolvimento, instalando-se como extensões naturais às linguagens
existentes nesses ambientes. Estas ainda precisam de algum tempo para colmatar algumas falhas
e apresentar ferramentas que permitam solucionar alguns problemas e melhorar a experiência do
87
programador, de maneira a permitir a depuração, garantir a exacta aplicação dos aspectos, bem
como, manter a aplicabilidade dos aspectos quando existe a alteração ou evolução do código
base, por exemplo através da utilização de refactoring. Para tal, são necessários estudos que
sejam conclusivos e demonstrados com métricas quantitativas que permitam definir exactamente
que tipo de crosscuting concerns a POA, tecnologicamente falando, melhora. Isto é, tem de
existir a garantia que a nova forma de modularização não acarreta prejuízos tecnológicos.
Podem-se enumerar alguns problemas que a POA acarreta, devido à falta de ferramentas
ou à sua complexidade, tais como:
•
Complexidade: como todas as novas tecnologias existe uma curva de
aprendizagem aos novos conceitos. Os vários estilos de programação que existem
em POA uns mais complexos que outros, com o tempo e o amadurecimento das
linguagens de programação diminuirão a complexidade;
•
Que tipos de comportamentos crosscutting são possíveis desenvolver, para além
de transacções e registos;
•
Padrões de desenho ou de frameworks evitam o uso de POA: estas técnicas e
tecnologias não são uma solução genérica como a POA;
•
Depuração: a depuração de aspectos é problemática, e em qualquer tecnologia a
depuração é uma tarefa difícil sem as ferramentas adequadas, para tal em POA
devem-se desenvolver ferramentas que permitam uma depuração mais
simplificada;
•
A evolução dos sistemas pode ser afectada pelos aspectos já desenvolvidos: a
forma de desenvolver pointcuts pode limitar a evolução das classes ou criar um
sistema onde os aspectos vão ter um comportamento incerto, já que as regras de
88
captura podem não se aplicar. As ferramentas de desenvolvimento devem ajudar a
prevenir estas situações suportando avisos ou refactoring para adaptar os
aspectos;
•
Segurança, como prevenir que sistemas possam ser estendidos através de POA?
Como benefícios da utilização do Paradigma Orientado as Aspectos podem-se enumerar:
•
Código mais limpo: ao ser necessário só ter em consideração uma área de cada
vez é mais fácil de perceber e editar o código escrito;
•
Modularização superior: todos os concerns dos sistemas são separados de acordo
com a sua forma natural;
•
Fácil de evoluir: modificações para melhorar a performance de um sistema ou
regras de negócio são facilmente acopladas ao sistema;
•
Mais código reutilizável: a eliminação do efeito de intrusão e espalhamento
permite que os módulos sejam facilmente acoplados a outros sistemas de
software;
•
Rápido de implementar: fácil divisão de tarefas e desenvolvimento paralelo em
separado;
•
Redução de custos: a possibilidade de desenvolver mais rapidamente e o aumento
da reutilização tem como efeito a redução de custos;
Existe um grande esforço e dedicação da comunidade académica na investigação nas
várias áreas da DSOA, que trabalham para colmatar a falta de ferramentas e modelos para as
várias etapas do desenvolvimento de software. Este esforço começa a ser recompensado com a
89
crescente implementação de tecnologias como o Postsharp e inclusão de Weavers em
frameworks populares demonstrando que os problemas da tecnologia estão a ser corrigidos e que
os conceitos mais práticos são viáveis, acarretando benefícios ao nível comercial nomeadamente
na redução de tempo de implementação e custos, o que faz prever que a tecnologia continue a
evoluir e amadurecer nas suas diversas áreas.
O presente trabalho abre caminho como base de introdução à tecnologia POA na tentativa
de se conhecer o problema que se tenta resolver, o modelo e as ferramentas que existem para o
resolver, podendo ainda ser enriquecido com a adição de novos estudos quantitativos,
tecnologias POA emergentes, outras formas de desenvolvimento e exemplo de aplicações reais
da tecnologia.
90
5. BIBLIOGRAFIA
Aksit, M., K. Wakita, et al. (1993). Abstracting Object-Interactions Using Composition-Filters.
Object-Based Distributed Processing. R. Guerraoui, O. Nierstrasz and M. Riveill,
Springer-Verlag Lecture Notes in Computer Science: 152-184.
Backus, J. W., R. J. Beeber, et al. (1957). "The FORTRAN automatic coding system." Western
Joint Computer Conference: 188-198.
Berard, E. V. (2002). "Abstraction, Encapsulation, and Information Hiding."
Bergmans, L. and M. Aksit (2001). "Composing Crosscutting Concerns Using Composition
Filters." Comm. ACM 44(10): 51--57.
Booch, G. (1994). Object-oriented analysis and design with applications, Benjamin/Cummings
Pub. Co Redwood City, Calif.
Booch, G. (2001). "Through the Looking Glass." Software Development, July.
Briand, L., K. El Emam, et al. (1995). "Theoretical and Empirical Validation of Software
Product Measures." International Software Engineering Research Network, Technical
Report ISERN-95-03.
Brown, A. W. and K. C. Wallnau (1998). "The current state of CBSE." Software, IEEE 15(5):
37-46.
Castor Filho, F., N. Cacho, et al. (2006). "Exceptions and aspects: the devil is in the details."
Proceedings of the 13th ACM SIGSOFT 14th international symposium on Foundations
of software engineering: 152-162.
Castor Filho, F., A. Garcia, et al. (2005). "A quantitative study on the aspectization of exception
handling." Proceedings of the ECOOP’2005 Workshop on Exception Handling in ObjectOriented Systems.
91
Ceccato, M. and P. Tonella (2004). "Measuring the Effects of Software Aspectization." Proc. of
the 1st Workshop on Aspect Reverse Engineering (CD-ROM), The Netherlands.
Clement, A., A. Colyer, et al. (2003). "Aspect-Oriented Programming with AJDT." ECOOP
Workshop on Analysis of Aspect-Oriented Software.
Czarnecki, K., U. W. Eisenecker, et al. Beyond Objects: Generative Programming.
Dijkstra, E. W. (1968). " Go To Statement Considered Harmful." Communications of the ACM
11: 147-148.
Dijkstra, E. W. (1976). A discipline of programming. Englewood Cliffs, New Jersey, PrenticeHall.
Elrad, T., M. Aksit, et al. (2001). "Discussing Aspects of AOP." Comm. ACM 44(10): 33--38.
Elrad, T., R. E. Filman, et al. (2001). "Aspect-Oriented Programming." Comm. ACM 44(10): 29-32.
Filman, R. E. and D. P. Friedman (2000). "Aspect-Oriented Programming is Quantification and
Obliviousness." Workshop on Advanced Separation of Concerns 2000.
Floyd, R. W. (1979). "The paradigms of programming." Communications of the ACM 22(8):
455-460.
Fraiteur, G. (2008). "User-friendly aspects with compile-time imperative semantics in .NET: an
overview of PostSharp." 9.
Gamma, E., R. Helm, et al. (1995). Design patterns: elements of reusable object-oriented
software.
Garcia, A., C. Sant'Anna, et al. (2005). "Modularizing design patterns with aspects: a
quantitative study." Aspect-oriented software development: Proceedings of the 4 th
international conference on Aspect-oriented software development 14(18): 3-14.
Hannemann, J. and G. Kiczales (2002). Design pattern implementation in Java and AspectJ.
Proceedings of the 17th ACM conference on Object-oriented programming, systems,
languages, and applications, ACM Press.
Harrison, W. and H. Ossher (1993). Subject-Oriented Programming---A Critique of Pure
Objects. Proc. 1993 Conf. Object-Oriented Programming Systems, Languages, and
Applications.
92
Harrison, W., H. Ossher, et al. (2005). "Concern modeling in the concern manipulation
environment." Proceedings of the 2005 workshop on Modeling and analysis of concerns
in software: 1-5.
Harrison, W., H. Ossher, et al. (2005). "Supporting aspect-oriented software development with
the Concern Manipulation Environment." IBM Systems Journal 44(2): 309-318.
Hayes, B. (2003). "THE POST-OOP PARADIGM." American Scientist 91(2).
Hilsdale, E. and J. Hugunin (2004). "Advice weaving in AspectJ." Proceedings of the 3rd
international conference on Aspect-oriented software development: 26-35.
Hürsch, W. L. and C. V. Lopes (1995). Separation of Concerns. Boston, MA, College of
Computer Science, Northeastern University.
Kaplan, M., H. Ossher, et al. (1996). Subject-Oriented Design and the Watson Subject Compiler.
Proc. OOPSLA'96 Workshop on Subjectivity.
Kernighan, B. W. and D. M. Ritchie (1988). The C programming language, Prentice-Hall.
Kiczales, G. (1996). "Beyond the black box: Open implementation." {IEEE} Software 13(1): 8-11.
Kiczales, G., J. des Rivieres, et al. (1991). The Art of the Metaobject Protocol. Cambridge,
Massachusetts, MIT Press.
Kiczales, G. and E. Hilsdale (2001). Aspect-oriented programming. Proceedings of the 8th
European Software Engineering Conference held jointly with 9th Acm Sigsoft
Symposium on Foundations of Software Engineering, ACM Press.
Kiczales, G., E. Hilsdale, et al. (2001). "Getting Started with AspectJ." Comm. ACM 44(10): 59-65.
Kiczales, G., E. Hilsdale, et al. (2001). An overview of AspectJ. Proc. ECOOP 2001, LNCS
2072, Berlin, Springer-Verlag.
Kiczales, G., J. Lamping, et al. (1997). Aspect-Oriented Programming. 11th Europeen Conf.
Object-Oriented Programming, Springer Verlag.
Kiczales, G. and M. Mezini (2005). Aspect-oriented programming and modular reasoning. ICSE
'05: Proceedings of the 27th international conference on Software engineering, New
York, ACM Press.
Kuhn, T. S. (1970). The Structure of Scientific Revolutions, University of Chicago Press.
93
Laddad, R. (2003). "AspectJ in Action." 512.
Lieberherr, K., D. Orleans, et al. (2001). "Aspect-Oriented Programming with Adaptive
Methods." Comm. ACM 44(10): 39--41.
Lieberherr, K. J. (1996). Adaptive Object-Oriented Software: the Demeter Method with
Propagation Patterns, PWS Publishing Company, Boston.
Lindholm, T. and F. Yellin (1999). Java Virtual Machine Specification, Addison-Wesley
Longman Publishing Co., Inc. Boston, MA, USA.
Liskov, B. H. and J. M. Wing (2001). "Behavioural subtyping using invariants and constraints."
Lopes, C. V. (1996). Adaptive parameter passing. 2nd Int'l Symposium on Object Technologies
for Advanced Software, Springer-Verlag.
Lopes, C. V. (1997). D: A Language Framework for Distributed Programming, College of
Computer Science, Northeastern University.
Lopes, C. V. (2002). AOP: A Historical Perspective (What's in a Name?): 97-122.
Löwy, J. (2003). Programming. NET Components, O'Reilly.
Maes, P. (1987). "Concepts and experiments in computational reflection."
Mehmet Aksit, K. W., Jan Bosch, Lodewijk Bergmans, Akinori Yonezawa (1994). "Abstracting
Object Interactions Using Composition Filters."
Mendhekar, A., G. Kiczales, et al. (1997). RG: A Case-Study for Aspect-Oriented Programming,
Palo Alto Research Center.
Okamura, H. and Y. Ishikawa (1994 ). "Object Location Control Using Meta-level
Programming." 299 - 319
Ossher, H. and P. Tarr (1999). Multi-Dimensional Separation of Concerns using Hyperspaces,
IBM Research Report.
Ossher, H. and P. Tarr (2000). "Hyper/J: multi-dimensional separation of concerns for Java."
Proceedings of the 22nd international conference on Software engineering: 734-737.
Ossher, H. and P. Tarr (2001). "The Shape of Things To Come: Using Multi-Dimensional
Separation of Concerns with Hyper/J to (Re)Shape Evolving Software." Comm. ACM
44(10): 43-50.
Parnas, D. L. (1972). "On the Criteria To Be Used in Decomposing Systems into Modules."
Comm. ACM 15(12): 1053-1058.
94
Rashid, A., P. Sawyer, et al. (2002). "Early aspects: a model for aspect-oriented requirements
engineering." Requirements Engineering, 2002. Proceedings. IEEE Joint International
Conference on: 199-202.
Rumbaugh, J., I. Jacobson, et al. (1996). "The Unified Modeling Language." Documentation Set
1.
Sant'Anna, C. a., A. Garcia, et al. (2003). On the Reuse and Maintenance of Aspect-Oriented
Software: An Assessment Framework. XVII Brazilian Symposium on Software
Engineering.
Smith, B. C. (1982). "Procedural Reflection in Programming Languages." 762.
Smith, B. C. (1985). "Prologue to "Reflection and Semantics in a Procedural Language''."
Szyperski, C. (1998). Component Software: Beyond Object-oriented Programming, AddisonWesley Professional.
Szyperski, C., J. Bosch, et al. (1999). "Component-Oriented Programming." Object-Oriented
Technology: ECOOP'99 Workshop Reader: ECOOP'99 Workshops, Panels, and Posters,
Lisbon, Portugal, June 14-18, 1999: Proceedings.
Tarr, P., H. Ossher, et al. (1999). N Degrees of Separation: Multi-Dimensional Separation of
Concerns. Proc. 21st Int'l Conf. Software Engineering (ICSE'1999), IEEE Computer
Society Press.
Wirth, N. (1971). "The programming language pascal." Acta Informatica 1(1): 35-63.
95
6. ANEXOS
96
ANEXO 1: LINGUAGEM ASPECTJ
6.1
PRESSUPOSTOS
Para a demonstração prática da linguagem utilizar-se-á o diagrama UML [Rumbaugh,
Jacobson et al. 1996] (figura 23).
Figura 23 – Diagrama UML do exemplo.
Para a elaboração dos exemplos foi utilizado o Eclipse 3.4, o java SDK 6 actualização 6 e
AspectJ 1.6.0.
97
6.2
JOIN POINT
Um elemento crítico no desenho de uma linguagem orientada a aspectos é o modelo de
join point (Pontos de Junção). Este modelo permite disponibilizar um quadro comum de
referência para definir a estrutura dos crosscutting concerns [Kiczales, Hilsdale et al. 2001].
Em AspectJ, join points são pontos bem definidos no fluxo do programa em execução.
São disponibilizados pela linguagem acesso a vários tipos de join points, os que são mais
frequentemente utilizados, visam capturar: chamadas aos métodos, capturar o contexto exterior
do método e capturar o despontar de um método dentro do contexto de outro método.
6.3
POINTCUT
PointCut (Secção) permite definir conjuntos de join points e expor informações de
contexto desses join points. Por exemplo, o seguinte pointcut “call (void Point.setX (int)) ”
intercepta chamadas ao método setX da classe Point com argumento de entrada tipo int e sem
argumento de saída, void, o tipo de join point usado foi o call que permite capturar chamadas a
métodos.
Em AspectJ a definição de pointcuts é definida pelo programador (exemplo 20).
pointcut
nome_do_pointcut ( arg1, ..., argN ) : definição de join points
Exemplo 20 - sintaxe para definir um pointcut em AspectJ.
Na definição (exemplo 20) nome_do_pointcut é o nome atribuído pelo programador para
identificar o pointcut, que cuja interface (a forma de interagir com o pointcut) tem por
98
argumentos “arg1, …, argN”, os Join points encontram-se declarados na parte da definição.
Existem vários tipos de operadores que identificam os vários Join points.
O Join Point é associado a um ponto no fluxo do programa, para por exemplo se capturar
uma chamada a um método, declara-se a assinatura do método a ser capturado (exemplo 21).
pointcut secção_setxPoint: call(void Point.setX(int));
Exemplo 21 – Exemplo de declaração de um pointcut.
6.3.1
Métodos e Construtores
AspectJ disponibiliza dois tipos primitivos para capturar a chamada e execução de
métodos ou construtores de classes.
Chamadas a métodos e/ou construtores
Efectua a captura de chamadas a métodos ou construtores, a captura ocorre depois da
avaliação dos argumentos e, antes ou depois da execução do bloco de código do método ou
construtor. Utiliza-se o operador call, que identifica o join point para capturar chamadas,
fornecendo a assinatura do método ou construtor a ser capturado.
No exemplo 22, é capturada a chamada ao método setX da classe Point com argumento
de entrada do tipo int e sem argumento de saída.
call( void Point.setX(int) )
Exemplo 22 – Intercepção de métodos.
Para capturar chamadas a um construtor de uma classe, ao invés de se declarar o nome do
método, como no exemplo anterior, é definido pela linguagem que se deve utilizar o termo ‘new’
e removido o argumento de saída da assinatura (exemplo 23).
99
call( Point.new(int, int) )
Exemplo 23 – Intercepção de um construtor de uma classe.
Execução de métodos e ou construtores
A intercepção da execução de métodos ou construtores permite a captura do inicio ou do
fim da execução do bloco de código interno desse método. A execução de métodos ou
construtores é definida utilizando o operador execution, fornecendo a assinatura do método ou
construtor (exemplo 24).
execution( void Point.setX(int) )
Exemplo 24 – Intercepção da execução de um método.
Para definir o mesmo tipo de comportamento para um construtor de uma classe, é
utilizado o termo ‘new’ invés do nome do método e é removido o argumento de saída,
analogamente ao que acontece com o pointcut do tipo ‘call’ (exemplo 25).
execution( Point.new(int, int) )
Exemplo 25 – Intercepção de um construtor.
6.3.2
Atributos
Atributos são variáveis definidas em classes, é possível capturar o acesso de leitura ou de
escrita a atributos estáticos e não estáticos.
Para capturar o acesso de leitura a um atributo utiliza-se o operador get (exemplo 26) e
para capturar acessos de escrita, o operador set, junto com a assinatura do atributo (exemplo 27).
get (Point.nome_atributo)
Exemplo 26 - Intercepção do acesso de leitura a um atributo da classe Point.
100
set (Point.nome_atributo)
Exemplo 27 – Intercepção do acesso de escrita a um atributo da classe Point.
6.3.3
Tratamento de excepções
Excepções permitem o tratamento de situações anómalas que ocorram durante a execução
de um certo bloco de código. Para capturar a execução do tratamento de uma excepção, o bloco
de código dentro da cláusula ‘catch’, utiliza-se o operador ‘Handler’ fornecendo o tipo da
excepção (exemplo 28).
handler( DataFormatException )
Exemplo 28 – Intercepção do tratamento de uma excepção.
6.3.4
Inicialização estática
A inicialização estática permite definir valores, que são atribuídos aquando da execução
do programa. Para capturar a inicialização de código estático utiliza-se o operador
‘staticinitialization’ junto com o tipo, por exemplo o nome de uma classe (exemplo 29).
staticinitialization( Point )
Exemplo 29 – Inicialização estática.
6.3.5
Inicialização de objectos
AspectJ permite capturar a execução da inicialização de objectos através dos operadores
‘initialization’ e ‘preinitialization’ fornecendo o nome da classe e a palavra ‘new’ que identifica
o construtor da classe.
No exemplo 30 captura-se a inicialização de objectos:
101
initialization(Point.new(..))
Exemplo 30 – Inicialização de Objectos.
No exemplo 31 captura-se da pré-inicialização de objectos:
preinitialization(Point.new(..))
Exemplo 31 – Pre-inicialização de Objectos.
6.3.6
Lógica Booleana
AspectJ permite agrupar vários Join Points num só pointcut, permitindo uma elaboração
minuciosa na definição de situações de captura.
Para agrupar vários join points utiliza-se a lógica booleana.
•
Intercessão, definida pelos símbolos ‘&&’;
•
União, definida pelos símbolos ‘||’;
•
Inversão, definida pelo símbolo ‘!’.
No exemplo 32, captura-se a chamada ao método setX ou ao método setY.
pointcut
secção_set(): call(void Point.setX(int)) || call(void Point.setY(int)
Exemplo 32 – Agrupar vários Join Points.
É possível construir condições lógicas com join points utilizando o operador de decisão
if. Permitindo limitar a captura de join points com base num certo contexto. No exemplo 33,
captura-se a chamada ao método setX, na classe Point cujo argumento de entrada seja do tipo int
e este for de valor inferior a 0.
call(void Point.setX(int)) && args(x) && if(x<0)
Exemplo 33 - Utilização do operador, if.
102
6.3.7
Exposição do contexto
É possível ter acesso ao contexto, ou seja, à informação de um determinado join point
durante a execução do programa. Por exemplo é possível ter acesso aos argumentos, de entrada
ou de saída, de um método. Os operadores que o permitem são this, target, e args. Estes
operadores tem dupla funcionalidade, funcionam como join point de selecção e expor o contexto.
No exemplo 34, define-se a captura de todos os métodos que tenham um único
argumento de entrada do tipo int, permitindo o acesso a esse argumento que está declarado na
interface do pointcut.
pointcut intArg(int i): args(i)
Exemplo 34 – Pointcut args.
Utilizando a lógica booleana é possível definir clausulas mais especificas. No exemplo
35, captura-se a chamada ao método setX e utiliza-se o operador args, para obter acesso ao
argumento de entrada, tipo int, do método para por exemplo, implementar uma restrição nos
valores que a função aceita.
pointcut
conjunto_setxPoint(int x):call(void Point.setX(int))&& args(x);
Exemplo 35 – Utilização da lógica booleana em pointcuts.
É possível também utilizar o operador de decisão if. No exemplo 36, utiliza-se o operador
if para filtrar o acesso ao contexto. Permitindo definir minuciosamente o pointcut, assim só serão
capturadas todas as chamadas ao método setX da classe Point cujo argumento de entrada tenha
um valor inferior a zero.
103
Pointcut
conjunto_setxPoint(int
x):call(void
Point.setX(int))
&&
args(x)
&&
if(x<0)
Exemplo 36 – Utilização do operador If.
Para capturar um tipo de objecto é possível utilizar o operador this fornecendo o nome da
classe. No exemplo 37, captura-se todos os join points que ocorram durante a execução a
instâncias do tipo Point, não captura contexto estático. É um comportamento dinâmico.
this(Point)
Exemplo 37 – Operador this.
Para capturar join points num objecto existe o operador target e que fornece o nome da
classe. No exemplo 38, captura-se todo o comportamento da classe Point, chamadas a métodos e
atribuições de atributos. É um comportamento estático.
target(Point)
Exemplo 38 – Operador target.
6.3.8
Redefinir assinaturas
AspectJ possibilita utilizar símbolos (Wildacards) para ajudar a definir as assinaturas dos
pointcuts, a tabela 10 mostra os símbolos permitidos.
Símbolo
“..”
“*”
“+”
Descrição
Quaisquer números e tipo de argumentos.
• Quaisquer palavras seguintes;
• Tipo de argumento de saída arbitrário;
• Tipo arbitrário de classe ou método.
Quaisquer subclasses.
Tabela 10 – Símbolos permitidos nas assinaturas.
104
No exemplo 39, é definido um pointcut que permite capturar a execução de métodos com
o nome “m” com um número arbitrário de argumentos de entrada, utilizando o símbolo “..”, e
sem argumentos de saída.
pointcut allM(): execution(void m(..))
Exemplo 39 – Utilização do símbolo “..”.
No exemplo 40, captura-se qualquer chamada a método (s) de nome “m” da classe
“MyClass” com quaisquer tipo de argumento de saída, utilizando o símbolo “*”, e quaisquer
número de argumentos de entrada, utilizando o símbolo “..”.
pointcut allM(): call(* MyClass.m (..))
Exemplo 40 – Utilização do símbolo “*” e “..”.
No exemplo 41, o símbolo “*” é utilizado para captura o método “mover”, este pode estar
implementado em quaisquer classe do programa, com quaisquer tipo de argumento de saída, e
utilizando o símbolo “..”, qualquer número e tipo de argumentos de entrada.
pointcut mover(): call(* *.mover(..))
Exemplo 41 – Utilização do símbolo “*” e “..”.
No exemplo 42, permite utilizar o símbolo “*”, para capturar qualquer método da classe
com quaisquer tipos de argumentos de saída e quaisquer números e tipo de argumentos de
entrada.
pointcut mover(): call(* Classe.*(..))
Exemplo 42 – Utilização do símbolo “*”.
No exemplo 43, o símbolo “*” é utilizado para completar o nome do método, capturando
qualquer tipo de argumento de saída e quaisquer tipo e número de argumentos de entrada de
qualquer método da classe, que comece por “set”, por exemplo agrupará setX e setY, etc.
105
pointcut mover(): call(* Classe.set*(..))
Exemplo 43 - Utilização do símbolo “*”.
No exemplo 44, o símbolo “*” é utilizado para seleccionar qualquer tipo de atributo da
classe que comece por “id” (idCliente, idXpto, etc.).
pointcut id(): get(* Classe.id*)
Exemplo 44 - Utilização do símbolo "*".
No exemplo 45, utiliza-se o símbolo “+” para capturar a chamada ao construtor da classe
Foo e de todos os construtores das subclasses que herdem de Foo.
pointcut allnew():
Exemplo 45 – Utilização do símbolo “+”.
call ( Foo+.new() )
No exemplo 46, é utilizado os símbolos “*”, “+” e (..), para seleccionar todas as
chamadas aos métodos que cujo nome comece por “set” e com um número arbitrário de
argumentos de entrada da classe Figura e todas as subclasses que herdem dessa classe.
pointcut allnew(): call (* Figura+.set*(..) )
Exemplo 46 – Utilização do símbolo “+”, “*” “(..)”.
6.3.9
Modificadores
É possível capturar métodos com base nos seus modificadores, public, static, para definir
cláusulas mais específicas (exemplo 47).
call(public final void C.foo() throws ArrayOutOfBoundsException)
Exemplo 47 – utilização do modificador public.
106
Também é possível utilizar o símbolo booleano da inversão (!) para definir join points
(exemplo 48).
call(!static * *(..))
Exemplo 48 – utilização do modificador static em conjunto com a negação.
6.3.10 Estrutura
Para definir join points em relação a estrutura dentro de uma classe, utiliza-se o operador
within para capturar join points dentro do corpo de uma classe e declarações associadas a esse
tipo de classe (exemplo 49).
within( Point )
Exemplo 49 – Operador Within.
Outro operador withincode permite capturar join points no corpo de um método de uma
classe fornecendo a classe e o método como assinatura (exemplo 50).
withincode( void Device.update() )
Exemplo 50 – Operador withincode.
6.3.11 Fluxo
O operador clfow (exemplo 51) permite capturar todos os join points a partir de um
determinado ponto, caso se queira excluir o join point inicial deve-se usar o operador cflowbelow
(exemplo 52).
cflow ( execution ( void device.update() ) )
Exemplo 51 – Operador cflow.
cflowbelow( execution ( void device.update() ) )
Exemplo 52 - Operador cflowbelow.
107
6.4
ADVICE
Advices permitem definir um bloco de código que é associado a um pointcut. O bloco de
código é o comportamento adicional onde é implementado o crosscutting concerns. Advices
podem ser executados ‘antes’, ‘depois’ ou ‘ao invés de’, permitindo definir o tempo em que é
aplicado o comportamento ao join point. Advices estão associados a um pointcut, este pode estar
previamente definido ou pode ser declarado anonimamente (exemplo 53).
TipoDeAdvice [ Excepção ] : Pointcut { Bloco de codigo }
Exemplo 53 – Sintaxe de declaração de um Advice.
6.4.1
Tipos de Advice
Para definir um comportamento que é efectuado antes de um join point define-se o tipo
de advice como before, na interface do advice devem ser colocados os argumentos que permitem
ter acesso ao contexto do join point capturado (exemplo 54).
before ( arg1, …, argN )
Exemplo 54 – Advice tipo antes.
Para definir um comportamento depois de uma chamada a um método tem de se ter em
conta que existem várias possibilidades para a forma de um método estar declarado. Este pode
ser declarado para lançar uma excepção, devolver um objecto ou nenhuma das duas anteriores.
Para definir um comportamento depois de uma chamada a um método e este devolver um
argumento de saída (exemplo 55).
after (arg1, …, argN) returning [ (arg1, …, argN) ]
Exemplo 55 – Advice tipo depois, com retorno de argumentos.
108
Para definir um comportamento depois de uma chamada a um método, e este método
lançar uma excepção (exemplo 56).
after (arg1, …, argN) throwing [ (arg1, …, argN) ]
Exemplo 56 – Advice tipo depois com declaração de excepção.
Para definir um comportamento depois de uma chamada a um método (exemplo 57).
after (arg1, …, argN)
Exemplo 57 – Advice tipo depois.
É possível substituir o bloco de código que é executado num determinado join point para
definir esse comportamento deve ser usado o tipo around (exemplo 58).
Tipo around (arg1, …, argN)
Exemplo 58 – Advice tipo invés de.
6.4.2
Exemplo de advice
No exemplo 59 é definido um pointcut que captura chamadas aos métodos setP1 e setP2
da classe Line. São implementados dois advices, um que executa antes e outro que executa
depois. Por exemplo com este dois tipos de advices é simples implementar um sistema de anular
e repor estados.
pointcut chamada(): call(void
Line.setP1(..))|| call(void
;
before(): chamada(){
System.out.println("antes");
}
after(): chamada(){
System.out.println("depois");
}
Exemplo 59 – Exemplo da declaração de advices
Line.setP2(..))
109
6.4.3
Lançar excepções
Excepções podem ser, opcionalmente, lançadas no bloco de código de um advice, mas
existem regras bem definidas para quando é possível lançar excepções e quando não é:
•
Execução ou chamadas a métodos ou construtores - só é possível lançar as excepções que
estão declaradas no(s) método(s) ou construtor(es) em causa;
•
Atribuição e leitura de atributos e inicialização estática - não é possível lançar qualquer
excepção;
•
Tratamento de excepções - as excepções que são possíveis lançar são do mesmo tipo que
a capturada;
•
Pré-inicialização e inicialização - é possível lançar as excepções que estão declaradas no
construtor da classe que está a ser inicializada;
•
6.4.4
Execução de advice - qualquer excepção que está declarada na clausula do advice.
Precedência
Podem existir casos em que múltiplos advices estão aplicados a um mesmo join point, a
ordem pela qual são aplicados pode ser definida pelo programador. Existem dois casos distintos:
1. Os advices estão em unidades modulares, aspectos, diferentes;
2. Os advices estão na mesma unidade modular.
Se dois advices, A e B, estão aplicados a um mesmo join point mas estes advices
encontram-se em aspectos diferentes, a precedência tem as seguintes condições:
110
•
Se o aspecto A é invocado antes que o aspecto B, especificando este comportamento com
o operador declare precedence, então todos os advices aplicados ao mesmo join point no
aspecto A, tem precedência aos do aspecto B;
•
Se o aspecto A estende o aspecto de B, então todos os advices definidos no aspecto A tem
precedência sobre os advices do aspecto B, a não ser que esteja especificado de outra
forma, com o operador declare precedence por exemplo;
•
Se não existir qualquer forma de declaração por parte do programador, então não é
possível determinar qual dos aspectos tem precedência.
Se dois advices aplicados a um mesmo join point se encontram declarados no mesmo
aspecto, a precedência tem as seguintes condições:
•
Se o advice é do tipo after, o advice que aparecer em último lugar no código do aspecto
terá precedência;
•
Para qualquer outro tipo de advice, o que aparecer em primeiro lugar no código do
aspecto terá precedência.
Sintaxe para a definição explícita de precedência de aspectos (exemplo 60).
declare precedence : Lista de Aspectos;
Exemplo 60 – Sintaxe da precedência de aspectos.
No exemplo 61, é declarada a precedência de alguns aspectos, de notar que se pode
utilizar Wildcards
declare precedence: *..*Security*, Logging+, *;
Exemplo 61 – Precedência de aspectos.
111
No exemplo 62, são definidos três aspectos. O aspecto Ordem define a ordem em que os
outros dois aspectos são executados, já que esses dois capturam o mesmo ponto no fluxo do
programa.
aspect Ordem {
declare precedence : CountEntry, DisallowNulls;
}
aspect DisallowNulls {
pointcut allTypeMethods(Line obj): call(* *(..)) && args(obj, ..);
before(Line obj): allTypeMethods(obj) {
if (obj == null) throw new RuntimeException();
}
}
aspect CountEntry {
pointcut allTypeMethods(Line obj): call(* *(..)) && args(obj, ..);
static int count = 0;
before(): allTypeMethods(Line) {
count++;
}
}
Exemplo 62 – Declaração de precedências.
6.5
INTER-TYPE DECLARATIONS
Inter-type declarations, permitem alterar o comportamento estático do programa, a
estrutura do código, como a introdução de atributos, métodos em classes e interfaces. Permitindo
a modificação da hierarquia, a declaração de erros, advertências de compilação e
enfraquecimento de excepções (secção 6.6).
112
6.5.1
Adição comportamentos
AspectJ permite a introdução de novos atributos e métodos em classes e interfaces, e
construtores em classes. A adição modular de comportamentos, evita redundância de código, e
permite a implementação de comportamentos por omissão em interfaces, que posteriormente são
compostos nas classes que as implementam.
Adicionar Métodos.
É possível adicionar métodos e declarar o seu bloco de código (exemplo 63) ou métodos
abstractos (exemplo 64).
[ Modificadores ] Tipo NomeClasse . nomeMetodo( argumentos ) [ Excepção ] { Código }
Exemplo 63 – Adição de um método.
abstract [Modificadores] Tipo NomeClasse. nomeMetodo ( argumentos ) [Excepção] ;
Exemplo 64 – Adição de um método abstracto.
No exemplo 65 é adicionado um método a um interface.
//definir a interface
interface Iface {}
aspect A {
private void Iface.m() {
System.err.println("metodo da interface");
}
void worksOnI(Iface iface) {
// invocar o método
iface.m();
}
m.
Exemplo 65 – Exemplificação da adição de métodos.
Adição de construtores
A adição de construtores têm sintaxe semelhante à adição de métodos mas segue a regra
que o nome tem de ser o termo new para a declaração de um construtor (exemplo 66).
113
[Modificadores] NomeClasse. new (argumentos) [Excepção] { Código }
Exemplo 66 – Adição de um construtor a uma classe.
Adição de atributos
È possível também adicionar atributos às classes, a sintaxe para a declaração de atributos
permite a atribuição de valores (exemplo 67) ou só a declaração do atributo (exemplo 68).
[Modificadores] Type NomeClasse. nomeAtributo = Expressão;
Exemplo 67 – Declaração de um atributo com atribuição de valor.
[Modificadores] Type NomeClasse. nomeAtributo;
Exemplo 68 – Declaração de um atributo.
Modificar hierarquia
AspectJ permite modificar a hierarquia das classes (exemplo 69), desde que esta não
viole as regras de herança do Java, e declarar interfaces (exemplo 70).
declare parents: NomeClasse extends TipoaEstender;
Exemplo 69 – Estender um tipo.
declare parents: NomeClasse implements TipoaEmplementar;
Exemplo 70 – Implementar um tipo.
No exemplo 71, o aspecto modifica a classe estendendo a hierarquia desta, implementado
a interface runnable e adicionando os métodos que são obrigatórios implementar dessa interface
“run”.
aspect A {
declare parents: SomeClass implements Runnable;
public void SomeClass.run() { ... }
}
Exemplo 71 – Estender e implementar métodos em classes.
114
Declaração de erros e advertências em tempo de compilação
É possível declarar erros e advertências em tempo de compilação. Quando se verifica um
determinado comportamento definido num pointcut, o compilador avisa com um erro ou com
uma advertência, emitindo uma mensagem que foi posteriormente definida pelo programador. O
pointcut pode estar previamente definido ou declarado anonimamente.
A sintaxe para declaração de erros é a do exemplo 72, enquanto a sintaxe para a
declaração de advertências é a definida no exemplo 73.
declare error: Pointcut: "Mensagem de erro"
Exemplo 72 – Atribuição de um erro a um pointcut.
declare warning: Pointcut: "mensagem de aviso"
Exemplo 73 – Atribuição de uma advertência a um pointcut.
No exemplo 74, define-se um erro para obrigar o programador a utilizar os métodos “set”
dos atributos, e fornecendo um erro quando este não o faz. Assim quando dentro da classe se
aceder directamente para escrever num atributo, o compilador ao compilar o programa emite um
erro.
declare error: set(int Point.*) && !withincode(void Point.set*(int)):
"use o método setter, mesmo dentro da classe.";
Exemplo 74 – exemplo da utilização da declaração de erros.
6.6
ENFRAQUECIMENTO DE EXCEPÇÕES
Podem existir situações onde é obrigatório tratar excepções, mas essas excepções da
forma que o código é implementado nunca irão ser despoltadas. O Java obriga a que seja
declarado o tratamento da excepção, excepto se essa excepção estender a excepção
115
‘RuntimeException‘ou ‘Error’. AspectJ permite redefinir excepções da linguagem Java para que
estas herdem da excepção RuntimeException, e assim evitar o tratamento da excepção (exemplo
75).
declare soft: TipoExcepção: Pointcut;
Exemplo 75 – Sintaxe para a declaração de enfraquecimento de excepções.
No exemplo 76, é utilizado o enfraquecimento de excepção para não tratar a excepção
ClassNotFoundException.
abstract pointcut softeningPC();
before() : softeningPC() {
Class.forName("FooClass"); // erro: ClassNotFoundException
}
declare soft : ClassNotFoundException : call(* Class.*(..));
Exemplo 76 - Exemplo do enfraquecimento de excepções.
6.7
ASPECTOS
Aspectos são unidades modulares que contêm a implementação dos comportamentos
transversais, são compostos por pointcuts, advices, inter-type declarations e código java normal.
Aspectos não podem ser instanciados directamente, mas se for necessário é possível criar um
construtor, mas este construtor não pode ter argumentos ou lançar excepções.
Para se declarar um aspecto utiliza-se o termo aspect (exemplo 77).
aspect Nome { ... }
Exemplo 77 – Declaração de um aspecto.
116
É possível que aspectos contenham outros aspectos internamente, estes aspectos que
estão acoplados dentro de outros aspectos têm de ser obrigatoriamente declarados estáticos com
o modificador static.
Aspectos podem ser estendidos por outros aspectos da mesma forma que uma classe
normal de Java, mas só se o aspecto base está declarado como abstracto. Aspectos podem
estender classes ou interfaces, embora classes não possam estender aspectos.
Aspectos não podem ser instanciados directamente, mas é possível aceder à sua instância
através do método estático, aspectOf, que está implementado em todos os aspectos concretos.
6.7.1
Aspectos Singleton
Por omissão o aspecto só tem uma instância, que é transversal ao programa que está a ser
executado, a este tipo de aspecto chama-se singleton. A sintaxe, normal, para a declaração de um
aspecto singleton é a do exemplo 78. Opcionalmente pode-se usar a definição do exemplo 79.
aspect Nome { ... }
Exemplo 78 – Declaração de um aspecto singleton.
aspect Nome issingleton() { ... }
Exemplo 79 - Declaração alternativa de um aspecto singleton.
6.7.2
Instanciar aspectos por objecto
É possível associar um aspecto a um objecto concreto, e os advices pertencentes a esse
aspecto só são aplicados a esse objecto. O aspecto é declarado e associado com um pointcut e, é
117
criado uma instância do aspecto por cada objecto que o pointcut intercepta. É possível definir
comportamento estático (exemplo 80) ou dinâmico (exemplo 81).
aspect Id perthis(Pointcut) { ... }
Exemplo 80 – Instancia de aspectos por objectos dinamicamente.
aspect Id pertarget(Pointcut) { ... }
Exemplo 81 - Instancia de aspectos por objectos estaticamente.
6.7.3
Aspectos por fluxo.
É possível criar um aspecto por cada fluxo do programa a partir de um determinado
joinpoint. O aspecto é associado com um pointcut. Quando se verificar esse poincut são
instanciados um aspecto por cada fluxo diferente que se crie a partir desse fluxo.
Sintaxe para a declaração a partir de um determinado fluxo (exemplo 82).
aspect NomeAspecto percflow(Pointcut) { ... }
Exemplo 82 – Definir aspecto por fluxo.
É possível omitir o primeiro fluxo que ocorre no joinpoint (exemplo 83).
aspect NomeAspecto percflowbelow(Pointcut) { ... }
Exemplo 83 – Definir aspecto por fluxo, excepto o inicial.
6.7.4
Aspectos com privilégios
Para possibilitar o acesso aos vários tipos de modificadores, em especial a métodos e
atributos declarados como private, é possível declarar aspectos privilegiados (exemplo 84).
privileged aspect Id { ... }
Exemplo 84 – definir aspecto com privilégios.
118
No exemplo 85, é demonstrado o acesso de um aspecto a atributos declarados private.
class C {
private int i = 0;
void incI(int x) { i = i+x; }
}
privileged aspect A {
static final int MAX = 1000;
before(int x, C c): call(void C.incI(int)) && target(c) && args(x) {
if (c.i+x > MAX) throw new RuntimeException();
}
}
Exemplo 85 – Exemplo de Aspecto privilegiado.
119
7. ANEXO 2: @ASPECTJ
7.1
@APECTJ
Na versão 1.5 da linguagem de programação AspectJ foi introduzido um novo estilo de
declarar aspectos, advices e pointcuts, mas mantendo a mesma semântica. A este estilo de
desenvolvimento foi chamado @aspectJ35, este estilo é semelhante às anotações do Java 5 e
parecido à aplicação de atributos da Framework .NET.
Este estilo permite desenvolver aspectos como se de classes normais de Java se tratassem,
que depois são anotadas, utilizando a semântica do AspectJ. Os métodos dessas classes quando
anotados tornam-se Advices.
Este estilo permite que se possa desenvolver programas POA sem alterar as ferramentas
de desenvolvimento, mas é necessário incorporar manualmente o processo de Weaving no
processo de compilação do projecto, através, por exemplo, de tarefas ANT36. Se não existir
integração do processo de compilação o projecto, o programa compilado tem o comportamento
normal POO.
35
36
http://www.eclipse.org/aspectj/doc/released/adk15notebook/ataspectj.html (Acedido em Junho de 2008)
http://ant.apache.org/resources.html (Acedido em Junho 2008)
120
Antes de se poder desenvolver programas POA neste estilo, é necessário primeiro instalar
a linguagem AspectJ37 e importar as bibliotecas, ficheiros jar, ao projecto Java.
7.2
DECLARAÇÃO DE ASPECTOS
Para declarar um aspecto, é necessário primeiro criar uma classe. Nesta classe é
necessário importar a anotação org.aspectj.lang.annotation.Aspect, com esta importação é
possível anotar a classe com a palavra “@aspect” por cima do nome da (exemplo 86). A
ferramenta de desenvolvimento, IDE, mesmo sem alterações reconhece a anotação, devido a
importação da anotação, e consegue detectar erros de sintaxe.
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class NotVeryUsefulAspect {
}
Exemplo 86 - declarar um aspecto em @AspectJ.
7.3
DECLARAÇÃO DE POINTCUTS
Para
declarar
um
pointcut,
primeiro
importa-se
a
anotação
org.aspectj.lang.annotation.Pointcut, que permite ao IDE reconhecer a anotação e ao
programador utilizar a mesma. Dentro da classe anotada como aspecto declara-se um método
como o bloco de código em branco. Anota-se esse método com a palavra “@Pointcut” e as
37
http://www.eclipse.org/aspectj/downloads.php (Acedido em Junho 2008)
121
condições do pointcut. A semântica é igual ao estilo previamente descrito no anexo 1 como é
possível verificar no exemplo 87.
import org.aspectj.lang.annotation.Pointcut;
..
@Pointcut("execution(* transfer(..))")// comportamento
private void anyOldTransfer() {}// assinatura
Exemplo 87 – Declaração de um Pointcut em @AspectJ.
É possível combinar vários pointcuts num só utilizando a lógica booleana (exemplo 88).
@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {}
@Pointcut("within(com.xyz.someapp.trading*")
private void inTrading() {}
@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {}
Exemplo 88 – junção de pointcuts em @AspectJ.
7.4
DECLARAÇÃO DE ADVICES
Neste estilo os advices são implementados através de métodos, os quais são anotados
com a devida sintaxe. Para definir advices primeiro é necessário importar as anotações referentes
ao tipo de comportamento pretendido, antes (.Before), depois (.After, .AfterReturning
.AfterThrowing) ou invés de (.Around).
Para anotar o método que vai implementar o
comportamento transversal, utiliza-se a sintaxe já previamente introduzida (exemplo 89).
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class BeforeExample {
ou
122
@Before("call(* dataAccessOperation(..))")
public void doAccessCheck()
{
// ...
}
}
Exemplo 89 – criação de um advice do tipo antes em @AspectJ.
Para definir um advice que seja invocado depois (After), a forma de declaração é idêntica
aos passos anteriores. Implementa-se o método que irá ser invocado ‘depois’ e anota-se as
condições para dar ocorrência do comportamento e não esquecendo importar primeiro a anotação
(exemplo 90).
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;
@Aspect
public class AfterReturningExample {
@AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doAccessCheck() {
// ...
}
}
Exemplo 90 - criação de um advice do tipo depois em @AspectJ
Também é possível aceder ao argumento de saída (returning) pelo método alvo depois da
sua execução (exemplo 91).
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;
@Aspect
public class AfterReturningExample {
@AfterReturning(
pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
returning="retVal")
public void doAccessCheck(Object retVal) {
// ...
123
}
}
Exemplo 91 - criação de um advice do tipo depois em @AspectJ com acesso a contexto.
É possível utilizar o tipo de advice ao invés de (Around) que permite por exemplo
executar código invés do código do método alvo.
No exemplo 92, é utilizado o tipo de advice ao invés de, para executar tarefas antes do
código do método alvo executar, efectuar a execução do código do método alvo (proceed), e no
fim executar outras tarefas.
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;
@Aspect
public class AroundExample {
@Around("call (* businessService())")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// iniciar contador
Object retVal = pjp.proceed();
// parar contador
return retVal;
}
}
Exemplo 92 - criação de um advice do tipo invés em @AspectJ
7.5
INTER-TYPE DECLARATIONS
Inter-type declarations neste estilo sofre de algumas limitações pois não é possível
introduzir atributos e construtores, já que não é possível invocá-los antes de estes estarem
tricotados com o resto do código. A implementação recorre a Interfaces, a estrutura que se
pretende introduzir é definida em classes e interfaces, e os métodos e atributos dessas classes e
interfaces são introduzidos no programa definindo aspectos anotados.
124
Nos exemplos 93,94 e 95 é mostrado como se procede a alteração da estrutura de uma
classe, primeiro é necessário criar uma interface (exemplo 93). Em seguido é necessário criar
uma implementação da interface (exemplo 94). Cria-se um aspecto onde é necessário definir qual
a classe que vai ser alterada, fornecendo a classe que implementa a interface (exemplo 95).
// Interface
public interface Moody {
Mood getMood();
};
Exemplo 93 – Interface que contem os métodos ou atributos a ser compostos.
// Implementação da Interface
public static class MoodyImpl implements Moody {
private Mood mood = Mood.HAPPY;
public Mood getMood() {
return mood;
}
}
Exemplo 94 – Implementação da interface do exemplo 97.
//Aspecto que compõem a interface
@Aspect
public class MoodIndicator {
// Introduzir a interface em classes
@DeclareParents(value="org.xzy..*",defaultImpl=MoodyImpl.class)
private Moody implementedInterface;
//Invocar a implementação da interface
@Before("execution(* *.*(..)) && this(m)")
void feelingMoody(Moody m) {
System.out.println("I'm feeling " + m.getMood());
}
}
Exemplo 95 – Aspecto para modificar a estrutura de uma classe
125
8. ANEXO 3: IDES
8.1
FERRAMENTAS DE DESENVOLVIMENTO
A linguagem de programação orientada a aspectos AspectJ pode ser adicionada a
qualquer projecto em Java como uma biblioteca. São disponibilizados por essa biblioteca um
conjunto de ferramentas para a compilação, depuração e documentação, alternativamente pode
estar incluída num plug-in para IDE, como o plug-in AJDT para Eclipse. Este Plug-in para além
de adicionar o suporte para a linguagem de programação facilita o seu desenvolvimento com
ajudas visuais.
8.2
PLUG-IN AJDT
O Plug-in AJDT38 [Clement, Colyer et al. 2003] para a plataforma Eclipse, adiciona à
plataforma de desenvolvimento o suporte necessário para o desenvolvimento orientado a
aspectos utilizando a linguagem AspectJ, e um conjunto de ferramentas visuais, para ajudar o
programador no desenvolvimento de aspectos.
38
http://www.eclipse.org/ajdt/ (Acedido em Julho de 2008)
126
AJDT possui a vista cross reference cujas indicações visuais permitem visualmente
perceber a interacção dos advices com o programa. A perspectiva visualizer que permite ver o
efeito dos aspectos no sistema, e o depurador que permite depurar passo a passo programas
AspectJ [Clement, Colyer et al. 2003].
Na figura 24, pode-se observar a perspectiva Java, do lado direito o package Explorer
que contem os ficheiros java ordenados por package. Ao centro o editor dos ficheiros java, e do
lado esquerdo a vista outline e debaixo desta a vista cross reference. De notar do lado esquerdo
do editor existem pequenos ícones que denotam informação sobre advices aplicados.
Figura 24 – Eclipse, perspectiva Java
Nas figuras 25, 26 apresenta-se a perspectiva visualizer, que permite ter a percepção
global de como os vários aspectos afectam o sistema. Como se pode verificar cada aspecto tem
127
uma cor diferente, que é utilizada para indicar os pontos no sistema onde estão esses aspectos
aplicados.
Figura 25 - Eclipse, Perspectiva visualiser
Figura 26 – Detalhe visualiser.
A vista cross reference (figura 27) permite visualizar onde os advices do aspecto estão a
ser aplicados. Os nomes por baixo de advices são navegáveis, o utilizador ao carregar num dos
128
nomes listados é levado para o editor de texto na linha onde é aplicado o advice. Caso se esteja a
editar uma classe que contenha locais onde são aplicados advices, na vista cross reference são
mostrados esses advices, e clicando nos advices ao utilizador é mostrado o ficheiro na janela do
editor de texto e na linha que contem esse advice.
Figura 27 – Detalhe de cross references.
A depuração de programas AspectJ está integrada na plataforma, a perspectiva de
depuração, figura 28, permite depurar passo a passo aspectos e código normal java.
Figura 28 – Perspectiva de depuração.
129
FERRAMENTAS STANDALONE DA BIBLIOTECA ASPECTJ
8.3
As ferramentas incluídas com a biblioteca normal da linguagem de programação AspectJ
são:
•
Ajc: o compilador (weaver);
•
Ajdoc: ferramenta para gerar a documentação;
•
Ajbrowser: para visualizar a interligação dos aspectos com o sistema de software;
Todos os parâmetros e configurações podem ser consultados online na página de ajuda do
projecto AspectJ3940
39
40
http://www.eclipse.org/aspectj/ (Acedido em Junho 2008)
http://www.eclipse.org/aspectj/doc/released/devguide/command-line-tools.html (Acedido em Junho 2008)
130
9. ANEXO 4: POSTSHARP
9.1
INTERCEPÇÃO DE CHAMADAS A MÉTODOS
A classe OnMethodBoundaryAspect, permite interceptar chamadas a métodos e definir
comportamentos a entrada, saída, em caso de sucesso ou em caso que seja lançada uma excepção
pelo método a que é aplicado o atributo. Este comportamento só é valido para os métodos dentro
do contexto do tipo (classe) ao qual é aplicado o atributo.
Os métodos da classe que permitem definir advices são apresentados na tabela 11.
Método
OnEntry
OnSuccess
OnException
OnExit
Tipo de Advice
Entrada no método alvo.
Execução do método alvo com sucesso.
Método alvo lança uma excepção.
Saída do método alvo.
Tabela 11 – Métodos da classe OnMethodBoundaryAspect.
No exemplo 96, o atributo Trace estende a classe OnMethodBoundaryAspect e reimplementa os quatro métodos que definem os tipos de advice.
class TraceAttribute: OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionEventArgs args)
{
//entrada no metodo
}
public override void OnExit(MethodExecutionEventArgs args)
{
131
//saida do metodo
}
public override void OnSuccess(MethodExecutionEventArgs args)
{
//metodo executado com sucesso
}
public override void OnException(MethodExecutionEventArgs args)
{
//metodo lançõu uma excepção
}
}
Exemplo 96 – Exemplo de um atributo Trace.
9.2
INTERCEPTAR INVOCAÇÃO A MÉTODOS
A classe OnMethodInvocationAspect, contem um método que permite receber
notificações de invocações a métodos que não estão no contexto da assembly, em que o atributo
está associado. O nome do método do advice é OnInvocation. Os atributos podem ser aplicados
no local onde os métodos são invocados. O único comportamento permitido é quando o método é
invocado, isto é antes de o método alvo ser executado.
No exemplo 97, mostra-se o código necessário para estender a classe. Este atributo deve
ser aplicado a uma classe ou método que contenha a invocação ao método alvo.
class TraceAttribute: OnMethodInvocationAspect
{
public override void OnInvocation(MethodInvocationEventArgs args)
{
//metodo foi invocado
}
}
Exemplo 97 – Exemplo de um atributo para invocação de métodos
132
9.3
INTERCEPTAR TRATAMENTO DE EXCEPÇÕES
A classe OnExceptionAspect, fornece o advice que permite receber notificações no caso
de lançamento de excepções. O método que define o advice é OnException (exemplo 98).
class TraceAttribute: OnExceptionAspect
{
public override void OnException(MethodExecutionEventArgs args)
{
//Tratar a excepção
}
}
Exemplo 98 - Exemplo de um atributo para tratar excepções.
9.4
INTERCEPTAR ACESSO A VARIÁVEIS
Os métodos da classe OnFieldAccessAspect permitem interceptar acessos de leitura e
escrita a variáveis. Os métodos que definem os advices são apresentados na tabela 12.
Metodo
OnGetValue
SetValue
Descrição do advice
Notificação de quando o valor da variável é lido.
Notificação de quando o valor da variável é escrito.
Tabela 12 – Métodos da classe OnFieldAccessAspect
No exemplo 99, são implementados pelo atributo os métodos para a notificação da escrita
e do acesso de leitura. O atributo deve ser atribuído a uma classe.
class TraceAttribute: OnFieldAccessAspect
{
public override void OnGetValue(FieldAccessEventArgs eventArgs)
{
//variavel foi acedida
}
public override void OnSetValue(FieldAccessEventArgs eventArgs)
{
//novo valor atribuido à variavel
133
}
}
Exemplo 99 – exemplo de atributo de acesso a variáveis.
9.5
IMPLEMENTAÇÃO DE MÉTODOS
A classe ImplementMethodAspect, permite implementar métodos abstractos ou extern,
deferindo a execução desses métodos para o método OnExecution. O bloco de código que é
executado é o que está definido no atributo. Se o atributo é aplicado a um método com um bloco
de código, este código não irá ser executado. O comportamento é semelhante ao invés dos
advices de AspectJ (exemplo 100).
class TraceAttribute: ImplementMethodAspect
{
public override void OnExecution(MethodExecutionEventArgs args)
{
//implementação do método
}
}
Exemplo 100 – exemplo de um atributo para implementar um método.
9.6
IMPLEMENTAR E COMPOR INTERFACES
A classe CompositionAspect, permite implementar uma interface e por composição
aplica-la a numa classe. É necessário implementar a interface numa classe, separadamente, e em
runtime
devolver
um
objecto
com
a
implementação
da
interface
no
método
CreateImplementationObject. O PostSharp necessita que em tempo de execução lhe seja
devolvido o tipo da interface que é implementada através do método GetPublicInterface.
134
No exemplo 101, são mostrados os dois métodos que são preciso implementar.
class TraceAttribute : CompositionAspect
{
public override object CreateImplementationObject(InstanceBoundLaosEventArgs eventArgs)
{
//devolver objecto contendo a implementação da interface
}
public override Type GetPublicInterface(Type containerType)
{
//devolver que interface que é implementada
}
}
Exemplo 101 – exemplo de atributo para compor interfaces.
9.7
APLICAÇÃO DOS ATRIBUTOS
A aplicação dos atributos é efectuada de igual forma que os atributos normais da
plataforma .NET, não existindo diferenças na atribuição dos atributos definidos pelos
programadores.
No exemplo 102, o atributo Trace é aplicado a uma classe, se por exemplo o aspecto
implementado pelo atributo é do tipo chamadas a métodos, o aspecto captura todos os métodos
desta classe.
[Trace]
class Program
Exemplo 102 – Aplicar atributo a uma classe.
No exemplo 103, o atributo é aplicado a um só método
[Trace]
static void sayHello(string name)
{
Console.WriteLine("Hello, {0}.", name);
}
Exemplo 103 – Aplicar atributo a um método.
No exemplo 104, o atributo é aplicado a todos os métodos da classe, mas só aqueles que
comecem pelas letras ‘say’.
[Trace(AttributeTargetMembers="say*")]
135
class Program
Exemplo 104 – Aplicar atributo definindo a quais métodos.
9.8
ATRIBUIÇÃO A MÚLTIPLOS TIPOS.
O PostSharp permite a atribuição de um atributo a múltiplos tipos, classes por exemplo,
no exemplo 105, o atributo Log é aplicado a todo o Namespace BusinessObjects
[assembly: Log(AttributeTargetTypes = " MyApp . BusinessObjects .*",
AttributeTargetMemberAttributes = MulticastAttributes.Public)]
Exemplo 105 – Atribuição de um atributo a múltiplos tipos.
9.9
EXEMPLO DE UTILIZAÇÃO
Para ilustrar uma utilização do PostSharp LAOS, desenvolvemos um projecto, que de
forma simples permite imprimir uma mensagem antes e depois de um determinado método ser
invocado, este tipo de comportamento é útil para fazer registos, segurança, transacções, anular e
repor estados de objectos. Este exemplo está disponível em vídeo41 na página oficial do
PostSharp42.
1. No Visual Studio 2005 cria-se um projecto C# do tipo Console Application
(figura 29) .
41
42
http://www.postsharp.org/about/video/default.aspx (Acedido em Junho 2008)
http://www.postsharp.org/ (Acedido em Junho 2008)
136
Figura 29 – Visual Studio criar Projecto
2. Importar a referência das assemblies PostSharp.public e PostSharp.Laos para o
projecto (figura 30). Ficando estas referencias associadas ao projecto (figura 31).
Figura 30 - Visual Studio importar Referencias.
137
Figura 31 - Visual Studio, referencias no projecto.
3. Criar os atributos estendendo as classes
fornecidas pelo Namespace
PostSharp.Laos. Criando uma nova Classe, e estender essa classe da classe
OnMethodBoundrayAspect e implementar os métodos necessários, no exemplo
106 são os métodos onEntry e onExit. No exemplo também se utiliza o método
CompileTimeInitialize para se obter o nome do método logo na fase de
compilação evitando assim que em tempo de execução se tenha de calcular o
nome do método.
using System;
using System.Collections.Generic;
using System.Text;
using PostSharp.Laos;
namespace TraceSample
{
[Serializable]
class TraceAttribute: OnMethodBoundaryAspect
{
string methodName;
public override void CompileTimeInitialize(System.Reflection.MethodBase method)
{
this.methodName = method.DeclaringType.Name + "/" + method.Name;
}
public override void OnEntry(MethodExecutionEventArgs eventArgs)
{
Console.WriteLine("On entry of {0}.", this.methodName);
}
public override void OnExit(MethodExecutionEventArgs eventArgs)
138
{
Console.WriteLine("On exit of {0}.", this.methodName);
}
}
}
Exemplo 106 – Exemplo prático de um Atributo desenvolvido em PostSharp Laos.
4. Criados os atributos, estes agora precisam de ser aplicados aos métodos, existem
várias formas de o fazer, no exemplo 107 é aplicado o atributo à classe e definido
que só deve ser aos métodos dessa classe que comecem pelas palavras “say”.
using System;
using System.Collections.Generic;
using System.Text;
namespace TraceSample
{
[Trace(AttributeTargetMembers="say*")]
class Program
{
static void Main(string[] args)
{
sayHello("World");
sayGoodbye("World");
Console.ReadKey();
}
static void sayGoodbye(string name)
{
Console.WriteLine("Good bye, {0}.", name);
}
static void sayHello(string name)
{
Console.WriteLine("Hello, {0}.", name);
}
}
}
Exemplo 107 – Exemplo prático da aplicação de um Atributo.
5. Completada a implementação do código efectua-se a compilação. O processo de
compilação do PostSharp é automaticamente inserido a quando da instalação no
139
processo de compilação do visual Studio 2005. O programador quando compila o
programa pode verificar no output o resultado da compilação, exemplo 108.
------ Rebuild All started: Project: TraceSample, Configuration: Debug Any CPU
…………………………………………….
Compile complete -- 0 errors, 0 warnings
PostSharp 1.0 [1.0.8.316] - Copyright (c) Gael Fraiteur, 2005-2008.
TraceSample -> C:\...\bin\Debug\TraceSample.exe
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========
Exemplo 108 – Resultado do Weaving.
6. Completada a compilação o resultado da execução do programa é mostrado na
figura 32.
Figura 32 – Resultado do programa de exemplo.
140
ANEXO 5: FRAMEWORKS
Para além de linguagens de programação orientada a aspectos, existem sistemas,
frameworks, que implementam o paradigma da programação orientada a aspectos de modo a
facilitar a adição modular de funcionalidades em aplicações.
9.9.1
Spring
Spring43 é uma Framework JAVA/J2EE para o desenvolvimento de aplicações
empresariais, permitindo modularmente configurar transacções, acesso remoto usando RMI ou
Web services, acesso a dados.
Spring suporta o paradigma orientado a aspectos que permite por exemplo gerir
transacções, complementando POO com AOP. Spring Framework suporta o crosscutting de
métodos embora seja possível adicionar bibliotecas do AspectJ para estender as possibilidades de
crosscutting.
A POA presente na Framework Spring está orientada para a intercepção de métodos,
antes, depois ou ao invés de, embora a integração com o AspectJ permita expandir o tipo de Join
Points.
43
http://www.springframework.org/ (Acedido em Junho 2008)
141
A implementação do paradigma é com base em proxys dinâmicas, e permite definir
aspectos através de anotações @AspectJ.
9.9.2
Jboss
Jboss44 é outra framework para o desenvolvimento de aplicações midleware que permite
o utilização de AOP para o aumento da modularização, proporcionando uma melhor separação
entre a lógica da aplicação e o código do sistema. Jboss utiliza o estilo anotado @Aspectj para a
definição de aspectos45. Os aspectos em Jboss são dinâmicos, podem ser activados e
desactivados durante a execução do programa.
44
45
http://www.jboss.org/jbossaop/ (Acedido em Junho 2008)
http://www.jboss.org/feeds/post/dynamic_aop_tutorial_part_1 (Acedido em Junho 2008)
142
ANEXO 6: EXEMPLO OBSERVER PATTERN.
No exemplo 109 ao 115 é apresentado uma possível implementação de um padrão de
desenho, o exemplo 109 é um aspecto abstracto que implementa o comportamento comum do
padrão de desenho Observer em AspectJ. Ser abstracto permite a outros aspectos estender e
reutilizar o comportamento comum e implementar só o comportamento específico a esse aspecto.
public abstract aspect ObserverProtocol {
protected interface Subject {
}
protected interface Observer { }
private WeakHashMap perSubjectObservers;
protected List<Observer> getObservers(Subject s) {
if (perSubjectObservers == null) {
perSubjectObservers = new WeakHashMap();
}
List observers = (List) perSubjectObservers.get(s);
if (observers == null) {
observers = new LinkedList<Observer>();
perSubjectObservers.put(s, observers);
}
return observers;
}
public void addObserver(Subject s, Observer o) {
getObservers(s).add(o);
}
public void removeObserver(Subject s, Observer o) {
getObservers(s).remove(o);
}
abstract protected pointcut subjectChange(Subject s);
abstract protected void updateObserver(Subject s, Observer o);
after(Subject s): subjectChange(s) {
Iterator iter = getObservers(s).iterator();
while (iter.hasNext()) {
updateObserver(s, ((Observer) iter.next()));
}
143
}
}
Exemplo 109 – Observer Protocol, Aspecto Abstracto.
No exemplo 110 é estendido o padrão de desenho abstracto do exemplo 92,
ObserverProtocol. Definem-se quais as classes participantes no padrão e qual é o comportamento
que o padrão de desenho tem.
public aspect CoordinateObserver extends ObserverProtocol {
declare parents: Point implements Subject;
declare parents: Line implements Subject;
declare parents: Screen implements Observer;
protected pointcut subjectChange(Subject s):
(call(void Point.setX(int))
|| call(void Point.setY(int))
|| call(void Line.setP1(Point))
|| call(void Line.setP2(Point)) ) && target(s);
protected void updateObserver(Subject s, Observer o) {
((Screen) o).display("Coordinate change.");
}
}
Exemplo 110 – CoordinateObserver, Aspecto Concreto.
No exemplo 111 é estendido
novamente o padrão de desenho abstracto,
ObserverProtocol. Definem-se quais as classes participantes no padrão e qual é o
comportamento que o padrão de desenho tem. Como é possível verificar implementou-se um
novo padrão de desenho com um comportamento diferente do exemplo 93.
public aspect ColorObserver extends ObserverProtocol {
declare parents: Point implements Subject;
declare parents: Line implements Subject;
declare parents: Screen implements Observer;
protected pointcut subjectChange(Subject s):
(call(void Point.setColor(Color)) ||
call(void Line.setColor(Color)) ) && target(s);
144
protected void updateObserver(Subject s, Observer o) {
((Screen) o).display("Color change.");
}
}
Exemplo 111 – ColoObserver, aspecto concreto.
No exemplo 112 a linha de acção é igual aos dois exemplos anteriores, estender o padrão
abstracto, definir as classes participantes e implementar o comportamento em advices.
public aspect ScreenObserver extends ObserverProtocol {
declare parents: Screen implements Subject;
declare parents: Screen implements Observer;
protected pointcut subjectChange(Subject s):
call(void Screen.display(String)) && target(s);
protected void updateObserver(Subject s, Observer o) {
((Screen) o).display("Screen updated.");
}
}
Exemplo 112 – ScreenObserver, aspecto concreto.
Nos exemplos seguintes 113, 114 e 115 é apresentada a implementação das classes
participantes no padrão de desenho.
public class Line {
private Point p1, p2;
private Color c;
public Point getP1() {
return p1;
}
public Point getP2() {
return p2;
}
public Color getColor() {
return c;
}
public void setP1(Point p1) {
this.p1 = p1;
}
public void setP2(Point p2) {
this.p2 = p2;
}
public void setColor(Color c) {
this.c = c;
145
}
}
Exemplo 113- classe Line.
class Point {
private Color c;
protected int x = 0;
protected int y = 0;
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setX(int newX) {
x = newX;
}
public void setY(int newY) {
y = newY;
}
public Color getColor() {
return c;
}
public void setColor(Color c) {
this.c = c;
}
}
Exemplo 114- classe Point.
public class Screen {
public void display(String s) {
}
public void update() {
}
}
Exemplo 115 - Classe Screen.
O padrão de desenho exemplificado apresenta melhor localidade do código, todo o
código referente ao padrão de desenho é implementado em aspectos e não nas classes
participantes. Qualquer alteração é efectuada num só local, no aspecto. As instâncias do padrão
de desenho são localizadas num único local.
146
As classes que participam no padrão de desenho são alheias ao padrão. A forma como o
padrão de desenho está implementada, abstractamente. Permite que seja estendido e partilhado
por várias instâncias de padrões de desenho, que o implementam em concreto, aspecto
colorObserver, aspecto CoordinatorObserver e aspecto ScreenObserver.
Download

Programação Orientada a Aspectos - Técnicas e paradigmas para a