• Nenhum resultado encontrado

Atividade6

N/A
N/A
Protected

Academic year: 2021

Share "Atividade6"

Copied!
28
0
0

Texto

(1)

Universidade Federal de Pernambuco – UFPE

Centro de Informática - CIn

Tópicos Avançados em

Engenharia de Software

Utilizando técnicas de transformação

de programas e geração de código

Dupla:

Caio César Sabino Silva (ccss2@cin.ufpe.br)

Lais Sousa de Andrade (lsa@cin.ufpe.br)

Professor: Paulo Borba (phmb@cin.ufpe.br)

(2)

Sumário

1. Introdução

2. Resolvendo problemas encontrados no projeto

2.1. Problema 1: Baixo grau de parametrização das propriedades do banco de dados

2.1.1. Situação anterior

2.1.1.1. hibernate.cfg.xml

2.1.2. Valores observados

2.1.3. Princípios utilizados

2.1.4. Refatorando o código

2.1.4.1. hibernate.cfg.vm

2.1.4.2. velocity.xml

2.1.4.3. RGMSGenerator.java

2.1.5. Situação posterior

2.2. Problema 2: Código exaustivo no ManagerImpl

2.2.1. Situação anterior

2.2.1.1. ManagerImpl.java

2.2.2. Valores observados

2.2.3. Princípios utilizados

2.2.4. Resolvendo o problema

2.2.5. Situação posterior

2.3. Problema 3: Código exaustivo em Servlets de remoção

2.3.1. Situação anterior

2.3.2. Valores observados

2.3.3. Princípios utilizados

2.3.4. Resolvendo o problema

2.3.4.1. Source file de Membro.java

2.3.4.2. Source file de Publicacao.java

2.3.4.3. Template LHS da transformação

2.3.4.4. Template RHS da transformação

2.3.4.5. transform.jtp

2.3.4.6. Code Template para o Eclipse

2.3.5. Situação posterior

2.4. Problema 4: Código repetitivo em função dos tipos de Notícia

2.4.1. Situação anterior

2.4.1.1. AdicionarNoticiaServlet

2.4.1.2. Noticia

2.4.2. Valores observados

2.4.3. Princípios utilizados

2.4.4. Refatorando o código

2.4.4.1. Source file de Noticia.java

2.4.4.2. Template LHS da transformação

2.4.4.3. Template RHS da transformação

(3)

2.4.4.4. transform.jtp

2.4.5. Situação posterior

(4)

1. Introdução

Nesta atividade, o código-fonte do sistema será refatorado utilizando técnicas de

transformação de programas e geração de código de modo a aumentar a produtividade de

desenvolvimento e permitir reuso de conceitos.

Para cada refatoramento feito, será descrito a situação anterior, o motivo do

refatoramento, o passo-a-passo tomado no refatoramento, descrevendo as técnicas utilizadas e

por fim mostrando e comparando a situação final com a anterior.

(5)

2. Resolvendo problemas encontrados no projeto

Alguns problemas de modularidade e reuso de código foram encontrados e

solucionados na atividade anterior. Algumas soluções ainda podem ser melhoradas e alguns

problemas menores ainda restam no sistema.

Em cada subseção, um problema será estudado e analisado de modo a construir uma

solução satisfatória para o mesmo.

2.1. Problema 1: Baixo grau de parametrização das propriedades do

banco de dados

O arquivo de definição de propriedades do Hibernate (hibernate.cfg.xml) possui alguns

dados relativos ao mapeamento objeto-relacional e outros referentes à conexão com o banco

de dados. Entretanto a parte referente ao banco de dados possui dados fixos inicializados

específicos a um banco de dados.

A resolução desse problema implicará na implementação da funcionalidade de

funcionar para diversos tipos de banco de dados, de forma a gerar uma versão específica

para cada configuração desejada.

2.1.1. Situação anterior

O arquivo hibernate.cfg.xml é assim:

2.1.1.1. hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC

"-//Hibernate/Hibernate Configuration DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration>

<session-factory>

<property name="hibernate.connection.driver_class">org.postgresql.Driver</property> <property name="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</property>

<property name="hibernate.hbm2ddl.auto">update</property> <property name="hibernate.connection.url">

jdbc:postgresql://localhost/postgres/rgms

