• Nenhum resultado encontrado

Refatorar sistema, utilizar e avaliar padrões de projeto e codificação

N/A
N/A
Protected

Academic year: 2019

Share "Refatorar sistema, utilizar e avaliar padrões de projeto e codificação"

Copied!
66
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 Refatoramento

e Design Patterns no Sistema

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)

Refatorar sistema, utilizar e avaliar padrões de projeto e

codificação

Nesta atividade, os principais problemas de reuso e modularidade encontrados nas atividades anteriores serão estudados de modo a construir uma solução que reduza seus efeitos negativos. Para construir estas soluções serão aplicadas técnicas de refatoração, que utilizam padrões de projeto e de codificação, e também técnicas de parametrização e componentes.

Este documento visa tanto descrever como foi feito o uso destas técnicas no projeto, como analisar e avaliar cada técnica utilizada, justificando suas escolhas e aplicações.

Rodando o projeto

(3)
(4)

Problemas

Problema 1 - Métodos idênticos nos Servlets

Durante a fase de análise de reuso e modularidade, os subtipos de Servlet possuíam uma redundância notável de código que coincidia com a sobrecarga de concerns que estavam sendo tratados por tais classes. Também ocorre que há métodos em comum entre os Servlets, com variações apenas do nome do atributo em alguns casos.

Situação inicial

(5)

TreeMap da situação anterior

(6)

Exemplo de código onde o problema ocorre - Clone set 42

Valores observados

Os valores observados no código que precisavam ser melhorados foram:

Simplicidade: o código dos Servlets não é simples. Sobretudo no método doPost, há inúmeras

linhas de código associados a diferentes concerns como visto na última análise.

Flexibilidade: o código dos Servlets é bastante rígido, pouco suscetível a reuso por outras

(7)

Princípios utilizados

Os princípios básicos utilizados nesse refatoramento para melhorar os valores observados são:

Minimização de repetições, uma vez que clones significativos foram encontrados vindos

desse problema. A questão principal desse clone set é o tamanho dele, pois o mesmo é o segundo maior de todos encontrados no sistema.

Extração de pontos de extensão, uma vez que deveria haver módulos abertos para extensão

que tratem um concern em comum entre os Servlets. Nesse sentido, percebeu-se que boa parte dos Servlets lidavam com requisições de formulários e utilizavam comandos em comum, enquanto alguns outros tratavam requisições de formulários com campos de upload. Esse concern de captura de dados poderia ser tratado por classes abertas para extensão que possuem valor semântico para o sistema em termos de modularidade, não sendo apenas classes que reusam código repetido.

Refatorando o código

(8)

Diversas classes puderam ter código reusado:

Alguns pequenos ajustes manuais foram feitos:

● Correção do erro de compilação por ausência de import em algumas classes como HttpServlet, IOException;

● Correção do erro de compilação nas subclasses que definiam doPost com modificador protected, pois a classe gerada é abstrata e definiu como public automaticamente na assinatura;

(9)

Após isso, aplicou-se Rename na classe para RGMSUploadFormServlet e dela extraiu-se uma

nova superclasse a RGMSFormServlet, uma vez que se observou que algumas classes só precisam usam campos de formulário tradicionais e outras usam, além dos tradicionais, alguns de upload.

Logo em seguida, fez-se Pull up no método extractFields para RGMSUploadFormServlet como

(10)

Nesse processo, identificou-se que alguns Servlets usavam um método extractFields e outros realizam seu código inline no doPost. Para obter um reuso de tal código, substituiu-se pela chamada

do método extractFields que está na superclasse RGMSUploadFormServlet. Outro detalhe é que o seguinte código estava replicado nos métodos doPost que poderia ser usado no supertipo:

// Create a factory for disk-based file items FileItemFactory factory = new DiskFileItemFactory();

// Create a new file upload handler

ServletFileUpload upload = new ServletFileUpload(factory);

Com o refactoring Rename, alterou-se o atributo foto para arquivo, para que represente um

upload de um arquivo genérico, podendo ser um pdf, uma foto, dependendo da classe que a usar. Outra alteração que foi realizada foi a mudança de pacotes de ListGenerator que não é um Servlet para o pacote util.

Realizou-se Pull up no método addField que havia em alguns subtipos de RGMSFormServlet.

(11)

RGMSFormServlet.java

public abstract class RGMSFormServlet extends HttpServlet {

private HashMap<String, String> formfields;

protected abstract void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException;

public RGMSFormServlet() { super();

}

protected void processFormField(FileItem item) { // Process a regular form field

if (item.isFormField()) {

String name = item.getFieldName(); String value = item.getString();

this.getFormfields().put(name, value); }

}

public HashMap<String, String> getFormfields() { return this.formfields;

}

public void setFormfields(HashMap<String, String> formfields) { this.formfields = formfields;

}

public void addField(String nome,HttpServletRequest request,HashMap<String, String> formfields) { if(request.getParameter(nome) != null && !request.getParameter(nome).trim().equals("")){

formfields.put(nome, request.getParameter(nome)); } this.formfields.put(nome, request.getParameter(nome)); request.removeAttribute(nome); } } RGMSUploadFormServlet.java

public abstract class RGMSUploadFormServlet extends RGMSFormServlet {

private byte[] arquivo;

@Override

protected abstract void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException;

public RGMSUploadFormServlet() { super();

}

(12)

public void init() throws ServletException { super.init();

this.setFormfields(new HashMap<String, String>()); }

protected void processUploadedFile(FileItem item) { if(item.getContentType() != null){

this.setArquivo(item.get()); }

}

public void setArquivo(byte[] arquivo) { this.arquivo = arquivo;

}

public byte[] getArquivo() { return arquivo; }

protected void extractFields(HttpServletRequest request) throws FileUploadException { // Create a factory for disk-based file items

FileItemFactory factory = new DiskFileItemFactory();

// Create a new file upload handler

ServletFileUpload upload = new ServletFileUpload(factory);

List /* FileItem */ items = upload.parseRequest(request);

// Process the uploaded items Iterator iter = items.iterator();

while (iter.hasNext()) {

FileItem item = (FileItem) iter.next();

if (item.isFormField()) { processFormField(item); } else { processUploadedFile(item); } } } }

(13)

Situação posterior

Os impactos do refatoramento foram:

● Aumento do reuso de código passível a mudanças do sistema, o que pode tem impactos positivos na sua manutenção;

● Facilidade para a criação de uma página que seja formada por um formulário (com ou sem campos de upload de arquivos) através do reuso deste código, uma vez que o básico destas páginas já está definido;

● Eliminição do código repetitivo em vários Servlets;

Polymetric da situação posterior

(14)

Gráfico dos clones após o refatoramento

(15)

TreeMap da situação posterior

No TreeMap, pode-se perceber que após o refatoramento os blocos pretos encontram-se isolados na classe que correspondem aos métodos doPost. Não há mais pequenos blocos ao redor que seriam os métodos replicados nos Servlets que foram extraídos para as superclasses.

