• Nenhum resultado encontrado

Soluções para a Localização de Conceitos

Capítulo 4 – Localização de Conceitos

4.2. Soluções para a Localização de Conceitos

Diversos trabalhos que seguiram a definição do problema de associação de conceitos de Biggerstaff (1993) buscaram formas de suportar a compreensão de programas. Estes trabalhos utilizaram diferentes abordagens atingir o objetivo de localizar a implementação de conceitos ou funcionalidades no código fonte de programas. Essa área de estudo é chamada de

Localização de Conceitos – Concept Location. A Figura 2 exemplifica o objetivo das técnicas de Localização de Conceitos, que é mapear o código fonte em conceitos de dimensão humana.

Figura 2 – Objetivo da Localização de Conceitos

As técnicas de localização de conceitos em código fonte podem se valer tanto da análise estática como da dinâmica, ou mesmo de ambas. Uma abordagem comum é a realizar uma análise estática e procurar por padrões de texto que de alguma forma lembram conceitos procurados; por exemplo, “extr” ou “extrat” para “extrato”. A solução pode ser baseada em ferramentas simples como o comando grep do UNIX (LAKHOTIA, 1993) (WILDE et al, 2002) ou em mais sofisticadas, como agentes inteligentes (BIGGERSTAFF et al, 1993). Outra linha de soluções procurou analisar rastros de execução dos programas para associar as partes do código aos conceitos disparados por casos de testes especialmente concebidos (WILDE e SCHULLY, 1995).

4.2.1. Abordagens baseadas em Análise Estática

No mesmo artigo em que publicou a definição do problema de associação de conceitos, Biggerstaff et al (1993) demonstrou sua abordagem – estática – para a realização da associação de conceitos a código fonte. Para ele existem duas tarefas principais: “identificar as

CONCEITOS Bagagem ! "#$ #% &#$ $ ' ( ) * * ++ #, $- -#.# #- // 0 123 4 PROGRAMA DE COMPUTADOR Cobrança Reserva de Assento Passagem Avião Remarcação Bilhete eletrônico Emissão Venda

poucas entidades e relações que são realmente importantes em meio às tantas existentes no código fonte”; e “atribuir a elas os conceitos e relações de domínio conhecidas”.

A solução proposta se baseou em uma ferramenta – DESIRE – que agrega diversas capacidades para facilitar buscas de padrões, visualização e navegação no código fonte. O envolvido na tarefa de compreensão pode abordar o problema através de diferentes estratégias. Poderia começar localizando estruturas de dados (variáveis, ponteiros e etc.) através de seus nomes ou comentários que as relacionassem aos conceitos pretendidos, a partir daí, a ferramenta auxilia na navegação entre as rotinas que usam estas estruturas. Se a procura se iniciar através de funções relacionadas ao conceito a ferramenta pode facilitar a localização de outras funções nas quais a função em foco é referenciada ou então navegar para funções referenciadas por ela através de capacidades de hipertexto. Geração de slices com base em expressões ou em conjuntos de elementos de interesse também é uma capacidade da solução de Biggerstaff. Até mesmo um agente inteligente experimental – DM-TAO – que se baseia em um modelo do domínio para sugerir possíveis associações de conceito, com base no conceito ou em um trecho de código fonte, é citado como uma das capacidades da solução.

Gold (2001) aplicou técnicas de inteligência artificial na localização de conceitos com o objetivo de realizar a localização de conceitos com menos interferência humana que na proposta de Biggerstaff. Sua técnica que se baseia em 3 passos: geração de hipóteses, segmentação e acoplamento de conceitos. No primeiro passo a solução utiliza uma base de conhecimento para identificar possíveis associações entre conceitos e código fonte. Depois, o código fonte é divido em segmentos que podem estar associados a diversos conceitos. No último passo cada segmento é analisado a fim de definir qual conceito tem a melhor evidência de estar implementado ali.

Em uma linha diferente das anteriores, Chen e Rajlich (2000) criaram um método baseado em grafos de dependência. O programador explora o código fonte seguindo as dependências entre as componentes do sistema. A cada componente visitado ele decide se aquele componente tem ou não relação com a implementação do conceito pretendido. O processo segue até que ele considere que atingiu o objetivo de compreensão. Uma ferramenta de apoio registra os passos dados pelo programador em suas visitas e permite que ele ande para trás ou para frente nas visitas realizadas.

Marcus et al (2004) utilizaram técnicas de recuperação de informação para extrair conceitos do código fonte. Através da indexação semântica latente eles tentaram tornar a busca por termos no código fonte mais fácil e mais precisa. As principais vantagens do uso desta técnica são a simplicidade e a independência da linguagem do programa. Xie, Poshyvanyk e Marcus (2006) aprimoraram esta solução agregando a ela uma ferramenta de visualização de programas, criaram assim um ambiente onde os históricos de busca e de navegação são armazenados e apresentados graficamente.

Zhang et al (2006) apresentaram uma solução para associar código fonte a casos de uso através de análise estática. O método é quase totalmente automatizado e se baseia na geração de grafos de chamadas, considerando o fluxo de controle do programas. Em um segundo passo, heurísticas são utilizadas para eliminar funções consideradas de muito baixo nível e finalmente gerar um modelo de caso de uso.