</property>

<property name="hibernate.connection.username">postgres</property> <property name="hibernate.connection.password">password</property>

(6)

<mapping class="br.ufpe.cin.rgms.membro.modelo.Estudante"/> <mapping class="br.ufpe.cin.rgms.linhasdepesquisa.modelo.LinhaPesquisa"/> <mapping class="br.ufpe.cin.rgms.publicacao.modelo.Publicacao"/> <mapping class="br.ufpe.cin.rgms.publicacao.modelo.PropriedadePublicacao"/> <mapping class="br.ufpe.cin.rgms.noticia.modelo.Noticia"/> <mapping class="br.ufpe.cin.rgms.noticia.modelo.NoticiaCadastrada"/> <mapping class="br.ufpe.cin.rgms.noticia.modelo.NoticiaTwitter"/> </session-factory> </hibernate-configuration>

2.1.2. Valores observados

Percebe-se que o arquivo de propriedades do hibernate pode se tornar mais flexível: os

parâmetros marcados de vermelho variam de SGBD para SGBD. Os valores de azul são os

utilizados durante o desenvolvimento. Caso o sistema ofereça a possibilidade de gerar uma

versão para cada banco de dados, tais valores não podem ser fixos. É importante que haja uma

parametrização de modo que cada versão tenha seus dados de comunicação com o banco de

dados definidos antes da geração do sistema final.

2.1.3. Princípios utilizados

Em geral, cada versão do sistema terá uma configuração desse arquivo de propriedades

fixa. Embora haja também a possibilidade de alterar em tempo de execução tais parâmetros,

nesse refatoramento não estamos preocupados com isso: estamos apenas preocupado em ter

a capacidade de gerar versões do sistema com configurações diferentes desse arquivo nas

propriedades variáveis.

Para tal, é necessário que esse arquivo seja aberto para redefinição nos pontos

marcados de azul. Isto é, precisamos que esse módulo, digamos, seja aberto para extensão de

certa forma.

2.1.4. Refatorando o código

Uma técnica que foi vista anteriormente para tornar o arquivo aberto a redefinição é

utilizando parametrização, mas devemos perceber que apenas queremos parametrizar para

poder gerar um arquivo fixo em tempo de compilação. Isso pode ser atingido com técnica de

pré-compilação (com geração de código), de forma que os parâmetros variáveis do banco de

dados sejam substituídos por valores e tal arquivo seja pré-processado substituindo cada

parâmetro por seu valor, gerando em tempo de compilação um arquivo fixo para cada versão.

Com a técnica de geração de código, vamos utilizar o Velocity, com o plugin

VeloEclipse, para fazer o pré-processamento:

(7)

<hibernate-configuration> <session-factory>

<property name="hibernate.connection.driver_class">$driver</property> <property name="hibernate.dialect">$dialect</property>

<property name="hibernate.hbm2ddl.auto">update</property> <property name="hibernate.connection.url">$url</property>

<property name="hibernate.connection.username">$user</property> <property name="hibernate.connection.password">$password</property> <mapping class="br.ufpe.cin.rgms.membro.modelo.Membro"/> <mapping class="br.ufpe.cin.rgms.membro.modelo.Estudante"/> <mapping class="br.ufpe.cin.rgms.linhasdepesquisa.modelo.LinhaPesquisa"/> <mapping class="br.ufpe.cin.rgms.publicacao.modelo.Publicacao"/> <mapping class="br.ufpe.cin.rgms.publicacao.modelo.PropriedadePublicacao"/> <mapping class="br.ufpe.cin.rgms.noticia.modelo.Noticia"/> <mapping class="br.ufpe.cin.rgms.noticia.modelo.NoticiaCadastrada"/> <mapping class="br.ufpe.cin.rgms.noticia.modelo.NoticiaTwitter"/> </session-factory> </hibernate-configuration>

Editor do VeloEclipse com autocomplete e syntax highlighting

2.1.4.2. velocity.xml

A princípio, vamos gerar dinamicamente com o Velocity usando o RGMSGenerator, mas

não usaremos mais o antigo arquivo de propriedades:

publicacao_from_bibtex=true driver=org.postgresql.Driver dialect=org.hibernate.dialect.PostgreSQLDialect url=jdbc:postgresql://localhost/postgres/rgms user=postgres password=password

