UNIVERSIDADE TECNOLÓGICA FEDERAL DO PARANÁ
CAMPUS PONTA GROSSA
CURSO SUPERIOR EM BACHARELADO EM CIÊNCIA DA
COMPUTAÇÃO
Grupo de Pesquisa em Sistemas de Informação - Linhas de
Pesquisa em Engenharia de Software - GPSI
Projeto: Refatoração do FrameMK
Elaborado por: Luma Alves Lopes
[email protected]
Víctor Pedroso Ambiel Barros
[email protected]
Relatório do GPSI
Relatório referente ao estudo sobre a técnica de refatoração para framework.
Realizado no período de 30 de Agosto de 2013 a 20 de Setembro de 2013.
Os assuntos abordados neste relatório foram baseados em Opdyke [1].
Preservar o comportamento durante a refatoração
1. Propriedades do programa de preservação e comportamento
Depois que uma refatoração é aplicada, o programa deve estar sintaticamente
correto, pois não há mudanças no funcionamento de suas operações. Por exemplo, na
subclasse ao redefinir uma função declarada na superclasse, é necessário manter o
comportamento interno das duas classes. O compilador pode detectar este erro, logo,
uma solução é salvar a versão atual do programa antes de cada refatoração ser aplicada,
pois se a refatoração levar a um erro, basta voltar à última versão salva do programa.
O artigo cita dois grandes problemas para essa abordagem. O primeiro é que a
abordagem pode ser lenta, tendo em vista operações que necessitam de refatorações de
alto nível que envolve refatorações primitivas. A segunda, que é mais importante ainda
que a primeira, é que existem alguns erros que podem alterar o comportamento do
programa, mas não são identificados pelo compilador, e desta forma, passam
despercebidos.
Na Figura 1, a função F1 é um membro protegido na classe Super, enquanto F2
é definida na classe Sub1, umas das suas subclasses. O argumento e retorno de F2 são
os mesmos de F1. Na classe Sub1, a função F3 inclui uma chamada a função F1, que é
herdada da superclasse.
A refatoração é aplicada na classe Sub1, para mudar o nome da função F2 para
F1. No entanto, o comportamento do programa provavelmente também muda, o que não
deveria acontecer.
1
UNIVERSIDADE TECNOLÓGICA FEDERAL DO PARANÁ
CAMPUS PONTA GROSSA
CURSO SUPERIOR EM BACHARELADO EM CIÊNCIA DA
COMPUTAÇÃO
Grupo de Pesquisa em Sistemas de Informação - Linhas de
Pesquisa em Engenharia de Software - GPSI
Projeto: Refatoração do FrameMK
Elaborado por: Luma Alves Lopes
[email protected]
Víctor Pedroso Ambiel Barros
[email protected]
Figura 1 - Renomeação errônea dos membros da função F2 - Erroneous Renaming
Of Member Function. Fonte: Opkney [1, p. 38]
A função F3 é definida na classe local, não é uma função previamente herdada
da superclasse, o que seria o correto. Se as duas funções se comportam de maneira
diferentes, o comportamento da função F3 teria mudado e o compilador não detectaria
esse erro.
Durante a criação de protótipos de pesquisa, um conjunto particular de
propriedades sintáticas e semânticas de programas foram encontradas para ser
facilmente violadas se as verificações não forem feitas antes de um programa ser
refatorado. Estas propriedades referem-se a herança, escopo, tipo de compatibilidade e
equivalência semântica. As propriedades citadas no artigo são:

Superclasse única: depois da refatoração, uma classe sempre tem no máximo
uma superclasse e sua superclasse também não deve ser uma de suas subclasses.

Nome de classes distintas: depois da refatoração, cada classe deve ter um nome
único.
2
UNIVERSIDADE TECNOLÓGICA FEDERAL DO PARANÁ
CAMPUS PONTA GROSSA
CURSO SUPERIOR EM BACHARELADO EM CIÊNCIA DA
COMPUTAÇÃO
Grupo de Pesquisa em Sistemas de Informação - Linhas de
Pesquisa em Engenharia de Software - GPSI
Projeto: Refatoração do FrameMK
Elaborado por: Luma Alves Lopes
[email protected]
Víctor Pedroso Ambiel Barros
[email protected]

Os nomes distintos: depois da refatoração, todas as variáveis-membro e funções
dentro de uma classe tem nomes distintos. No entanto, não é permitido que uma
função-membro de uma superclasse seja substituída em uma subclasse.

Variáveis-membro herdadas: a variável membro herdada de uma superclasse não
é redefinida em qualquer uma de suas subclasses.

Assinaturas compatíveis nos estados de redefinição da função: depois da
refatoração, se um membro da função definida em uma superclasse é redefinida
em uma subclasse, todos os atributos (exceto o corpo da função) das duas
funções devem ser compatíveis.