(16)

Problema 2 - Repetição de código de validação

Em diversos locais do projeto, há a verificação de diversos atributos de classes básicas de dados, em especial à verificação de Strings se são vazias ou nulas.

Situação anterior

(17)

Exemplo do código de clone

Valores observados

Os principais valores observados foram:

Simplicidade: o código acima possui uma enorme condição de if, dificultando a compreensão

sobretudo pela repetição de verificação dos campos Strings se são null ou vazio.

Legibilidade: o código acima pela ausência de uma expressão declarativa que represente seu

(18)

Princípios utilizados

Os principais princípios utilizados são:

Minimizar repetições: Há uma repetição do código “string == null || string.equals(“”)” para todo

campo String do objeto.

Generalização de código: Definição de regras de validação que possam ser usadas para

qualquer campo;

Utilização de expressões declarativas: Substituição da condição complexa por uma

expressão declarativa de melhor valor semântico deve ser feita.

Simetria: A condição do if é enorme e complexa, enquanto o código do bloco do if é somente

uma linha. Para aumentar a legibilidade, a simetria entre a condição e o corpo do if deve ser feita.

Open Closed Principle: qualquer mudança na forma como um campo é validado exige uma

modificação neste código, o que quebra este princípio. Seria interessante a criação de um ponto de extensão neste local de modo que qualquer mudança seja uma extensão do código.

Refatorando o código

Partindo da idéia de que a expressão do if representa a validação de um objeto, decidiu-se utilizar uma técnica de parametrização com a utilização de Reflection e arquivos Properties para validar os campos de uma classe básica. A motivação para isso é que as regras de negócio costumam variar num software e é importante que se possa alterá-las com facilidade no desenvolvimento. Em específico, será feito uma generalização das regras de propriedades de uma classe de dados. As associações regra-propriedade serão definidas num arquivo properties no pacote da classe de dados associada com mesmo nome e extensão “.validate.properties”. Dessa forma, as regras podem ser trocadas no ambiente de execução sem necessidade de reiniciar o servidor. Serão definidos dois pontos de extensão:

● Extensão de uma regra de validação: para que novas regras possam ser adicionadas facilmente;

● Extensão para que se possa aplicar regras de negócio mais complexas no próprio código da classe de dados associada.

Para a criação destes pontos de extensão, foram criadas interfaces que oferecem serviços de validação. A princípio foi criada uma interface que define uma regra de validação de um campo:

IValidationRule.java

public interface IValidationRule {

(19)

public abstract boolean validate(Object value);

}

O método setParameters carregará quaisquer parâmetros relevantes à regra de validação, enquanto o validate, recebe um valor de um campo e o valida de acordo com suas regras. Após isso, foi definida uma regra que corresponde à contida no clone destacado neste problema, RequiredFieldValidationRule:

RequiredFieldValidationRule.java

public class RequiredFieldValidationRule implements IValidationRule {

public void setParameters(String[] parameters){ // DO NOTHING

}

public boolean validate(Object value){ boolean valid = (value != null);

if(value instanceof String){

valid = valid && !((String) value).isEmpty(); }else if(value instanceof Collection<?>){

valid = valid && !((Collection<?>) value).isEmpty(); }

return valid; }

}

Agora será definido uma interface de validação de objetos de negócio do sistema:

IValidationUtil.java

public interface IValidationUtil {

public abstract boolean validate(AbstractBusinessEntity object); }

(20)

A idéia é que os arquivos de propriedades das classes sigam esse formato:

PropertiesValidationUtil.java

@Service(value = "PropertiesValidationUtil")

public class PropertiesValidationUtil implements IValidationUtil {

private IValidationRuleFactory factory;

@Autowired

public void setValidationRuleFactory(

@Qualifier(value = "ValidationRuleFactory") IValidationRuleFactory factory){ this.factory = factory;

}

public boolean validate(AbstractBusinessEntity object){ try{

Class c = object.getClass();

java.util.Properties properties = new Properties();

while(!c.equals(Object.class)){

properties.load(c.getResourceAsStream(

String.format("%s.validate.properties", c.getSimpleName()) ));

c = c.getSuperclass(); }

Iterator<String> props = properties.stringPropertyNames().iterator(); boolean validate = true;

while(props.hasNext() && validate){

String propertyName = props.next();

String[] value = properties.getProperty(propertyName).split(",");

IValidationRule validation = factory.createValidationRule(value);

validate = this.validate(object, object.getClass(), propertyName, validation);

}

return validate; }catch(IOException e){

(21)

} }

private boolean validate(Object object, Class clazz, String name, IValidationRule rule){ boolean valid = false;

if(!clazz.equals(Object.class)){ try {

Field field = clazz.getDeclaredField(name); boolean acessible = field.isAccessible(); field.setAccessible(true);

Object value = field.get(object);

field.setAccessible(acessible);

valid = rule.validate(value); } catch (SecurityException e) {

throw new IllegalStateException(); } catch (NoSuchFieldException e) {

valid = this.validate(object, clazz.getSuperclass(), name, rule); } catch (IllegalAccessException e) {

valid = this.validate(object, clazz.getSuperclass(), name, rule); }

}else{

throw new IllegalStateException(); }

return valid; }

}

Porém, neste ponto, a classe PropertiesValidationUtil necessita de uma implementação da interface IValidationRule, que está sendo indicada no arquivo de properties. Foi utilizado então o padrão de fábricas para a instanciação desta regra, criando-se uma Factory abstrata que instanciará as subclasses de IValidationRule adequadas em função do valor class no arquivo .properties. O Spring foi utilizado para injetar a implementação desejada da Factory. A interface das factories e uma implementação delas vêm a seguir:

IValidationRuleFactory.java

public interface IValidationRuleFactory {

public IValidationRule createValidationRule(String[] value);

}

ValidationRuleFactory.java

@Service(value = "ValidationRuleFactory")

public class ValidationRuleFactory implements IValidationRuleFactory {

(22)

String[] clazz = ValidationRuleFactory.getParameterAndValue(value[0]);

try{

Class c = Class.forName(clazz[1]);

if(IValidationRule.class.isAssignableFrom(c)){

IValidationRule validation = (IValidationRule) c.newInstance(); validation.setParameters(value);

return validation; }else{

throw new IllegalStateException(); }

}catch(ClassNotFoundException e){

throw new IllegalArgumentException(); } catch (InstantiationException e) {

throw new IllegalArgumentException(); } catch (IllegalAccessException e) {

throw new IllegalArgumentException(); }

}else{

throw new IllegalStateException(); }

}

private static String[] getParameterAndValue(String text){ if(text != null){

String[] divisions = text.split("=");

if(divisions.length == 2){ return divisions; }else{

throw new IllegalStateException(); }

}else{

throw new IllegalStateException(); }

} }