Apenas alteraremos para que o RGMSGenerator gere os arquivos do Velocity no próprio

projeto, para facilitar seu uso durante os testes, através de um xml que define os arquivos a

serem pré-processados no projeto:

Para isso, definimos um xml que indica quais arquivos serão pré-processados e

indicando o nome do arquivo de saída

[azul]

e o ambiente no qual tal pré-processamento

(8)

ocorrerá

[vermelho]

:

<?xml version="1.0" encoding="UTF-8"?> <velocity>

<files>

<preprocessing input=”src/hibernate.cfg.vm” output=”src/hibernate.cfg.xml”/>

<preprocessing input=”WebContent/WEB-INF/web.vm” output=”WebContent/WEB-INF/web.xml”/> <preprocessing input=”WebContent/adicionarpublicacao.vm”

output=”WebContent/adicionarpublicacao.jsp”/>

</files> <context>

<variable name=”publicacao_from_bibtex”>true</variable> <variable name=”driver”>org.postgresql.Driver</variable>

<variable name=”dialect”>org.hibernate.dialect.PostgreSQLDialect</variable> <variable name=”url”>jdbc:postgresql://localhost/postgres/rgms</variable> <variable name=”user”>postgres</variable>

<variable name=”password”>password</variable>

</context> </velocity>

2.1.4.3. RGMSGenerator.java

Após isso, o código do RGMSGenerator foi alterado para poder pré-processar em

função da lista de arquivos e o contexto do xml:

public class RGMSGenerator {

private VelocityEngine engine; private VelocityContext context; private XMLHelper helper; private Document xml;

public RGMSGenerator() throws Exception { this.initVelocity();

this.setUpContext(); }

private void setUpContext() throws SAXException, IOException, ParserConfigurationException {

this.xml = this.helper.getXML(new File("velocity.xml")); for(Element variable :

this.helper.getElements(this.xml.getDocumentElement(), "variable")){ context.put(variable.getAttribute("name"), helper.getContent(variable)); }

}

private void initVelocity() throws Exception { this.engine = new VelocityEngine(); Properties config = new Properties(); this.engine.init(config);

(9)

this.context = new VelocityContext(); this.helper = XMLHelper.getInstance(); }

public void process() throws ResourceNotFoundException, ParseErrorException, Exception{ for(Element processing :

this.helper.getElements(this.xml.getDocumentElement(), "preprocessing")){ Template t = engine.getTemplate( processing.getAttribute("input") ); PrintWriter writer = new PrintWriter(processing.getAttribute("output")); t.merge( context, writer );

writer.close(); }

}

public static void main( String args[] ) throws Exception{ new RGMSGenerator().process();

} }

2.1.5. Situação posterior

Os impactos deste refatoramento foram:

implementação, de certa forma, de uma funcionalidade: possibilidade de gerar versões

diferentes do sistema para bancos de dados distintos.

otimização da utilização do velocity com o uso de dois arquivos num mesmo projeto, o

template do velocity (arquivo com extensão .vm) e o arquivo final.

(10)

2.2. Problema 2: Código exaustivo no ManagerImpl

O código da classe ManagerImpl é bastante repetitivo em função das funcionalidades do

DAO.

2.2.1. Situação anterior

Na classe ManagerImpl, destacamos trechos interessantes:

2.2.1.1. ManagerImpl.java

public class ManagerImpl<Tipo extends AbstractBusinessEntity> implements IManager<Tipo> { private static ApplicationContext context;

protected Dao<Tipo> dao; static {

ManagerImpl.context =

new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); }

protected void validar(Tipo tipo) throws RGMSException{

IValidationUtil validation = (IValidationUtil) context.getBean("XMLValidationUtil"); if(!validation.validate(tipo)){

throw new RGMSException("Dados inválidos na inserção"); }

}

public Dao<Tipo> getDao() { return dao;

}

public void setDao(Dao<Tipo> dao) { this.dao = dao;

}

public void inserir(Tipo tipo) throws RGMSException { validar(tipo);

Persistence.getInstance().beginTransaction(); this.dao.adicionar(tipo);

Persistence.getInstance().commit(); }

