TÓPICOS AVANÇADOS EM
ENGENHARIA DE SOFTWARE
Relatório da Atividade 7
-
Técnicas de transformação de programas
e
geração de código
Alunos: Lucas Praciano Marinho {lpm@cin.ufpe.br} Ivson Diniz {ids@cin.ufpe.br}
Professor: Paulo Borba {phmb@cin.ufpe.br}
Introdução
! Nesta atividade procuramos explorar o uso de técnicas de
transformação de programas e geração de código para resolver os problemas identificados no projeto escolhido(projeto 1). Após um estudo dos conceitos apresentados na aula procuramos tentar instalar as ferramentas sugeridas. Como foi apresentada uma escolha entre a ferramenta JaTS e o Velocity preferimos utilizar o Velocity por apresentar uma documentação mais extensa na rede, o que facilitaria a solução de possíveis problemas encontrados durante o seu uso.
! Inicialmente tentamos instalar o plugin VeloEdit na IDE Eclipse, como
sugerido nas instruções, mas encontramos problemas na instalação devido à falta de suporte a versões mais recentes da IDE pelo plugin. Dessa forma decidimos usar um plugin alternativo com recursos similares chamado
Veloeclipse, disponível em http://code.google.com/p/veloeclipse/ .
! Tendo configurado o ambiente pudemos prosseguir com a atividade, que
consistiu das seguintes tarefas:
• Procurar problemas no projeto para os quais o uso de geração de código com o Velocity oferecesse uma boa solução.
• Implementar a solução para os problemas identificados.
• Analisar os resultados do ponto de vista de qualidade de código.
• Procurar trechos de código recorrentes no projeto para os quais o uso dos templates do Eclipse seja aplicável.
• Implementar os templates na IDE.
Oportunidades Para Geração de Código
Velocity
! No nosso processo de análise do projeto para identificar oportunidades
de geração de código procuramos problemas de qualidade de código com as seguintes características:
1. Certo trecho de código ou estrutura se repete com frequência.
2. As variações dessa estrutura dependem somente de alguns parâmetros bem definidos(ou não apresentam variações).
! A escolha dessas características procura explorar as vantagens que a geração de código oferece, que consistem em evitar a digitação de código repetitivo e pouco complexo. O critério 2 é de importância particular, não é vantajoso gerar um código extenso que precisa ser posteriormente modificado pelo programador com frequência para atender as necessidades particulares de cada caso para o qual o código foi gerado, pois qualquer modificação que implique em uma nova geração de código implicará na perda das modificações feitas pelo programador. Variações mais profundas no comportamento do código gerado devem ser implementadas sem a necessidade de alterá-lo, através de delegação, interfaces, subclasses ou patterns como Template Method.
! Dentro desse contexto no projeto logo se destacou a classe Fachada, do
ponto de vista de cada característica enunciada anteriormente podemos observar que é um caso ideal para a aplicação de geração de código:
! Do ponto de vista do critério 1 vemos que a classe Fachada consiste
! No critério 2 vemos que o código varia segundo os seguintes parâmetros bastante precisos:
• Que classes serão expostas.
• Que métodos de cada classe serão expostos.
• Assinatura de cada método(nome, tipo de parâmetros, tipo de retorno).
! Também é possível notar que a variação da classe Fachada consiste
somente da variação nestes parâmetros, qualquer código que o programador vá inserir na Fachada manualmente geralmente pode ser associado com má prática uma vez que muito provavelmente vai fugir dos concerns da classe (integração do sistema).
! No critério 3 podemos explorar algumas soluções alternativas ao
problema, em particular o uso de introspecção e AOM para automatizar a exposição dos métodos das classes de negócio. Neste caso a classe fachada iria expor um conjunto de Strategies que seriam geradas a partir de introspecção sobre as classes de negócio ou a partir de um arquivo descrevendo a Fachada. Dessa forma a classe Fachada ficaria mais flexível e sem repetição. No entanto o acesso à interface da Fachada se tornaria bem mais complexo com o uso de Strategy. Por outro lado o código não necessita de nenhuma variação em tempo de execução do ponto de vista dos métodos expostos em Fachada, então a solução parece ter uma complexidade um pouco excessiva neste caso, podendo ser mais prejudicial que benéfica.
! Dessa forma consideramos o uso de geração de código na Fachada
adequado para nossos objetivos.
! Além dessa oportunidade para geração também existe a possibilidade do uso de templates do Eclipse para automatizar a geração de pequenos trechos de código. A sua aplicabilidade é um pouco diferente da de geração de código através do Velocity. Geralmente os templates são trechos mais curtos de código, e serão gerados apenas uma vez pelo programador para cada lugar onde serão usados. Dessa forma o critério 2 apresentado anteriormente muda um pouco, uma vez que templates são feitos para serem modificados extensivamente pelo programador depois de sua geração.
! Nesse contexto achamos interessante fazer o uso de templates na
criação das classes Servlet, que apresentam métodos doPost e doGet com muita frequência.
Implementação de Geração de Código
Velocity
! Para a Fachada decidimos fazer uma classe que implementa a rotina de
geração do seu código chamada GeradorFachada, sua implementação é bem direta e é apresentada a seguir:
! Pode-se ver que um conjunto de classes que serão expostas através da
fachada.vm
package br.ufpe.cin.in980.logic; import br.ufpe.cin.in980.basic.*;
#foreach( $classe in $classesExpostas ) import $classe.getName();
#end
public class Fachada {
private final static Fachada INSTANCIA = new Fachada(); private Fachada() {
}
public static Fachada obterInstancia(){ return INSTANCIA;
}
#foreach( $classe in $classesExpostas )
#set( $nomeClasse = $classe.getSimpleName() )
#foreach( $metodo in $classe.getDeclaredMethods())
#set( $parameters = $metodo.getGenericParameterTypes())
#set($parameterType = "")
#foreach( $parameter in $parameters)
#set($parameterType = $parameter.getSimpleName())
#end
#set($returnType = $metodo.getGenericReturnType().toString()) public $returnType $metodo.getName()(#if( $parameterType != "" )
$parameterType p#end) throws Exception {
#if( $parameterType != "" ) if( p != null) {
$nomeClasse controle = new $nomeClasse();
controle.$metodo.getName()(#if( $parameterType != "" ) p#end);
}
#else
$nomeClasse controle = new $nomeClasse();
return controle.$metodo.getName()(#if( $parameterType != "" ) p#end);
#end
}
#end #end
}
! O que vemos aqui é o gerador classe Fachada, o template percorre
! A seguir mostramos um trecho do código gerado através desse template:
public class Fachada {
private final static Fachada INSTANCIA = new Fachada();
private Fachada() { }
public static Fachada obterInstancia(){
return INSTANCIA; }
public void cadastrarMembro(Membro p) throws Exception { br.ufpe.cin.in980.basic.Membro membro;
if( p != null) {
ControleMembro controle = new ControleMembro(); controle.cadastrarMembro(p);
}
}
public boolean isMembroInvalido(Membro p) throws Exception {
if( p != null) {
ControleMembro controle = new ControleMembro(); controle.isMembroInvalido(p);
}
}
public java.util.List<br.ufpe.cin.in980.basibr.ufpec.Membro> buscarMembro(String p) throws Exception {
if( p != null) {
ControleMembro controle = new ControleMembro(); controle.buscarMembro(p);
}
}
public void editarMembro(Membro p) throws Exception {
if( p != null) {
ControleMembro controle = new ControleMembro(); controle.editarMembro(p);
} //...
! Em particular tivemos alguns problemas com os tipos que os métodos
de introspecção expõem, eles apresentam o caminho completo para a classe e isso gera alguns problemas quando se declara esses tipos nas assinaturas dos métodos. Além disso alguns métodos foram expostos quando não deviam, já que foram adicionados pelo AspectJ utilizado no projeto.
Templates
! Os templates do Eclipse foram implementados através do seguinte
servletsDoGet&doPost add methods doGet & doPost
/** add "extends HttpServlet" to the class*/ @Override
public void doGet(${request:newType(javax.servlet.http.HttpServletRequest)} request,
${response:newType(javax.servlet.http.HttpServletResponse)} response) throws ServletException, IOException {
${cursor}
/*adicione codigo do metodo*/ }
@Override
public void doPost(${request:newType(javax.servlet.http.HttpServletRequest)} request,
${response:newType(javax.servlet.http.HttpServletResponse)} response) throws ServletException, IOException {
this.doGet(request, response); }
! Ele permite a adição de métodos doGet e doPost a um servlet