4.2.2. Abordagens baseadas em Análise Dinâmica

As principais técnicas e soluções para localização de conceitos que utilizaram análise dinâmica se basearam no trabalho de Wilde e Scully (1995) que desenvolveram a técnica Software Reconnaissance para identificar que partes de um programa implementam uma determinada funcionalidade a partir da execução de casos de teste. A técnica de Wilde se estabeleceu como uma referência importante nas soluções de localização de conceitos e diversos outros autores estenderam ou incorporaram suas idéias em métodos e ferramentas mais elaborados (AGRAWAL et al, 1998) (DEPREZ e LAKHOTIA, 2000) (EISENBARTH et al, 2001) (SALAH et al, 2006).

O objetivo da técnica é o de ajudar a responder perguntas como (DEPREZ e LAKHOTIA, 2000):

• “Que segmentos do código fonte são responsáveis pela implementação da funcionalidade f ?”

• “Que segmentos implementam unicamente a funcionalidade f ?”

• “Que segmentos são indispensáveis para a implementação da funcionalidade f ?” O ponto central da técnica está na escolha de dois conjuntos de casos de teste. Um conjunto positivo para a funcionalidade deve conter casos de teste que fazem uso da funcionalidade que se pretende estudar. Por exemplo, para compreender como um editor de texto implementa a gravação de um arquivo, um caso de teste do primeiro conjunto pode ser

criar um novo arquivo e salvá-lo. No segundo conjunto estão os casos de teste que devem ser semelhantes aos do primeiro, mas sem, contudo, fazer uso da funcionalidade. No exemplo do editor de texto, um destes casos de teste poderia ser o de criar um novo arquivo no editor de texto, mas sair do editor sem salvá-lo.

Uma vez identificados os casos de teste, passa-se então a realizar a análise dinâmica. Os casos de testes são executados e os rastros coletados. Comparando-se os rastros pode-se verificar então que partes do código fonte não foram executadas em nenhum caso de teste, que partes do código fonte foram executadas em casos de teste dos dois conjuntos e que partes foram executadas exclusivamente por casos de testes do conjunto positivo para a funcionalidade.

Podemos resumir a técnica nos seguintes passos (WILDE et al, 2001):

• Dada a funcionalidade alvo da análise, são definidos os dois conjuntos de casos de teste;

• Instrumentação do código fonte, onde ele é modificado para gerar os rastros. É importante que não se altere o fluxo de controle ou estado de nenhuma variável para que a geração do rastro não modifique o comportamento geral do programa; • Geração de um novo programa executável e execução dos casos de teste;

• Análise dos rastros.

A Software Reconnaissance é muito eficiente para encontrar as partes de um código que implementam unicamente certa funcionalidade. Mas não ajuda a determinar todo o código envolvido na implementação da funcionalidade, enfim, todo o código que precisa ser compreendido. O seu principal produto é indicar ao mantenedor por onde começar o processo de entender uma funcionalidade.

Uma limitação da técnica é que a sua aplicação depende que a funcionalidade procurada possa ser disparada através da interface do sistema. Características difíceis de controlar externamente não são bons alvos. Apesar de haver requisitos funcionais e não- funcionais, apenas os funcionais – também chamados de funcionalidades – são considerados, pois é muito difícil definir casos de teste para aspectos não funcionais, como desempenho ou segurança (EISENBARTH et al, 2001). Funcionalidades dependentes umas das outras ou funcionalidades que, aparentemente, não estão relacionadas, mas usam código em comum podem gerar resultados não tão efetivos (WILDE e SCHULLY, 1995).

O trabalho de Wong et al (2000) aplicou técnicas de slicing dinâmico de forma semelhante à técnica de Wilde, mas com um objetivo diferente. Ao invés de localizar o código fonte relacionado a uma funcionalidade eles trabalharam na obtenção de métricas. As métricas obtidas são disparidade, concentração e dedicação. A concentração mede o quanto uma funcionalidade está implementada em um dado componente, enquanto a dedicação mede o quanto um componente atende a determinada funcionalidade. A disparidade é um misto das duas anteriores e seus valores estão no intervalo real entre 0 e 1: ela é 1 (um) se uma funcionalidade não usa nenhuma parte do componente; e é 0 (zero) se todas as linhas de código do componente são dedicadas a uma funcionalidade e todas as linhas de código que implementam a funcionalidade estão no componente.

Agrawal et al (1998) demonstraram como sua ferramenta de análise de cobertura de código fonte utilizou-se de heurísticas e de facilidades de visualização para uma implementação específica de Software Reconnaissance.

Deprez e Lakhotia (2000) procuraram aperfeiçoar a técnica agregando um método formal para definir casos de testes. O texto da documentação do sistema é analisado para a criação de uma gramática que irá guiar a criação dos casos de teste.

Eisenbarth et al (2003) associaram a Software Reconnaissance com grafos de dependências (CHEN e RAJLICH, 2000) e análise formal de conceitos (BIRKHOFF, 1940 apud EISENBARTH et al, 2003). A combinação destas técnicas permitiu quantificar as semelhanças entre a implementação das funcionalidades com base na análise das relações funcionalidade-componente.