public void alterar(Tipo tipo) throws RGMSException{ this.validar(tipo);

(11)

Persistence.getInstance().beginTransaction(); this.dao.atualizar(tipo);

Persistence.getInstance().commit(); }

public List<Tipo> listar() {

Persistence.getInstance().beginTransaction();

List<Tipo> retorno = this.dao.listarTudo();

Persistence.getInstance().commit();

return retorno;

}

public Tipo consultarUnicoResultado(long id) { Persistence.getInstance().beginTransaction();

Tipo retorno = this.dao.procurar(id);

Persistence.getInstance().commit();

return retorno;

}

public void remover(Tipo tipo){

Persistence.getInstance().beginTransaction();

this.dao.remover(tipo);

Persistence.getInstance().commit(); }

public void remover(long id){

Persistence.getInstance().beginTransaction();

this.dao.remover(id);

Persistence.getInstance().commit(); }

public void rebind(Tipo tipo){

Persistence.getInstance().beginTransaction();

this.dao.rebind(tipo);

Persistence.getInstance().commit(); }

public Tipo searchOnUniqueField(String field, Object value) { Persistence.getInstance().beginTransaction();

Tipo retorno = this.dao.queryOnUniqueField(field, value);

Persistence.getInstance().commit();

(12)

}

public List<Tipo> list(HashMap<String, Object> restrictions) { Persistence.getInstance().beginTransaction();

List<Tipo> retorno = this.dao.queryOnRestrictionMap(restrictions);

Persistence.getInstance().commit();

return retorno;

}

public boolean exists(Tipo membro, String[] fields) { Persistence.getInstance().beginTransaction();

boolean retorno = this.dao.exists(membro, fields);

Persistence.getInstance().commit();

return retorno;

}

public List<Tipo> consulta(String atributo, String query){ Persistence.getInstance().beginTransaction();

List<Tipo> retorno = this.dao.consulta(atributo,query);

Persistence.getInstance().commit();

return retorno;

}

}

A parte de

azul

é comum aos métodos e consiste em basicamente um método que abre

e fecha a transação com o banco de dados. A parte variável de

vermelho

corresponde aos

parâmetros e o retorno que um método pode ter e que coincide com os mesmos do DAO. A

parte de

verde

é opcional: aparece em uns ou não dependendo se o método retorna algo ou

não.

2.2.2. Valores observados

O código destacado no ManagerImpl:

é repetitivo: percebe-se que os diversos métodos de azul fazem uma mesma coisa:

abrir a transação, delegar uma função ao DAO e fechar a transação. Pela falta dessa

abstração, cada operação é implementada manualmente, e o programador tem que,

para cada funcionalidade do DAO, criar um novo método no Manager que faz as

mesmas coisas dos outros, só mudando a função delegada.

diminui a produtividade de desenvolvimento: Caso a forma como a função seja

delegada ao DAO mude, o código de todos os métodos do Manager precisam ser

alterados manualmente. Igualmente acontece quando adicionamos funcionalidade ao

(13)

DAO: precisamos acrescentar no Manager também. Isso tudo gera uma produtividade

menor.

2.2.3. Princípios utilizados

O princípio utilizado nesse refatoramento é basicamente o de minimizar repetições. Há

uma observação, entretanto, de que será minimizada a repetição de código implementado pelo

programador, e não o código final da aplicação. Isso será explicado melhor na próxima seção.

2.2.4. Resolvendo o problema

Para resolver o problema de produtividade, pensamos inicialmente em usar a

ferramenta JaTS, mas ela não suporta tipos parametrizados, ocorrendo erro de Parsing na

definiçao dos parâmetros de uma classe parametrizada. Dessa forma, vamos definir um Code

Template no Eclipse:

public

${r}

${name}

(

${paramList}

){

Persistence.getInstance().beginTransaction();

${r}

retorno = this.dao.

${name}

(

${argList}

);

Persistence.getInstance().commit();

return retorno;

}

Agora ao digitar “delegate_dao_method”, pode-se usar o auto-complete e preencher

apenas as variações destacadas:

(14)

public void

${name}

(

${paramList}

){

Persistence.getInstance().beginTransaction();

this.dao.

${name}

(

${argList}

);

Persistence.getInstance().commit();

}

2.2.5. Situação posterior

Perceba que as repetições no código no final não foram resolvidas, uma vez que o