Percebe-se que todas as dependências de validação são em entidades abstratas. O Spring vai devolver a implementação do validador desejada. Esse princípio permite a independência entre as duas camadas, podendo trocar de camada facilmente, apenas requisitando a implementação adequada ao Spring.

O próximo passo é tornar o Controle, que era uma classe parametrizada em Tipo, em uma

classe parametrizada em Tipo extends AbstractBusinessEntity e o método validar(Tipo tipo) deixar

de ser abstrato, uma vez que agora passa a ser definido como:

protected void validar(Tipo tipo) throws RGMSException{

ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); IValidationUtil validation = (IValidationUtil) context.getBean("PropertiesValidationUtil");

if(!validation.validate(tipo)){

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

(23)

As subclasses de Controle como apenas realizam esse tipo de validação não precisam redefinir o método validar. A condição do if, destacada como clone pelo CCFinder, foi subistituida pela chamada do método validar definido no Controle, deixando a condição mais legível, com o uso de uma expressão declarativa.

Este fatoramento em especial foi totalmente desenvolvido manualmente, uma vez que estes pontos de extensão não existiam e tiveram que ser criados do zero. Com o código existente não foi possível a utilização de técnicas automáticas para a refatoração.

Situação posterior

Os impactos do refatoramento foram:

● Possibilidade de alterar a validação de uma entidade básica em tempo de execução;

● Facilidade de definição de uma regra de validação de um campo graças à interface IValidationRule, de modo que a modificação desta funcionalidade é feita adicionando-se novo código e não modificando os existentes;

● Possibilidade de estender a validação via código Java apenas redefinindo o método validate(Tipo tipo), acrescentando quaisquer regras mais complexas que necessitem ser realizadas no código;

● Reuso de regras de validação para campos distintos;

Separação dos concerns de Validação e Controle, como havia sido detectado como

necessário na análise de modularidade;

(24)

Gráfico dos clones após as alterações

(25)

Problema 3 - Pouca flexibilidade da implementação do DAO

O código da classe DAO é definido utilizando generics com o intuito de generalizar, entretanto há subtipos de DAO para cada tipo de entidade com código repetido entre si que são originados de uma perda de generalização na implementação de alguns métodos fazendo com que o código de métodos nessas classes não seja facilmente reutilizável por outra classe.

Situação anterior

(26)

Amostra do Clone Set 56

Valores observados

Os valores observados nesse problema foram:

Flexibilidade: o código getLinhaPesquisa e getNoticia é bastante rígido, pois realiza uma

(27)

Simplicidade: o fato de existirem múltiplas implementações específicas de DAO torna o código

mais complexo. É interessante verificar se não existe uma abordagem mais simples que possa generalizar o conceito das consultas do DAO e unificá-las num DAO mais genérico.

Princípios utilizados

Generalização do código: implementar as funcionalidades do Dao de maneira não específica,

de modo que funcione para uma classe de entidades, não para uma apenas;

Minimização de repetição: remover as repetições de código existentes nas várias

implementações específicas de Dao, colocando-as na classe genérica;

Refatorando o código

A princípio, percebeu-se que os métodos getFirstAtributoDeOrdenacao e getSecondAtributoDeOrdenacao poderiam ser generalizados para um atributo atributosOrdenacao,

que estende a lógica para quantidade arbitrária de atributos de ordenação. Dessa forma, métodos como:

public List<T> listarTudo() {

Criteria criteria = Persistence.getInstance().getSession().createCriteria(this.classe);

criteria.addOrder(Order.asc(this.getFirstAtributoDeOrdenacao())); criteria.addOrder(Order.asc(this.getSecondAtributoDeOrdenacao()));

return criteria.list(); }

agora se comportam de forma mais genérica:

public List<T> listarTudo() {

Criteria criteria = Persistence.getInstance().getSession().createCriteria(this.classe);

for(String order : this.atributosOrdenacao){ criteria.addOrder(Order.asc(order)); }

return criteria.list(); }

(28)

DaoMembro.java

● Membro getUsuario(String email)

public Membro getUsuario(String email) { Membro retorno = null;

Criteria criteria = Persistence.getInstance().getSession().createCriteria(Membro.class);

criteria.add(Restrictions.eq("email", email));

List<Membro> lista = criteria.list();

if(lista.size() > 0){

retorno = lista.get(0); }

return retorno; }

Este método foi substituído por um queryOnUniqueField:

public T queryOnUniqueField(String field, Object value){ T retorno = null;

Criteria criteria = Persistence.getInstance().getSession().createCriteria(this.classe);

criteria.add(Restrictions.eq(field, value));

List<T> lista = criteria.list();

if(lista.size() > 0){

retorno = lista.get(0); }

return retorno; }

● List<Membro> getMembros(HashMap<String, String> formFields)

public List<Membro> getMembros(HashMap<String, String> formfields) {

Criteria criteria = Persistence.getInstance().getSession().createCriteria(Membro.class);

criteria.addOrder(Order.asc(this.getFirstAtributoDeOrdenacao())); criteria.addOrder(Order.asc(this.getSecondAtributoDeOrdenacao()));

for(String campo : formfields.keySet()){

String like = "%" + formfields.get(campo) + "%";

criteria.add(Restrictions.ilike(campo, like)); }

(29)

Este método foi substituído por um queryOnRestrictionMap:

public List<T> queryOnRestrictionMap(HashMap<String, Object> formfields) {

Criteria criteria = Persistence.getInstance().getSession().createCriteria(this.classe);

for(String order : this.atributosOrdenacao){ criteria.addOrder(Order.asc(order)); }

for(String campo : formfields.keySet()){

String like = "%" + formfields.get(campo) + "%"; criteria.add(Restrictions.ilike(campo, like)); }

return criteria.list(); }

● boolean exists(Membro membro)

public boolean DaoMembro.exists(Membro membro) {

Criteria criteria = Persistence.getInstance().getSession().createCriteria(Membro.class);

criteria.add(Restrictions.eq("email", membro.getEmail())); criteria.add(Restrictions.eq("senha", membro.getSenha()));

return criteria.list().contains(membro); }

Este método foi substituído por um exists:

public boolean exists(T object, String[] restrictions){

Criteria criteria = Persistence.getInstance().getSession().createCriteria(this.classe);

for(String campo : restrictions){

criteria.add(Restrictions.eq(campo,

ReflectionUtil.getFieldValue(object, this.classe, campo))); }

return criteria.list().contains(object); }

No fim, como todos os métodos foram acoplados em Dao, restou apenas em MembroDao o construtor, que continua a instanciar qual Class será gerenciado pelo MembroDao e quais são os atributos utilizados para ordenação.

public class MembroDao extends Dao<Membro> {

public MembroDao() {

super(Membro.class, new String[] {"tipo","nome"}); }

(30)

DaoPublicacao.java

Todos os métodos dessa classe não são mais necessários, uma vez que os métodos generalizados definidos acima já cobrem todas as funcionalidades desta classe. Apenas o construtor persiste, o mesmo informando qual classe terá sua persistencia gerenciada pelo Dao e também indica quais são os atributos que serão utilizados para ordenação.

public class PublicacaoDao extends Dao<Publicacao>{

public PublicacaoDao() {

super(Publicacao.class, new String[] {"titulo","ano"}); }

}

DaoLinhaPesquisa.java

Todos os métodos dessa classe não são mais necessários, uma vez que os métodos generalizados definidos acima já cobrem todas as funcionalidades desta classe. Novamente resta na classe apenas o construtor.

public class LinhaPesquisaDao extends Dao<LinhaPesquisa> {

public LinhaPesquisaDao() {

super(LinhaPesquisa.class, new String[] {"titulo","breveDescricao"}); }

}

DaoNoticia.java

Todos os métodos dessa classe não são mais necessários, uma vez que os métodos generalizados definidos acima já cobrem todas as funcionalidades desta classe. Apenas o construtor continua a existir:

public class NoticiaDao extends Dao<Noticia>{

public NoticiaDao() {

super(Noticia.class, new String[] {"id","autor"}); }

}

Adaptando os formfields para o mapa de restrição mais genérico

(31)

Após a criação dos métodos generalizados, os Controles e alguns aspectos foram alterados para usar os novos métodos.

Situação posterior

Os impactos do refatoramento foram:

● Aumento no reuso de código, uma vez que todos os modelos do sistema utilizam a mesma implementação de Dao para operações de persistencia;

● Generalização da implementação de alguns tipos de consultas, de forma que um conjunto de maior de consultas pode ser realizado utilizando um código de tamanho menor.

(32)

Problema 4 - Pouca flexibilidade da implementação de Controle

Assim como o problema anterior, foi detectada uma oportunidade de reuso de código através da generalização desta classe. A diferença principal entre as classes de controle mais específicas, que são ControleMembro, ControleNoticia, ControlePublicacao e ControleLinhaPesquisa é a implementação de Dao utilizada. A seguir, pode-se ver que as operações realizadas pelo controle são bastante similares.

Situação anterior

(33)

Exemplo de código do clone 53

Valores observados

Os valores observados nesse problema foram:

Flexibilidade: o código de getPublicacao e getNoticia é um tanto rígido, pois realiza uma

(34)

Simplicidade: o fato de existirem múltiplas implementações específicas de Controle torna o

código mais complexo. É interessante verificar se não existe uma abordagem mais simples que possa generalizar o conceito das operações que um Controle realiza sobre um tipo de entidade de negócios arbitrário. A implementação inicial percebeu parte das características em comum e as colocou numa classe estaticamente polimórfica Controle<T>, entretanto abriu mão da generalização em algumas outras operações.

Princípios utilizados

Generalização do código: implementando um Controle que funcione para uma classe de

entidades, não apenas para entidades específicas;

Minimização de repetição: Uma vez que existir apenas uma classe de Controle, todo código

que antes era comum entre as diversas implementações será eliminado, redizindo o número de repetições e de clones significativos encontrados no projeto;

Refatorando o código

O Controle pode ser unificado aplicando-se apenas algumas mudanças em métodos mais específicos de cada tipo, deixando-os mais genéricos. Este refatoramento é bem similar ao aplicado nas classes Dao, do problema anterior. Vamos procurar generalizar o conceito de cada método nos subtipos de Controle:

ControleMembro.java

● Membro getUsuario(String email)

public Membro getUsuario(String email) {

Persistence.getInstance().beginTransaction();

Membro retorno = this.dao.queryOnUniqueField("email", email);

Persistence.getInstance().commit();

return retorno; }

Esta operação pode ser generalizada para searchOnUniqueField(String field, Object value):

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

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

Persistence.getInstance().commit();

(35)

}

● List<Membro> getMembros(HashMap<String, Object> formFields)

public List<Membro> getMembros(HashMap<String, Object> formfields) { Persistence.getInstance().beginTransaction();

List<Membro> retorno = this.dao.queryOnRestrictionMap(formfields);

Persistence.getInstance().commit();

return retorno; }

Neste caso, generalizaremos para list(HashMap<String, Object> restrictions):

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

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

Persistence.getInstance().commit();

return retorno; }

● boolean exists(Membro membro)

public boolean ControleMembro.exists(Membro membro) { Persistence.getInstance().beginTransaction(); Dao<Membro> dao = this.getDao();

boolean retorno = dao.exists(membro, new String[] {"email", "senha"});

Persistence.getInstance().commit();

return retorno; }

Esta operação pode ser generalizada para exists(T value, String[] fields):

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

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

Persistence.getInstance().commit();

(36)

ControlePublicacao.java

Todos os métodos dessa classe não são mais necessários, uma vez que os métodos generalizados definidos acima já cobrem todas as funcionalidades desta classe.

ControleLinhaPesquisa.java

Todos os métodos dessa classe não são mais necessários, uma vez que os métodos generalizados definidos acima já cobrem todas as funcionalidades desta classe.

ControleNoticia.java

Todos os métodos dessa classe não são mais necessários, uma vez que os métodos generalizados definidos acima já cobrem todas as funcionalidades desta classe.

Situação posterior

Os impactos do refatoramento foram:

● Aumento no reuso de código, uma vez que todos os modelos do sistema utilizam a mesma implementação de Controle;

(37)
(38)

Problema 5 - Dependências concretas entre DAO e Controle

O problema existente no sistema é que o Controle está dependente da implementação concreta de um Dao para Hibernate. Na verdade é mais genérico que isso, pois a Facade está dependendo de implementações específicas de Controle.

Situação anterior

Este problema se deve ao fato de o módulo de persistência ser pouco extensível. Pelo fato de ser um problema de modularidade, mostraremos os problemas dos módulos pelo AOPmetrics e SourceMiner. Após as alterações, discutiremos as melhoras que ocorreram registrando novamente as

métricas. A princípio, mostraremos um exemplo do código ilustrando o problema:

public Controle(Class clazz, String[] atributos) { this.dao = new Dao<Tipo>(clazz, atributos); }

AOP metrics para a situação anterior

Ao se fazer as métricas do AOP metrics, percebeu-se que as classes DAO e Controle possuem alto valor de RFM (26 e 29, respectivamente), o que indica que ambos os módulos são responsáveis

(39)

Perceba as dependências entre Controle e DAO em destaque (14)

Valores observados

Flexibilidade: o código é rígido no sentido de não permitir a simples adição de um mecanismo

de persistência diferente do existente.

Imobilidade: pelo fato de o controle estar intimamente ligado a um DAO, o código está

inabilitado a trocar de implementação de DAO facilmente. O motivo da imobilidade se deve ao fato de haver dependências de instanciação, e ligações diretas entre entidades concretas.

Princípios utilizados

Princípio da inversão de dependência (DIP): um dos princípios básicos é a presença de

dependências em entidades abstratas, sejam elas classes abstratas ou interfaces, de modo que possam ser estendidas por possíveis implementações. Esse princípio se deve em função de outro em que uma superclasse deve poder ser substituída por suas subclasses.

Definição de pontos de extensão: as classes de DAO representam um tipo de

implementação bastante específica de DAO. O código é pouco oportuno à definição de novas implementações de DAO e ligação com as demais camadas.

Princípio da dependência de estabilidade (SDP): será explicado no final.

Refatorando o código

(40)

Possibilitando implementações distintas de DAO

A classe Dao foi renomeada com o Refactoring Rename para HibernateDao, uma vez que

representa um DAO que utiliza Hibernate como mecanismo de persistência. Dessa forma, pode-se utilizar a técnica Extract Interface na HibernateDao para criar um Dao genérico.

(41)

Possibilitando implementações distintas de Controle

Foi utilizado um processo similar no qual se extraiu a interface Controle e a implementação AbstractControle. Como na prática o Controle representa um Manager no projeto e os verdadeiros

controladores estão nos Servlets, a partir de agora tais classes serão usadas como IManager e ManagerImpl.

Possibilitando a troca de implementação de Dao e Controle

Para isso foi usada a técnica de injeção de dependência com o Spring. Com o exemplo abaixo, pode-se observar como isso ocorre:

PublicacaoHibernateDao.java

@Service(value = "PublicacaoHibernateDao")

public class PublicacaoHibernateDao extends HibernateDao<Publicacao>{

public PublicacaoHibernateDao() {

super(Publicacao.class, new String[] {"titulo","ano"}); }

}

PublicacaoManager.java

@Service(value = "PublicacaoManager")

public class PublicacaoManager extends ManagerImpl<Publicacao>{

@Autowired @Override

public void setDao(@Qualifier("PublicacaoHibernateDao") Dao<Publicacao> dao) { super.setDao(dao);

} }

A troca de implementação de Dao pode ser feita com a mudança do Qualifier no manager desejado. Perceba que agora nenhum Manager instancia um Dao, muito menos referencia qualquer classe concreta que seja subtipo de Dao.

Resolvendo a Facade

(42)

public class Facade {

private static Facade singleton;

private IManager<Membro> controleMembro;

private IManager<Publicacao> controlePublicacao;

private Facade() {

this.controleMembro = new MembroManager();

this.controlePublicacao = new PublicacaoManager(); }

public static Facade getInstance(){ if(singleton == null){

singleton = new Facade(); }

return singleton; }

... }

Resolveremos aplicando a mesma técnica:

@Service(value = "Facade") public class Facade {

private static ApplicationContext context;

private static Facade singleton;

private IManager<Membro> managerMembro;

private IManager<Publicacao> managerPublicacao;

public static Facade getInstance(){ if(singleton == null){

Facade.context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); singleton = (Facade) Facade.context.getBean("Facade");

}

return singleton; }

@Autowired

public void setManagerMembro(@Qualifier(value = "MembroManager") IManager<Membro> manager) { this.managerMembro = manager;

}

@Autowired

public void setManagerPublicacao(@Qualifier(value = "PublicacaoManager") IManager<Publicacao> manager) { this.managerPublicacao = manager;

}

(43)

Trocando também os point cuts de alguns aspectos como NoticiasAspect.aj para:

@Autowired

public void Facade.setManagerNoticia(@Qualifier(value="NoticiaManager") IManager<Noticia> manager){ this.managerNoticia = manager;

}

Situação posterior

As duas dependências Facade-Manager e Manager-DAO agora envolvem interfaces

Aplicando o princípio da dependência em pacotes estáveis

Ao final, temos o pacote br.ufpe.cin.rgms.base com quatro classes IManager, ManagerImpl,

(44)

Se prestarmos atenção na métrica A, vemos que o pacote base é bem abstrato (A = 0,75), mas

a métrica Ce (acoplamento eferente) é alto, o que o torna mais instável do que o desejado (I = 0,4).

Precisamos alterá-lo para reduzir sua instabilidade, de forma que os outros pacotes que dependam do pacote base estejam ligados a um pacote estável (obedecendo ao princípio de estabilidade).

Percebendo que o que gera a instabilidade no pacote base são as implementações de Manager e Dao, criaremos dois pacotes:

br.ufpe.cin.rgms.base.persistencia - Este pacote conterá as implementações de Dao

genéricas, como a classe HibernateDao. Dessa forma, todos as implementações de HibernateDao (como MembroHibernateDao, PublicacaoHibernateDao) dependerão apenas deste pacote.

br.ufpe.cin.rgms.base.negocio - Este pacote conterá as implementações de Manager

genéricas, como a classe ManagerImpl. Dessa forma, todos as implementações de ManagerImpl (como MembroManager, PublicacaoManager) dependerão apenas deste pacote.

Temos agora um pacote br.ufpe.cin.rgms.base bastante estável (I = 0,166667) com um

pequeno aumento do Dn, de modo que as novas implementações de Controle e Dao que surgirem dependentes do pacote base estarão dependentes de um pacote estável.

Os dois novos pacotes, embora sejam instáveis, não são tão instáveis para seu grau de abstração (Dn ~= 0,2). Isso ainda se torna menos relevante se levarmos em consideração que as

classes de tais pacotes são genéricas e portanto podem ser reusadas para muitas funcionalidades.

Os impactos do refatoramento foram:

● Remoção da dependência entre entidades concretas;

● Definição de um mecanismo que permite a alteração da camada de persistência sem ter que alterar as demais camadas, obedecendo o Open Closed Principle;

(45)

Problema 6 - Complexidade dos métodos doPost nos Servlets

A complexidade dos métodos doPost em vários Servlets é muito alta. Percebe-se um padrão de repetição em alguns Servlets que consiste em obter um objeto do banco de dados, alterar vários atributos de acordo com a requisição e atualizá-lo.

Situação anterior

TreeMap da situação anterior: destacam-se os métodos doPost pretos isolados em alguns Servlets

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

try {

this.extractFields(request);

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

String sobrenome = (String) this.getFormfields().get("sobrenome"); String universidade = (String) this.getFormfields().get("universidade"); String departamento = (String) this.getFormfields().get("departamento"); String tipo = (String) this.getFormfields().get("vinculo");

String situacao = (String) this.getFormfields().get("situacao"); String email = (String) this.getFormfields().get("email");

String orientador = (String) this.getFormfields().get("orientador"); String coOrientador = (String) this.getFormfields().get("coorientador"); String website = (String) this.getFormfields().get("website");

(46)

String emailOriginal = (String) this.getFormfields().get("emailoriginal");

Membro membroParaAlterar = Facade.getInstance().getMembro(emailOriginal);

if(!membroParaAlterar.getEmail().equals(email)

&& Facade.getInstance().getMembro(email) != null){ request.setAttribute("membrostatus",

"O e-mail fornecido pertence a outro membro cadastrado no sistema."); }else{ membroParaAlterar.setEmail(email); membroParaAlterar.setNome(nome); membroParaAlterar.setSobrenome(sobrenome); membroParaAlterar.setTipo(tipo); membroParaAlterar.setDepartamento(departamento); membroParaAlterar.setUniversidade(universidade); membroParaAlterar.setTelefone(telefone); membroParaAlterar.setWebsite(website); membroParaAlterar.setCidade(cidade); membroParaAlterar.setPais(pais); membroParaAlterar.setSituacao(situacao);

if(foto != null){

membroParaAlterar.setFoto(foto); }

if(membroParaAlterar instanceof Estudante){

Estudante estudanteParaAlterar = (Estudante) membroParaAlterar;

estudanteParaAlterar.setOrientador(orientador); estudanteParaAlterar.setCoOrientador(coOrientador); Facade.getInstance().alterarMembro(estudanteParaAlterar); }else{ Facade.getInstance().alterarMembro(membroParaAlterar); }

request.setAttribute("membrostatus", "Membro alterado com sucesso."); }

} catch (FileUploadException e1) {

// TODO Auto-generated catch block e1.printStackTrace();

request.setAttribute("membrostatus", "Erro no banco de dados."); } catch (RGMSException e) {

// TODO Auto-generated catch block e.printStackTrace();

request.setAttribute("membrostatus", "Erro no upload para o banco de dados."); }

RequestDispatcher view = request.getRequestDispatcher("membrostatus.jsp"); view.forward(request, response);

(47)

Valores observados

Os valores observados foram:

Simplicidade: o tamanho do método doPost em vários Servlets torna difícil de compreender o

que o código está tratando;

Legibilidade: o tamanho do método também dificulta a legibilidade do mesmo;

Princípios utilizados

Os princípios utilizados foram:

Generalização do código: extrair a parte comum do doPost de modo a permitir que o mesmo

código seja utilizado para várias entidades;

Minimização de repetição: extraindo parte do código, o mesmo pode ser reutilizado pelos

Servlets que tenham código semelhantes;

Utilização de expressões declarativas: que permitem o fácil entendimento do que está sendo

feito no método;

Refatorando o código

Vamos definir um Servlet genérico que representa um Servlet que altera os dados de uma entidade arbitrária T (podendo até ser subtipos de T):

RGMSAlterarDadosServlet.java

Utilizando Template Method, a classe ficou definida da seguinte forma:

public abstract class RGMSAlterarDadosServlet<T extends AbstractBusinessEntity> extends RGMSUploadFormServlet {

protected abstract T getObject();

protected abstract void alterar(T alterado) throws RGMSException;

protected abstract void handleError(HttpServletRequest request, HttpServletResponse response, Exception erro);

protected abstract void redirect(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;

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

(48)

properties.load(clazz.

getResourceAsStream(String.format("%s.formfields.properties", clazz.getSimpleName()))); properties.load(clazz.

getResourceAsStream(String.format("%s.fieldmap.properties", clazz.getSimpleName())));

clazz = object.getClass();

String[] fields = properties.getProperty(clazz.getName()).split(",");

for(String field : fields){

ReflectionUtil.setField(this.getFormfields().get(field),

object.getClass(), clazz, properties.getProperty(field)); }

}

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

try {

this.extractFields(request);

T alterar = this.getObject();

this.setObject(alterar);

this.alterar(alterar);

this.handleSuccess(request, response); } catch (Exception e) {

this.handleError(request, response, e); }

this.redirect(request, response); }

}