Atribuições de tipos de seguro: depois de uma refatoração, o tipo de cada
expressão atribuída a variável deve ser uma instância do tipo definido na
variável.
2. Refatoração por especialização: subclasses e simplificação de condicionais
Pode-se melhorar uma classe grande e complexa dividindo-a em classes
menores. Isso ocorre geralmente quando uma classe complexa representa uma abstração
geral e contempla vários casos concretos que podem ser especializados. A
decomposição da classe pode ser realizada com base na análise do conjunto de flags,
tags e das suas instruções condicionais. Para cada condição, uma subclasse é criada
satisfazendo a sua abstração.
Especializar uma classe e simplificar condicionais envolve várias etapas:
1 – Escolher uma condicional cuja condição sugere a criação de subclasse. A escolha da
condicional pode ser com destaque aquelas que aparecem com mais frequência.
2 – Para cada condição criar uma subclasse com uma classe invariante.
3 – Copiar a condicional para cada subclasse, e nas subclasses simplificá-las com base
na invariável que é verdade para a subclasse.
3
UNIVERSIDADE TECNOLÓGICA FEDERAL DO PARANÁ
CAMPUS PONTA GROSSA
CURSO SUPERIOR EM BACHARELADO EM CIÊNCIA DA
COMPUTAÇÃO
Grupo de Pesquisa em Sistemas de Informação - Linhas de
Pesquisa em Engenharia de Software - GPSI
Projeto: Refatoração do FrameMK
Elaborado por: Luma Alves Lopes
[email protected]
Víctor Pedroso Ambiel Barros
[email protected]
4 – Especializar as expressões que criam as instâncias da SuperClasse, substituindo-a
por uma expressão que crie a instância de uma subclasse.
2.1. Classes Invariantes (constantes)
Essa classe é um predicado cujas variáveis livres são as variáveis-membro da
classe.
Ela expressa restrições de consistências gerais que se aplicam a todas as
instâncias de uma classe.
Para que a classe esteja correta se deve:

Criar um procedimento para a classe produzir um estado que satisfaça a classe
invariante.

A classe deve permanecer invariável até o restante do tempo de vida de cada
instância da classe.
Otimizações locais se caracterizam como sendo alterações aplicadas aos
segmentos de código.
Como por exemplo, avaliar expressões constantes de
antecedência e eliminar instruções inúteis.
Otimizações globais lidam com o fluxo de controle entre os blocos de base. As
refatorações que simplificam condicionais podem ser pensadas como uma espécie de
otimização global.
Para aperfeiçoar um programa ou procedimento, torna-se necessário
determinar os conjuntos de variáveis lidos e atualizados por ele. Entretanto, isso deve
ser analisado para que a otimização seja segura, sendo a análise dos efeitos de uma
ligação dos dados denominado Interprocedural da análise de fluxo.
A Interprocedural análise de Fluxo de dados é um procedimento ou
subprograma que pode ser representado como um grafo de fluxo de dados de blocos de
base, onde as arestas direcionadas representam o fluxo de controle dentro do
procedimento, cada nó representa um procedimento e arcos direcionados às chamadas
entre os procedimentos.
4
UNIVERSIDADE TECNOLÓGICA FEDERAL DO PARANÁ
CAMPUS PONTA GROSSA
CURSO SUPERIOR EM BACHARELADO EM CIÊNCIA DA
COMPUTAÇÃO
Grupo de Pesquisa em Sistemas de Informação - Linhas de
Pesquisa em Engenharia de Software - GPSI
Projeto: Refatoração do FrameMK
Elaborado por: Luma Alves Lopes
[email protected]
Víctor Pedroso Ambiel Barros
[email protected]
Alguns problemas de fluxo de dados podem ser resolvidos utilizando a análise
de fluxo para frente, seguindo a direção do fluxo de um programa, outros, entretanto,
são resolvidos analisando o fluxo no sentido oposto.
Poderá haver casos onde um predicado é uma classe invariante, mas não pode
ser verificado por meio da análise de fluxo de dados. O que torna esses predicados
difíceis de validar é que cada cláusula garante um valor de uma variável de membro e
essa variável pode mudar de valor ao longo do tempo.
Figura 2 – Exemplo de como uma classe invariante pode ser usado para simplificar uma
condicional. Fonte: Opkney [1, p. 39]
Neste exemplo, o predicado atribuído como classe invariante do
CondJumpFlowNode é final_statement==conditional_jump especificado em termos de
5
UNIVERSIDADE TECNOLÓGICA FEDERAL DO PARANÁ
CAMPUS PONTA GROSSA
CURSO SUPERIOR EM BACHARELADO EM CIÊNCIA DA
COMPUTAÇÃO
Grupo de Pesquisa em Sistemas de Informação - Linhas de
Pesquisa em Engenharia de Software - GPSI
Projeto: Refatoração do FrameMK
Elaborado por: Luma Alves Lopes
[email protected]
Víctor Pedroso Ambiel Barros
[email protected]
um valor fixo. Esta invariante pode ser utilizada para simplificar a expressão
condicional na função otimizar, removendo o teste condicional.
2.2. Criar subclasses e atribuir invariantes
O primeiro passo é criar novas subclasses e atribuir invariantes de classe. Os
argumentos para essa refatoração são:

A classe sendo especializada

Valores das variáveis membros são enumerados para utilizá-las na criação de
subclasses.
Para cada valor enumerado:
1 - Chamar uma classe vazia de refatoração para criar uma subclasse, com o nome
correspondente ao valor enumerado.
2 - Chamar a criação da função membro para adicionar um construtor para a subclasse.
O construtor irá estabelecer condições iniciais implicadas pela classe invariante.
3 - Atribuir um predicado para a classe como a classe invariante.
Para aplicar essa refatoração, nenhum dos nomes dos valores enumerados
podem coincidir com os existentes e nem com o nome das funções- membro da
superclasse.
2.3. Usar uma classe invariável para simplificar uma condicional
O algoritmo consiste em:

Reduzir verdadeiro ou falso para cada condição de uma condicional com base em
uma classe invariante.

Eliminar código não utilizado e executar outras simplificações.
2.4. Migrar e Simplificar condicionais
6
UNIVERSIDADE TECNOLÓGICA FEDERAL DO PARANÁ
CAMPUS PONTA GROSSA
CURSO SUPERIOR EM BACHARELADO EM CIÊNCIA DA
COMPUTAÇÃO
Grupo de Pesquisa em Sistemas de Informação - Linhas de
Pesquisa em Engenharia de Software - GPSI
Projeto: Refatoração do FrameMK
Elaborado por: Luma Alves Lopes
[email protected]
Víctor Pedroso Ambiel Barros
[email protected]
As declarações são separadas em novas funções, que são copiadas e
simplificadas para as subclasses.
Separa a condicional numa função independente, copiando-a para as subclasses
e simplificando-as com base na classe invariante, simplificando a instrução condicional.
Após verificar a condição, a refatoração consiste em:
1 - Chamar o segmento do código convertido para separar a condicional em uma função
separada, atribuindo um nome exclusivo.
2 - Construir o conjunto de todas as funções-membro e variáveis referenciadas pela
condicional. Para cada membro, mudar o controle de acesso para protegido.
3 – Chamar a criação da função membro para copiar a nova função para cada uma das
novas subclasses.
4 - Para uma instrução condicional de uma classe invariante pode ser determinado:
(A) simplificação da instrução condicional.
(B) Eliminação de todos os ramos onde o teste for falso (sem efeitos) contra a classe
invariante.
(C) Converter qualquer ramo onde o teste for falso (as com efeitos colateriais) para a
correção da declaração.
2.5. Usar uma classe invariável para especializar expressões que criam instâncias
Depois da definição das subclasses, o programa pode, às vezes, ser melhorado
especializando as expressões que criam as instâncias da superclasse, ou seja, substituir
uma expressão que cria uma instância da superclasse por uma que crie uma instância da
subclasse.
Isso não é feito com segurança, a menos que seja determinado que nenhuma
instância criada pela expressão viola o invariante da subclasse, o que envolve encontrar
onde as instâncias criadas por esta expressão são inicialmente atribuídas e encontrar os
[S1] Comentário: não compreendi
lugares onde a variável pode ser utilizada.
Se a variável é passada por referência em uma chamada de função, toda a sua
cadeia de uso também deve ser construída. Para determinar que a instância pode ser
7
UNIVERSIDADE TECNOLÓGICA FEDERAL DO PARANÁ
CAMPUS PONTA GROSSA
CURSO SUPERIOR EM BACHARELADO EM CIÊNCIA DA
COMPUTAÇÃO
Grupo de Pesquisa em Sistemas de Informação - Linhas de
Pesquisa em Engenharia de Software - GPSI
Projeto: Refatoração do FrameMK
Elaborado por: Luma Alves Lopes
[email protected]
Víctor Pedroso Ambiel Barros
[email protected]
especializada,
nenhuma operação sobre estas variáveis podem violar o invariante
definido na subclasse.
2.6. Especializar Expressões que criam instâncias
Os argumentos são:

Expressão que cria uma instância da superclasse.

Substituir a expressão que cria uma instância da nova subclasse.
Em seguida:
1 - Substituir a expressão com uma expressão equivalente que cria instâncias da
subclasse.
Para executar essa refatoração a pré-condição é que as instâncias criadas pela
expressão não podem violar a subclasse invariável.
2.7. Discussão
Se houver alguma variável-membro definida na superclasse que são somente
referenciadas em um subconjunto das subclasses, então elas podem ser migradas para
baixo nas subclasses.
As refatorações citadas neste relatório consideram especializações com base na
condição de uma instrução condicional e classes invariantes. Há ainda casos, em que o
estado de uma instância pode mudar violando as invariantes definidas nas subclasses.
Referências
[1] OPDYKE, William F. Refactoring Object-Oriented Frameworks. Urbana:
Illinois, 1992.
8
Download

Preservar o comportamento durante a refatoração.