código gerado continua o mesmo, entretanto boa parte do código é gerada pelo Eclipse,

apenas as variações são fornecidas pelo programador. Então podemos dizer que essa

repetição de código não afeta a produtividade mais, uma vez que é gerada automaticamente

pela ferramenta.

(15)

2.3. Problema 3: Código exaustivo em Servlets de remoção

Alguns Servlets pequenos ainda possuem código bastante semelhante entre si.

Novamente, vamos apenas remover a repetição de código digitada pelo programador e não no

código final.

2.3.1. Situação anterior

Vejamos dois exemplos de servlets do tipo Remover:

Código muito semelhante dos Servlets

2.3.2. Valores observados

O código destacado no servlets:

é repetitivo;

(16)

diminui a produtividade de desenvolvimento: Caso a forma como a função é

realizada seja alterada, todo o código precisa ser alterado manualmente em todos os

locais.

2.3.3. Princípios utilizados

O princípio utilizado nesse refatoramento é basicamente o de minimizar repetições

implementadas pelo programador.

2.3.4. Resolvendo o problema

Para resolver o problema de produtividade, pensamos inicialmente em usar a

ferramenta JaTS para realizar uma transformação de programa que irá gerar o Servlet de

remoção em função de uma entidade.

Basicamente termos entidades como Membro e Publicacao e um template de saída

que gerará um Servlet de remoção para cada uma delas. Há um inconveniente em que o

JaTS não suporta tipos genéricos, logo o código das classes básicas foi comentado nas

partes em que utiliza Generics ou Annotations para podermos realizar a transformação com

a ferramenta. Outro problema são as variações entre entidades que deviam ser parâmetros a

serem configurados para cada entidade. Isso foi contornado definindo constantes na entidade e

guardando em variáveis no LHS.

Vamos mostrar o exemplo aplicado a Membro.java e Publicacao.java:

2.3.4.1. Source file de Membro.java

Apenas a parte que interessa ao template foi extraída:

public class Membro extends AbstractBusinessEntity {

public final static String ENTITY_PARAMETER = "membro"; public final static String STATUS_FIELD = "membrostatus";

public final static String OPERATION_MESSAGE = "Membro removido com sucesso."; public final static String DESTINATION_PAGE = "membrostatus.jsp";

}

2.3.4.2. Source file de Publicacao.java

public class Publicacao extends AbstractBusinessEntity{

(17)

public final static String STATUS_FIELD = "publicacaostatus";

public final static String OPERATION_MESSAGE = "Publicacao removida com sucesso."; public final static String DESTINATION_PAGE = "publicacaostatus.jsp";

}

2.3.4.3. Template LHS da transformação

ClassPattern #EntityClass {

package br.ufpe.cin.rgms.#PackageName.modelo; ImportDeclarationSet:#C_IDS;

ModifierList:#M class #Entity extends AbstractBusinessEntity {

public final static String ENTITY_PARAMETER = #EntityParameter; public final static String STATUS_FIELD = #StatusField;

public final static String OPERATION_MESSAGE = #OperationMessage; public final static String DESTINATION_PAGE = #DestinationPage; } }

2.3.4.4. Template RHS da transformação

ClassPattern #ServletClass { package br.ufpe.cin.rgms.#PackageName.controle; import java.io.IOException; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import br.ufpe.cin.rgms.Facade; import br.ufpe.cin.rgms.#PackageName.modelo.#Entity;

public class #<(#Entity.addPrefix("Remover")).addSuffix("Servlet")># extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

#Entity entity = Facade.getInstance().

#<#Entity.addPrefix("get")># ((String) request.getParameter( #EntityParameter ));

(18)

request.setAttribute( #StatusField, #OperationMessage );

RequestDispatcher view = request.getRequestDispatcher(#DestinationPage); view.forward(request, response);

}

public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { }

} }

2.3.4.5. transform.jtp

As transformações precisaram ser feitas em arquivos diferentes. Se colocassemos os

dois source files para os templates de sourcePattern, ele dava erro por encontrar dois matches.

Publicacao.java -> RemoverPublicacaoServlet_out.java

#Arquivo de descricao de transformacao