Utiliza-se dois arquivos Properties, como mostrado abaixo:

AlterarDadosMembroServlet.java

public class AlterarDadosMembroServlet extends RGMSAlterarDadosServlet<Membro> {

@Override

protected Membro getObject() {

String emailOriginal = (String) this.getFormfields().get("emailoriginal"); return Facade.getInstance().getMembro(emailOriginal);

}

@Override

protected void alterar(Membro alterado) throws RGMSException { Facade.getInstance().alterarMembro(alterado);

}

@Override

protected void setObject(Membro object) throws IOException { super.setObject(object);

(49)

if(foto != null){

object.setFoto(foto); }

}

@Override

protected void handleError(HttpServletRequest request, HttpServletResponse response, Exception erro) { request.setAttribute("membrostatus", "Erro no banco de dados.");

}

@Override

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

request.setAttribute("membrostatus", "Membro alterado com sucesso."); }

@Override

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

RequestDispatcher view = request.getRequestDispatcher("membrostatus.jsp"); view.forward(request, response);

}

}

com os seguintes arquivo properties:

AlterarDadosMembroServlet.formfields.properties

Define todos os campos de formulário a serem atualizados no objeto em correspondência com a classe do objeto que será alterado.

br.ufpe.cin.rgms.membro.modelo.Membro= nome,sobrenome,departamento,vinculo,universidade,telefone,website,cidade,pais,email,situacao br.ufpe.cin.rgms.membro.modelo.Estudante= nome,sobrenome,departamento,vinculo,universidade,telefone,website,cidade,pais,email,situacao,orientador,co orientador AlterarDadosMembroServlet.fieldmap.properties

Mapeia cada campo de formulário a um campo da classe do objeto a ser alterado:

(50)

orientador=orientador coorientador=coOrientador

Analogamente o mesmo foi feito com as demais classes AlterarDadosPublicacaoServlet, AlterarDadosNoticiasServlet e AlterarDadosLinhaPesquisaServlet.

Vamos definir agora um Servlet genérico que representa um Servlet que insere um objeto de uma entidade arbitrária T (podendo até ser subtipos de T):

RGMSInserirServlet.java

Utilizando Template Method, a classe ficou definida da seguinte forma:

public abstract class RGMSInserirServlet<T extends AbstractBusinessEntity> extends RGMSUploadFormServlet {

protected abstract void inserir(T object);

protected abstract void handleError(HttpServletRequest request, HttpServletResponse response, Exception erro);

protected abstract void redirect(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;

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

protected abstract T createObject();

protected void setObject(T object) throws IOException{ Properties properties = new Properties();

Class clazz = this.getClass(); properties.load(clazz. getResourceAsStream(String.format("%s.formfields.properties", clazz.getSimpleName()))); properties.load(clazz. getResourceAsStream(String.format("%s.fieldmap.properties", clazz.getSimpleName())));

clazz = object.getClass();

String[] fields = properties.getProperty(clazz.getName()).split(",");

for(String field : fields){

ReflectionUtil.setField(this.getFormfields().get(field),

object.getClass(), clazz, properties.getProperty(field)); }

}

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

try {

this.extractFields(request);

T object = this.createObject();

this.setObject(object); this.inserir(object);

(51)

} catch (Exception e) {

this.handleError(request, response, e); } this.redirect(request, response); } } AdicionarMembroServlet.java