filesDirectory=D:/Downloads/Project/Graduation/TAES/RGMS_Work/jats/servlet-remover wizardJarName=null source.0=Publicacao.java sourceJarName=null applyOutputDir=D:/Downloads/Project/Graduation/TAES/RGMS_Work/jats/servlet-remover/Saida/ templateLeft.0=sourcePattern.jats templateRight.0=targetPattern.jats

(19)

Publicacao.java -> RemoverPublicacaoServlet_out.java package br.ufpe.cin.rgms.publicacao.controle; import java.io.IOException; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import br.ufpe.cin.rgms.Facade; import br.ufpe.cin.rgms.publicacao.modelo.Publicacao; public class RemoverPublicacaoServlet extends HttpServlet {

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException{

Publicacao entity =

Facade.getInstance().getPublicacao((String)request.getParameter("publicacao")); Facade.getInstance().removerPublicacao(entity);

request.setAttribute("publicacaostatus", "Publicacao removida com sucesso."); RequestDispatcher view = request.getRequestDispatcher("publicacaostatus.jsp"); view.forward(request, response);

}

public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{

} }

Membro.java -> RemoverMembroServlet_out.java

#Arquivo de descricao de transformacao

(20)

wizardJarName=null source.0=Membro.java sourceJarName=null applyOutputDir=D:/Downloads/Project/Graduation/TAES/RGMS_Work/jats/servlet-remover/Saida/ templateLeft.0=sourcePattern.jats templateRight.0=targetPattern.jats Membro.java -> RemoverMembroServlet_out.java package br.ufpe.cin.rgms.membro.controle; import java.io.IOException; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import br.ufpe.cin.rgms.Facade; import br.ufpe.cin.rgms.membro.modelo.Membro;

public class RemoverMembroServlet extends HttpServlet {

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException{

Membro entity = Facade.getInstance().getMembro((String)request.getParameter("membro")); Facade.getInstance().removerMembro(entity);

request.setAttribute("membrostatus", "Membro removido com sucesso."); RequestDispatcher view = request.getRequestDispatcher("membrostatus.jsp"); view.forward(request, response);

}

public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{

} }

(21)

2.3.4.6. Code Template para o Eclipse

Alternativamente poderiamos gerar um template para o Eclipse:

public class Remover${entity}Servlet extends HttpServlet {

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException{

${entity} entity =

Facade.getInstance().get${entity}((String)request.getParameter(${parameter})); Facade.getInstance().remover${entity}(entity);

request.setAttribute(${messageAttribute}, ${message});

RequestDispatcher view = request.getRequestDispatcher(${page}); view.forward(request, response);

}

public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{}

}

Como o template do Eclipse é mais prático de usar, optamos por usar essa solução.

2.3.5. Situação posterior

Após a criação dos templates do JaTS ou do Eclipse, temos agora um mecanismo de

geração de Servlets de remoção em função da definição de uma entidade básica com alguns

parâmetros extras (as constantes que foram inseridas nessas classes). Caso a lógica do Servlet

seja alterada, basta alterarmos os RHS dos templates e regerar tais classes, ao invés de fazer

manualmente as alterações. Dessa forma, tratamos o problema da produtividade em função de

mudanças que possam ocorrer nessas classes.

(22)

2.4. Problema 4: Código repetitivo em função dos tipos de Notícia

No sistema, há dois tipos de Notícias: notícia cadastrada e notícia twitter. As notícias

foram modeladas com mecanismo de herança, pois cada subtipo possui atributos específicos.

Mas cada notícia tem um atributo tipo que indica a propriedade do arquivo de idiomas

relacionada a um tipo de notícia. Eventualmente para cada subtipo de Noticia haverá somente

um tipo, então no código dos servlets, é verificado o tipo da notícia e o subtipo correto é

instanciado e manipulado. Essa verificação entretanto é variável em função da quantidade de

tipos de notícia.

2.4.1. Situação anterior

Vejamos a classe AdicionarNoticiaServlet:

2.4.1.1. AdicionarNoticiaServlet