public class AdicionarMembroServlet extends RGMSInserirServlet<Membro> {

@Override

protected Membro createObject() { Membro ret;

if(this.getFormfields().get("vinculo").equals("Estudante")){ ret = new Estudante();

} else{

ret = new Membro(); }

return ret; }

@Override

protected void inserir(Membro object) throws RGMSException { Facade.getInstance().inserirMembro(object);

}

@Override

protected void handleError(HttpServletRequest request, HttpServletResponse response, Exception erro) { request.setAttribute("membrostatus", "Erro no banco de dados.");

}

@Override

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

request.setAttribute("membrostatus", "Membro inserido com sucesso."); }

@Override

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

RequestDispatcher view = request.getRequestDispatcher("membrostatus.jsp"); view.forward(request, response);

} }

(52)

AdicionarMembroServlet.formfields.properties

Define todos os campos de formulário a serem atualizados no objeto em correspondência com a classe do objeto que será alterado.

br.ufpe.cin.rgms.membro.modelo.Membro= nome,sobrenome,departamento,vinculo,universidade,telefone,website,cidade,pais,email,situacao br.ufpe.cin.rgms.membro.modelo.Estudante= nome,sobrenome,departamento,vinculo,universidade,telefone,website,cidade,pais,email,situacao,orientador,co orientador AdicionarMembroServlet.fieldmap.properties

Mapeia cada campo de formulário a um campo da classe do objeto a ser alterado:

nome=nome sobrenome=sobrenome departamento=departamento vinculo=tipo universidade=universidade telefone=telefone website=website cidade=cidade pais=pais email=email situacao=situacao orientador=orientador coorientador=coOrientador

Analogamente o mesmo foi feito com as demais classes AdicionarPublicacaoServlet, AdicionarNoticiasServlet e AdicionarLinhaPesquisaServlet.

Observando as duas classes abstratas criadas neste refatoramento, RGMSAlterarDadosServet e RGMSInserirServlet, vemos que quase todos os métodos abstratos são em comum com ambas as classes, e os métodos concretos são bastante semelhantes, sendo o setObject(T object) idêntico nas duas implementações. Analisando o significado semântico destes Servlets, vemos que ambos lidam com a apresentação de objetos que estendem AbstractBusinessEntity em páginas html. Neste caso especial a apresentação ainda lida com entradas de um formfield, mapeando o objeto com o formulário da página.

Para melhorar este refatoramento, foi aplicado o método Extract Superclass na classe

(53)

Extract superclass

Manualmente, foi inserido o método abstrato handleObject(T object), que lida com o objeto de acordo com a finalidade do Servlet. No caso do RGMSAlterarDadosServlet, o handleObject chama a

Facade para alterar o valor de um objeto mapeado pelo Servlet. No caso do RGMSInserirServlet, o

handleObject chama a Facade para inserir um nodo dado no banco de dados.

AbstractModelMapperServlet.java

public abstract class AbstractModelMapperServlet<T extends AbstractBusinessEntity> extends RGMSUploadFormServlet {

public AbstractModelMapperServlet() { super();

}

(54)

protected void setObject(T object) throws IOException { Properties properties = new Properties();

Class clazz = this.getClass(); properties.load(clazz.getResourceAsStream(String.format("%s.formfields.properties", clazz.getSimpleName()))); properties.load(clazz.getResourceAsStream(String.format("%s.fieldmap.properties", clazz.getSimpleName())));

clazz = object.getClass();

String[] fields = properties.getProperty(clazz.getName()).split(",");

for(String field : fields){

ReflectionUtil.setField(this.getFormfields().get(field), object, clazz, properties.getProperty(field)); }

}

protected abstract void handleObject(T object) throws RGMSException;

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

try {

this.extractFields(request);

T alterar = this.getObject();

this.setObject(alterar);

this.handleObject(alterar);

this.handleSuccess(request, response); } catch (Exception e) {

this.handleError(request, response, e); }

this.redirect(request, response); }

protected abstract void handleError(HttpServletRequest request, HttpServletResponse response, Exception erro);

protected abstract void redirect(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;

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

}

Com essa nova classe, RGMSAlterarDadosServlet e RGMSInserirDadosServlet não são

mais necessárias. Apenas essa classe será estendida para cada entidade unificando os métodos em comum entre o Inserir e Alterar, como pode ser visto abaixo:

PublicacaoModelMapperServlet.java

(55)

@Override

protected void processFormField(FileItem item) { if (item.isFormField()) {

String name = item.getFieldName();

if(name.equals("autoresmembros")){ String value = item.getString();

List<String> naoMembros = ListGenerator.createListaNaoMembro(value);

this.getFormfields().put(name, naoMembros); }else if(name.equals("autoresnaomembros")){

String value = item.getString();

List<Membro> membros = ListGenerator.createListaMembro(value);

this.getFormfields().put(name, membros); }else{

super.processFormField(item); }

} }

@Override

protected void redirect(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException { RequestDispatcher view = request.getRequestDispatcher("publicacaostatus.jsp"); view.forward(request, response);

} }

Situação posterior

(56)

Polymetric da situação posterior

Gráfico dos clones da situação posterior com somente 16 clone sets

(57)

Depois de aplicado este refatoramento, podem-se notar os seguintes resultados:

● Maior simplicidade no código do doPost de vários Servlets, uma vez que o processamento foi dividido em vários métodos com fins específicos;

● Maior legibilidade dos métodos refatorados, com o uso de expressões declarativas para realizar a funcionalidade;

● Separação de concerns, que antes eram tratados em um mesmo método e agora estão em métodos separados do Servlet;

● Remoção de código de validação no Servlet, passando-o para a Facade.

● Generalização do código, que agora recebendo as informações de campos e nomes de atributos de arquivos properties pode ser utilizado para mais de uma classe básica;

● Reuso de código entre os Servlets que faziam a mesma operação, através da generalização do mesmo;

● Facilidade de modificação, uma vez que mudança em htmls de formulários ou em classes básicas exigem apenas mudanças em arquivos .properties, e não no código do Servlet;

(58)

Problema 7 - Complexidade de código no BibtexAspect

O código dos métodos createBibTex é complexo em cada subtipo de Publicacao:

Situação anterior

TreeMap da situação anterior: no centro da imagem temos o BibTexAspect com um retângulo dividido em três partes, sendo uma em cor vinho e duas em cor marrom.