public class AdicionarNoticiaServlet extends NoticiaModelMapperServlet { @Override

protected Noticia getObject() { Noticia ret = null;

String tipo = (String) this.getFormfields().get("tipo"); if (tipo.equals(Noticia.CADASTRADA)) {

ret = new NoticiaCadastrada(); }

if (tipo.equals(Noticia.TWITTER)) { ret = new NoticiaTwitter(); }

ret.setTipo(tipo); return ret; }

@Override

protected void handleObject(Noticia object) throws RGMSException { Facade.getInstance().inserirNoticia(object);

}

@Override

protected void handleSuccess(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException { request.setAttribute("noticiastatus","Noticia cadastrada com sucesso."); }

(23)

}

2.4.1.2. Noticia

@Entity

public abstract class Noticia extends AbstractBusinessEntity implements Comparable<Noticia>{ public final static String CADASTRADA = "noticia_cadastrada";

public final static String TWITTER = "noticia_twitter"; private Membro autor;

private String tipo;

public Noticia(){

// TODO Auto-generated constructor stub }

public Noticia(Membro autor, String tipo){ this.autor = autor;

this.tipo = tipo; }

@OneToOne()

public Membro getAutor() { return autor; }

public void setAutor(Membro autor) { this.autor = autor;

} @Basic

public String getTipo() { return tipo; }

public void setTipo(String tipo) { this.tipo = tipo;

}

public abstract int compareTo(Noticia noticia); }

2.4.2. Valores observados

Percebe-se que o código desse Servlet de Noticias:

é repetitivo: um número de verificações proporcional à quantidade de tipos de notícia

deve ser feito.

(24)

2.4.3. Princípios utilizados

Vamos usar o princípio de minimização de repetições do código que é implementado

pelo programador.

2.4.4. Refatorando o código

Uma abordagem que contemplaria os dois valores do problema seria utilizando AOM,

permitindo alteração dos tipos de Notícia em tempo de execução. Entretanto o custo de

alteração dessa técnica seria muito maior que a abordagem que mostraremos a seguir.

Vamos usar uma abordagem que gera o código que é repetitivo no Servlet. O código

final continuará contendo a repetição de código, entretanto, como tal código é gerado, não há

qualquer prejuízo sobre a produtividade do desenvolvimento. Além do mais, em caso de

mudanças nos tipos de notícia, regerando o código automaticamente, o sistema estará correto

(embora não haja a possibilidade de alterar a definição em tempo de execução).

Usaremos o JaTS para isso, mas devido a suas restrições, alteraremos a classe de

fonte da seguinte forma:

2.4.4.1. Source file de Noticia.java

Com annotations e Generics removidos e alterando os nomes das constantes como os

sufixos dos nomes dos subtipos de Noticia correspondentes (por ausência de documentação de

métodos que manipulem String):

public abstract class Noticia extends AbstractBusinessEntity {

public final static String Cadastrada = "noticia_cadastrada"; public final static String Twitter = "noticia_twitter"; private Membro autor;

private String tipo;

public Noticia(){

// TODO Auto-generated constructor stub }

public Noticia(Membro autor, String tipo){ this.autor = autor;

this.tipo = tipo; }

public Membro getAutor() { return autor; }

(25)

public void setAutor(Membro autor) { this.autor = autor;

}

public String getTipo() { return tipo; }

public void setTipo(String tipo) { this.tipo = tipo;

}

public abstract int compareTo(Noticia noticia); }

2.4.4.2. Template LHS da transformação

ClassPattern #EntityClass {

package br.ufpe.cin.rgms.noticia.modelo; ImportDeclarationSet:#C_IDS;

ModifierList:#M class Noticia extends AbstractBusinessEntity {

FieldDeclarationSet:#FDS; InitializerSet:#BASIC_TYPE_INIT_SET; ConstructorDeclarationSet:#BASIC_TYPE_CDS; MethodDeclarationSet:#BASIC_TYPE_MDS; } }

2.4.4.3. Template RHS da transformação

ClassPattern #ServletClass { package br.ufpe.cin.rgms.noticia.controle; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import br.ufpe.cin.rgms.Facade; import br.ufpe.cin.rgms.base.RGMSException; import br.ufpe.cin.rgms.noticia.modelo.Noticia; import br.ufpe.cin.rgms.noticia.modelo.NoticiaCadastrada; import br.ufpe.cin.rgms.noticia.modelo.NoticiaTwitter;

(26)

public class AdicionarNoticiaServlet extends NoticiaModelMapperServlet { protected Noticia getObject() {

Noticia ret = null;

String tipo = (String) this.getFormfields().get("tipo");

forall #A #in #FDS {

#if(#A.hasModifier("public") && #A.hasModifier("static") && #A.hasModifier("final")) {

forall #VD #in #< #A.getVariables() ># { if (tipo.equals(Noticia.#< #VD.getName() >#)) { ret = new #<#VD.getName().addPrefix("Noticia")>#(); } } } } ret.setTipo(tipo); return ret; }

protected void handleObject(Noticia object) throws RGMSException { Facade.getInstance().inserirNoticia(object);

}

protected void handleSuccess(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException { request.setAttribute("noticiastatus","Noticia cadastrada com sucesso."); }

} }

2.4.4.4. transform.jtp

#Arquivo de descricao de transformacao

filesDirectory=D:/Downloads/Project/Graduation/TAES/RGMS_Work/jats/add-noticia wizardJarName=null source.0=Noticia.java sourceJarName=null applyOutputDir=D:/Downloads/Project/Graduation/TAES/RGMS_Work/jats/add-noticia/Saida/ templateLeft.0=sourcePattern.jats templateRight.0=targetPattern.jats

(27)

O código gerado foi AdicionarNoticiaServlet_out.java:

package br.ufpe.cin.rgms.noticia.controle; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import br.ufpe.cin.rgms.Facade; import br.ufpe.cin.rgms.base.RGMSException; import br.ufpe.cin.rgms.noticia.modelo.Noticia; import br.ufpe.cin.rgms.noticia.modelo.NoticiaCadastrada; import br.ufpe.cin.rgms.noticia.modelo.NoticiaTwitter;

public class AdicionarNoticiaServlet extends NoticiaModelMapperServlet {

protected Noticia getObject() { Noticia ret = null;

String tipo = (String)this.getFormfields().get("tipo");

if (tipo.equals(Noticia.Cadastrada)) { ret = new NoticiaCadastrada(); }

if (tipo.equals(Noticia.Twitter)) { ret = new NoticiaTwitter(); }

ret.setTipo(tipo); return ret; }

protected void handleObject(Noticia object) throws RGMSException{ Facade.getInstance().inserirNoticia(object);

}

protected void handleSuccess(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException{

(28)

} }

Há apenas o inconveniente de as constantes precisarem ser do mesmo jeito que o

sufixo de cada subtipo, por não encontrar uma forma simples de manipular com Strings no

JaTS.

2.4.5. Situação posterior

Esse refactoring foi apenas para mostrar o poder que a transformação de programas

pode dar para eliminar as repetições de código feitas pelo programador. Na situação posterior,

pode-se fazer isso em todo local onde ocorram tais if-else para cada subtipo de forma que tal

código pode ser gerado automaticamente em função dos tipos de publicação existentes no

sistema.

Referências

Documentos relacionados

É termo utilizado para descrever um método de ataque, onde alguém faz uso da persuasão, muitas vezes abusando da ingenuidade ou confiança do usuário, para obter informações que

Em pacientes que foram submetidos à cirurgia de revascularização (por exemplo: colocação de ponte de safena para substituir segmentos de artérias do coração entupidas) do

No prazo de até trinta e seis meses, a contar da data da publicação deste Decreto, os serviços de transporte coletivo aéreo e os equipamentos de acesso às

A fim de evitar ou, quando tal não for praticável, reduzir as emissões para o solo e para a água provenientes do armazenamento de estrume sólido, a MTD consiste em utilizar

O Artigo 79 da Convenção de Viena prevê a liberação de uma obrigação que se tornou impossível devido a um impedimento superveniente não imputável a uma parte, de

Uma vez experimentada a criação e o pecado — do qual só o diabo e o homem são responsáveis: salvando, portanto, a liberdade divina—, Deus não pode ser incoerente com seu desejo

Neste processo eleitoral, a Afubesp/ Comissão Nacional dos Aposentados do Banes- pa (CNAB) em conjunto com o Sindicato dos Bancários de São Paulo, Osasco e Re- gião, Contraf –CUT

Aqui na Principled Technologies, comparamos a experiência de conectar um sistema Dell Latitude Ultrabook com o novo processador Intel Core da 5ª geração a um monitor HD de