privileged public aspect BibtexAspect {

public abstract File Publicacao.createBibTex();

//N‹o consegui colocar o annotation @Override public File ArtigoConferencia.createBibTex(){

File bib = new File(this.getTitulo()+".bib");

String autoresMembros = (this.listaAutoresMembros()!=null)? this.listaAutoresMembros(): ""; String autoresNaoMembros =

(this.listaAutoresNaoMembros()!=null)? this.listaAutoresNaoMembros():"";

try{

bib.createNewFile(); bib.setWritable(true);

FileWriter writer = new FileWriter(bib); PrintWriter saida= new PrintWriter(writer);

saida.println("@conference{"+this.getTitulo().trim()+",");

saida.println("author = \""+autoresMembros+" "+autoresNaoMembros+"\","); saida.println("title = \""+this.getTitulo()+"\",");

saida.println("booktitle = \""+this.getConferencia()+"\","); saida.println("year = \""+this.getAno()+"\",");

(59)

saida.close(); writer.close(); }catch(IOException error){ error.getMessage(); } return bib; } ... }

Valores observados

Simplicidade: o código embora realize operações simples não parece facilmente

compreensível em razão do seu tamanho;

Flexibilidade: o código não define nenhuma base em comum para extensões de BibTex em

outras entidades.

Princípios utilizados

Minimização de repetições: reduzindo assim o número de clones e deixando o código mais

legível;

Generalização de código: permitindo que os procedimentos que manipulam com o BibTex

possam definir uma base para outras entidades que possam também utilizá-lo.

Refatorando o código

Generalizou-se o método de escrita de arquivo BibTex utilizando Template Method, de forma

que o resultado foi:

privileged public aspect BibtexAspect {

public File Publicacao.createBibTex(){

return this.writeBibTex(this.getEntityTag(), this.getEntries()); }

public HashMap<String, Object> Publicacao.getEntries(){

String autoresMembros = (this.listaAutoresMembros()!=null)? this.listaAutoresMembros(): ""; String autoresNaoMembros =

(this.listaAutoresNaoMembros()!=null)? this.listaAutoresNaoMembros():""; HashMap<String,Object> entries = new HashMap<String, Object>();

entries.put("author", autoresMembros+" "+autoresNaoMembros); entries.put("title", this.getTitulo());

(60)

return entries; }

public abstract String Publicacao.getEntityTag();

private File Publicacao.writeBibTex(String entity, HashMap<String, Object> table){ File bib = null;

try{

bib = new File(this.getTitulo()+".bib"); bib.createNewFile();

bib.setWritable(true);

FileWriter writer = new FileWriter(bib); PrintWriter saida= new PrintWriter(writer);

saida.println(entity + "{"+this.getTitulo().trim()+",");

for(String key : table.keySet()){

saida.println(key + " = \"" + table.get(key) +"\","); } saida.println("}"); saida.close(); writer.close(); }catch(IOException e){ e.printStackTrace(); } return bib; }

public String ArtigoConferencia.getEntityTag(){ return "@conference";

}

public HashMap<String, Object> ArtigoConferencia.getEntries(){ HashMap<String,Object> entries = super.getEntries(); entries.put("booktitle", this.getConferencia());

return entries; }

public String ArtigoPeriodico.getEntityTag(){ return "@article";

}

public HashMap<String, Object> ArtigoPeriodico.getEntries(){ HashMap<String,Object> entries = super.getEntries(); entries.put("journal", this.getJornal());

return entries; }

public String PublicacaoPosGraduacao.getEntityTag(){ String tag = "@phdthesis";

(61)

tag = "@mastersthesis"; }

return tag; }

public HashMap<String, Object> PublicacaoPosGraduacao.getEntries(){ HashMap<String,Object> entries = super.getEntries(); entries.put("school", this.getUniversidade());

return entries; }

}

Situação posterior

(62)

Gráfico dos clones da versão final

Os impactos do refatoramento foram:

● Redução da complexidade dos métodos do BibTex;

(63)

Reorganização dos pacotes de acordo com os concerns

Como foi comentado ao longo dos problemas, os pacotes não representavam os concerns adequadamente. Após as modificações, o novo esquema foi o seguinte:

Esquema dos pacotes após refatoramento

(64)

Common Closure Principle: uma vez que as classes referentes a cada elemento diferente

(Membro, Publicação, Linha de Pesquisa e Notícia) estão agrupadas em um mesmo pacote, e dentro deste pacote as classes relacionadas

Stable Dependencies Principle: as dependências das classes concretas são no sentido da

estabilidade, graças à reorganização feita no pacote base de forma a aumentar a estabilidade. Graças a isso os pacotes concretos membro e publicação dependem de pacotes estáveis.

Stable Abstractions Principle: o pacote estável base (I = 0,166667) tem um grau de

(65)

Testes

É necessário a criação de um conjunto de testes significativo que possa garantir, com uma certa segurança, que o comportamento do sistema não foi alterado depois da aplicação destas técnicas. O conjunto de testes construído visa cobrir algumas das possíveis entradas do sistema de modo a executar principalmente os trechos modificados no refatoramento.

Teste de funcionalidades

Para testar o funcionamento das camadas mais internas do sistema, a classe GerarBanco foi testada, utilizando a inserção de todas as entidades (descomentando as linhas que estavam comentadas de algumas entidades).

Essa parte foi prejudicada pelo fato de algumas funcionalidades não funcionarem corretamente na versão antes do refatoramento. Alguns erros de codificação foram encontrados, corrigidos, mas alguns testes foram planejados (sendo alguns deles já executados):

1. Todos os servlets de alteração e inserção de dados: a. Membro (inserir / alterar)

b. Publicacao (inserir / alterar) c. Noticia (inserir / alterar)

(66)

Exemplo de página do projeto

Imagem

Gráfico dos clones após o refatoramento
Gráfico dos clones após as alterações
Gráfico dos clones da situação posterior: sem o clone set alvo do refatoramento
Gráfico dos clones da situação posterior com somente 16 clone sets
+2

Referências

Documentos relacionados

2. Identifica as personagens do texto.. Indica o tempo da história. Indica o espaço da história. Classifica as palavras quanto ao número de sílabas. Copia do texto três

1- A vida das comunidades recoletoras era muito difícil, devido ao frio intenso e aos animais ferozes, mas também porque era difícil encontrar comida e lugares onde se abrigarem.. 2-

Um senhorio é um território, pertencente a um Senhor (do Clero ou da Nobreza), em que podemos encontrar terras cultivadas pelos camponeses que vivem no senhorio,

Em janeiro, o hemisfério sul recebe a radiação solar com menor inclinação e tem dias maiores que as noites, encontrando-se, assim, mais aquecido do que o hemisfério norte.. Em julho,

6 Consideraremos que a narrativa de Lewis Carroll oscila ficcionalmente entre o maravilhoso e o fantástico, chegando mesmo a sugerir-se com aspectos do estranho,

O desenvolvimento das interações entre os próprios alunos e entre estes e as professoras, juntamente com o reconhecimento da singularidade dos conhecimentos

Vários trabalhos têm demonstrado que a mortali- dade por síndrome ascítica é maior nos machos do que nas fêmeas, acreditando-se que este fato esteja relacionado com a maior

O empregador deverá realizar a avaliação ambiental de poeira de asbesto nos locais de trabalho em intervalos não superiores a seis meses.. Os registros das avaliações deverão