Projeto
Utilizando Visualização de Informação para Compreensão de Software
De que se trata o artigo:
Este artigo apresenta como a visualização de informação em conjunto com as métricas de código fonte podem ajudar no processo de compreensão do software.
Para que serve:
A cada dia, com o contínuo aumento na complexidade dos sistemas de software, o processo para análise e compreensão do código visando evolução e manutenção do sistema se torna mais complexo e demorado. Aplicando os conceitos discutidos neste artigo, podemos tornar estas etapas menos difíceis e trabalhosas.
Em que situação o tema é útil:
Atualmente, existem diversas ferramentas que auxiliam a modelagem de sistemas, o controle de versões, testes, que possibilitam o desenvolvimento em grupo, mas ainda não é comum o uso de ferramentas que facilitem o processo de compreensão do software. Talvez por isso, ainda seja comum a existência de código duplicado, com alta complexidade, entre outros pontos negativos que reduzem a qualidade do sistema desenvolvido. A partir deste ponto, percebemos a importância da compreensão do software nas etapas de desenvolvimento e manutenção.
É cada vez mais evidente a importância de se ter controle sobre o modo como um software está sendo desenvolvido. Quando as primeiras linguagens de programação foram criadas, considerando aspectos como tamanho e complexidade dos sistemas desenvolvidos e o conhecimento sobre técnicas de programação, percebeu-se que os desenvolvedores não possuíam uma metodologia para estruturação de seu código. Possivelmente, os desenvolvedores não imaginavam uma forma bem definida na qual fosse possível analisar os códigos escritos sem ter que, necessariamente, olhar as centenas, ou até mesmo milhares de linhas de código (uma tarefa difícil e custosa). Além disso, retirar informações úteis e precisas dessas linhas com objetivo de dar continuidade ou manutenção ao sistema não era uma atividade trivial.
Embora muito tempo tenha se passado e muita evolução tenha ocorrido no desenvolvimento de técnicas para apoiar o desenvolvimento de sistemas, a tarefa de análise de código ainda é um campo com tópicos a serem explorados. Neste contexto, apresentaremos neste artigo o que se tem estudado sobre compreensão de código utilizando técnicas de visualização de informação, métricas e mineração visual de dados e ao final comentaremos sobre um plug-in de visualização desenvolvido para o Eclipse.
Visualização de Informação
Os seres humanos possuem dificuldades em processar dados no formato de textos e tabelas; por isso, freqüentemente, recorremos a meios visuais para interpretar o mundo a nossa volta [1]. Imagine por exemplo, dois mapas geográficos apresentando o número de habitantes (dado em análise) de cada país. No mapa 1, apresentamos sobre cada país o número de habitantes. No mapa 2, utilizamos cores com tonalidades diferentes para indicar o dado em análise, onde, a cor mais escura representa o país com maior população. Considerando que seu interesse seja estudar os países com população entre 50 e 100 milhões, qual mapa você utilizaria para obter esta informação de maneira mais rápida e segura?
O objetivo da visualização de informação é possibilitar que dados sejam apresentados através de formas simples e intuitivas [2].
A grande preocupação da visualização de informação é como os dados serão apresentados, pois uma boa escolha da forma de apresentação resulta na facilidade do entendimento e possibilita a descoberta de (novas) informações. Voltando ao exemplo do mapa, é possível que você “descubra” rapidamente, através do mapa que utiliza cores, que a região com maior concentração populacional esteja no continente asiático.
Como o número de habitantes é um dado comum e bastante discutido, talvez este não seja o melhor exemplo. Agora, como você analisaria o código do seu sistema com milhares de linhas para encontrar os pontos mais complexos, com alto índice de acoplamento e que a refatoração se torna indicada para facilitar a evolução e manutenção do software?
Muitas maneiras podem ser utilizadas para apresentar dados, dentre elas, o formato, cores, movimentos, posicionamento na tela e tamanho. Um exemplo dessa abordagem pode ser visto na Figura 1. Através dela, conseguimos notar como a forma, o tamanho e a cor podem ser usados para diferenciar os dados.
Hoje em dia é comum encontrarmos técnicas de visualização de informação até mesmo em players de música para dispositivos móveis, onde cada música é representada por um ponto e este ponto é inserido em um plano. Os pontos mais a esquerda indicam que aquelas músicas possuem um ritmo mais rápido, pontos localizados a direita, músicas lentas e assim por diante. Com isso, para escolher as músicas que você deseja ouvir basta marcar o plano com o ponto, então o programa definirá um raio e tocará apenas as músicas que estiverem dentro da região especificada.
Imaginemos que cada símbolo geométrico da Figura 1 representa um filme de uma pequena locadora, onde a cor está relacionada com o gênero (comédia-verde; drama-vermelho; ação-azul; e suspense-preto), o tamanho relacionado com a duração e o formato indicando se é um dvd ou uma fita. Agora imagine que você deseja alugar uma comédia, de longa duração e que esteja disponível em DVD. Fácil, não? E se quiséssemos apresentar também o dado que indica o número de vezes que o filme foi locado? Seria necessário relacionar este dado com algum atributo visual existente ou escolher outra técnica de visualização para a apresentação dos dados.

Figura 1. Dados representados através da cor, tamanho e forma.
Por isso, é importante ressaltar que o formato visual da apresentação dos dados pode não somente facilitar, mas também dificultar a análise que se deseja fazer. Neste sentido, a escolha correta do paradigma visual e os atributos visuais que serão usados para representar os dados tornam-se uma escolha crucial à atividade de visualização.
O paradigma visual utilizado deve se adaptar à natureza dos dados representados. Por exemplo, grafos são adequados para representar dados que descrevem relacionamentos (por exemplo, sites de relacionamento podem ser representados através de grafos). Árvores são adequadas para representar dados hierárquicos (por exemplo, estrutura de diretórios).
Uma vez definido o paradigma visual, vários atributos visuais podem ser associados aos dados a serem representados. A adequação de um atributo visual ao dado depende da escala e tipo desse último atributo. Para exemplificar esta situação, imagine representar todos os países participantes de uma copa do mundo utilizando apenas duas cores.
Os dados podem ser do tipo categórico ou numérico. Enquanto os dados categóricos não possuem um sentido de ordem ou unidade, os dados numéricos possuem. Considere o caso da análise de sistema de softwares que queremos visualizar; neste caso, o nome dos pacotes, classes e métodos são dados categóricos, enquanto o tamanho e a complexidade dos métodos são dados numéricos.
Atributos numéricos podem ser mapeados naturalmente para atributos visuais como tons de cores, tamanho e posicionamento na tela. Atributos categóricos podem ser mapeados naturalmente para formas e conjuntos diferentes de cores. É importante ressaltar que o tipo e a natureza do dado pode também sugerir qual o mapeamento mais indicado para a sua representação visual. Voltando ao exemplo da copa do mundo onde desejamos representar visualmente o registro dos países participantes. Caso se decida representar o país por cores, espera-se que a cor usada para representar cada país esteja relacionada com a nossa percepção natural. Por exemplo, o registro do Brasil poderia estar representado com a cor verde ou amarela e o registro dos Estados Unidos representado pela cor vermelha ou azul.
Dado o cenário acima descrito e a tarefa que temos em mão – representar visualmente atributos do código fonte do software – temos que escolher que paradigma e atributos visuais melhor se adaptam para representar a estrutura e as métricas dos sistemas de software que queremos analisar.
Para uma boa escolha da forma de apresentação, é preciso conhecer a respeito do domínio no qual os dados serão utilizados, como esses dados serão utilizados, as informações que serão apresentadas e o objetivo da apresentação dos dados.
Após escolher a forma de apresentação, o projetista deve levar em consideração alguns dos fatores como: o tipo dos dados, se estes são textuais e/ou numéricos; se existe a necessidade de interação entre o usuário e os dados; com que freqüência os dados apresentados devem ser atualizados; e se o usuário está interessado em informações precisas ou na relação entre o conjunto de dados.
Analisaremos agora como acontece o processo de visualização, discutindo todas as etapas existentes, partindo dos dados brutos até a interação do usuário com as visões.
Processo de Visualização
Para que os dados possam ser apresentados visualmente, eles devem passar por um processo de mapeamento, como pode ser visto na Figura 2 [1].

Figura 2. Mapeamento de dados para formas visuais
A Figura 2 mostra todo o processo de visualização ou mineração visual de dados (MVD). Neste processo, podem-se identificar três fases: (1) Preparação dos dados, onde os dados são organizados e formatados para que a aplicação possa entendê-los; (2) Mapeamento, etapa em que os dados são relacionados com as formas que serão utilizadas para a visualização; (3) Renderização, momento em que a imagem é apresentada na tela. As setas que saem da direita para a esquerda representam a realimentação feita pelo ser humano para explorar os dados que são apresentados de forma visual. A partir de agora analisaremos cada uma das etapas apresentadas na Figura 2.
Dados Brutos e Tabela de Dados
Os dados brutos são os dados que queremos analisar ainda no formato de origem. Ele pode estar no formato de tabelas, textos ou formulários. No nosso caso, os dados brutos são as classes. A partir do processo de preparação ou transformação, os dados são adaptados para uma forma estruturada, e que permitirá a sua utilização pela ferramenta de visualização.
Normalmente, o resultado do processamento dos dados brutos são representados no formato tabular, onde cada linha representa um registro de dados e cada coluna representa um atributo, ou campo do registro de dados. Seguindo nosso objetivo, o resultado do processamento das classes (veja a Figura 3) será um arquivo com os dados extraídos das classes (pacote, classe, método, tamanho e complexidade). Estes dados são extraídos utilizando métricas de software, que veremos mais adiante neste artigo.

Figura 3. Transformação dos dados
Estruturas Visuais e Visões
Na visualização, tabelas de dados são mapeadas em estruturas visuais. É neste momento que os dados são relacionados com as formas utilizadas para apresentação da informação (observe a Figura 4).
A partir das estruturas visuais montadas, o usuário poderá interagir com as informações, obtendo diferentes visões dos dados apresentados.

Figura 4. Mapeamento visual
Interação Humana
O processo de exploração visual envolve não apenas a construção de estruturas visuais, mas também a interação humana sobre esta estrutura. O processo de interação ocorre conforme mostrado na Figura 2, sendo realizado em três momentos. O primeiro momento é na seleção do conjunto de dados que será apresentada visualmente. O segundo ocorre no mapeamento dos atributos dos dados selecionados com atributos visuais que serão apresentados na tela. O terceiro momento acontece na interação do usuário com a visão formada, criando novos cenários visuais.
É importante mencionar que boas ferramentas de visualização permitem a execução de qualquer uma dessas três formas de interação (seleção de dados, o mapeamento visual e a modificação da visão), oferecendo mecanismos fáceis de manusear.
Visualização de dados hierárquicos
É muito comum encontrar dados organizados de forma hierárquica, como é o caso de um sistema de arquivos ou a estrutura de pacotes, classes e métodos em sistemas de software.
Uma boa forma de representar dados organizados de forma hierárquica é utilizando estruturas visuais conhecidas como mapas em árvore [3]. No Java, temos uma estrutura semelhante, conhecido como TreeMap (ver Figura 5).

Figura 5. Dados apresentados através de um Mapa em árvore
Mapas em árvore
A técnica de mapas em árvore, proposta originalmente por Johnson e Shneidermann [4], consiste em representar o nível mais alto da hierarquia como uma região retangular que preenche todo o espaço disponível. Os níveis mais baixos são desenhados recursivamente como retângulos dentro da região maior. O tamanho de cada retângulo é proporcional ao atributo que dimensiona os itens nos níveis imediatamente abaixo na hierarquia.
Na representação de mapas em árvore através de retângulos, a árvore é exibida como um grupo de retângulos recursivamente aninhados, onde cada nó é desenhado como um subconjunto de seu pai, conforme ilustrado na Figura 6. Esta técnica de visualização permite que todo o espaço disponível na tela de desenho seja utilizado, apesar da limitação existente em relação à área de exibição das informações dentro de cada grupo (retângulo). Essa abordagem permite que hierarquias com dezenas de milhares de itens possam ser desenhadas em uma única cena visual.
Figura 6. Uma árvore hierárquica e sua representação em um mapa em árvore
Utilizando visualização para compreensão de software
Quando uma pessoa aprende sobre algum assunto, ela se torna capaz de discutir e explicar sobre aquilo que aprendeu. Um processo similar ocorre com os desenvolvedores de software. Quando estes entendem o funcionamento do programa, é normal que saibam explicar como o sistema funciona, qual a estrutura utilizada, e também modificá-lo para se enquadrar a novos objetivos.
Porém, entender o funcionamento do software não é uma atividade trivial. Dentre os problemas que dificultam o entendimento de um sistema, podemos destacar: o tamanho da aplicação, a sua complexidade, as práticas de programação e as características da linguagem de programação usada na sua construção.
Esse processo de compreensão do código é caracterizado como o momento em que o desenvolvedor monta um modelo mental desse software. Como temos memória de curto prazo pequena, não somos capazes de armazenar muitos itens de informação durante o processo de compreensão de um sistema [5]. Por essa razão, é normal que os desenvolvedores sintam dificuldade em analisar muitas informações diferentes, e que interagem entre si. A solução para este problema está numa frase bastante conhecida em nossa área: “Dividir para conquistar!”. Então, organizamos a informação em níveis diferentes de abstração, onde grandes sistemas são inicialmente percebidos como poucos módulos de grande porte que interagem entre si. Por sua vez, esses módulos podem ser analisados como conjuntos de sub-módulos que interagem entre si, e assim por diante. Dessa forma, a compreensão de um grande sistema pode ser dividida na compreensão de seus sub-módulos.
Esse processo prossegue recursivamente até o nível de detalhe desejado pelo analista. Dessa forma, apresentamos um processo hierárquico, onde um grande problema é recursivamente dividido em problemas menores em um nível de abstração que permite que um ser humano, com limitações que são naturais, possa compreender e analisar grandes sistemas.
Dado esse cenário, pode-se notar a importância das ferramentas de visualização software. Estas ferramentas permitem que o programador entenda o funcionamento e a organização do sistema de uma forma mais rápida, eliminando a maioria das dificuldades citadas no parágrafo anterior. Isto é conseguido por que estas ferramentas possibilitam que o usuário interaja com as informações através de mecanismos de análise, exploração e visualização em diferentes níveis de abstração.
A partir do momento em que as dificuldades são eliminadas, pode-se esperar vários benefícios com o uso de técnicas de visualização de software. Dentre eles, podemos citar: entendimento do software de forma mais rápida, aumento de produtividade, e melhoria na manutenção, análise, e evolução do software [6].
Tipos de visualização de software
Voltado para a área de software, temos dois tipos de visualização: estática e dinâmica.
A visualização estática é a visualização feita das estruturas estáticas do software. Por exemplo, os dados extraídos da estrutura de um código fonte qualquer. A partir desses dados é montada uma forma visual que representa informações sobre o código a ser analisado.
Neste tipo de visualização, não é apresentado o comportamento de execução do sistema sendo analisado.
A visualização dinâmica tem o objetivo de apresentar estruturas visuais que descrevem o comportamento do sistema em tempo de execução. Um exemplo desse tipo de visualização são os sistemas de animação de algoritmos computacionais [7], por exemplo, os algoritmos de inserção de nós em árvore. Outro exemplo são as visualizações de execução construídas pelos sistemas modernos de depuração de código.
Métricas de Software
Métricas de software têm sido apontadas como um dos principais mecanismos para tornar o desenvolvimento e manutenção de software uma disciplina mais previsível e controlável [8]. Medir é uma prática básica em qualquer tipo de engenharia, e na “engenharia de software” não é diferente. Várias métricas têm sido propostas para se medir atributos como tamanho, complexidade, coesão e acoplamento dos mais variados artefatos de software.
As métricas estão relacionadas tanto com o produto, como com processos de desenvolvimento e manutenção do software. A partir delas, conseguem-se dados quantitativos que oferecem uma boa informação sobre o andamento do projeto. Com essas informações, é possível fazer a estimativa de custos, prazos de entrega e até mesmo ter noção sobre a qualidade do sistema [8].
O desenvolvimento de grandes sistemas é uma atividade que vem se tornando cada vez mais comum e que consome muito tempo e esforço. Sabendo-se que cada etapa do desenvolvimento de um sistema requer planejamento e esforço, é preciso fornecer informações para que os desenvolvedores possam tomar decisões sobre o andamento das atividades. Desta forma, as métricas de software tornam-se necessárias para identificar informações sobre o processo e auxiliar nas decisões tomadas.
Tipos de Métricas
As métricas de software são divididas em métricas do produto, do processo e do projeto. As métricas do produto descrevem características do produto como tamanho, complexidade, design, performance e níveis de qualidade. Métricas de processo podem ser usadas para melhorar as atividades de desenvolvimento e manutenção do software. Já as métricas de projeto descrevem as características do projeto em desenvolvimento, como o número de desenvolvedores, utilização de padrões, custo, produtividade, entre outros.
Entre as muitas métricas de produto, as métricas de código fonte estão entre as mais importantes. Muitas destas estão diretamente relacionadas ao paradigma da linguagem de programação, como é o caso das métricas orientadas a objeto. Entretanto, existem também métricas que independem do paradigma de desenvolvimento adotado. Neste ponto, vale ressaltar a importância da escolha das mesmas.
Auxiliado pelas métricas de código fonte, é possível conhecer a complexidade, o tamanho do sistema, a quantidade de métodos, o nível de coesão e o grau de acoplamento entre classes, além de se poder medir a produtividade dos programadores, dentre outros.
Métricas de código fonte
Uma das métricas mais simples que pode ser extraída de um software é a métrica de número de linhas de código. Entretanto, muitos são os fatores que podem gerar uma diferença no resultado da contagem. Antigamente, quando a linguagem de programação utilizada era o Assembler, cada comando representava uma linha de código. No entanto, atualmente, com as linguagens de alto nível, a contagem pode obter diferentes valores, podendo ser influenciada pela contagem ou não de comentários, linhas em branco, e a forma como as estruturas da linguagem são escritas. Um exemplo deste problema pode ser visto nas Listagens 1 e 2, onde o mesmo código é apresentado, só que escrito de formas diferente.
Listagem 1. Código comentado
1. import java.io.*;
2.
3. public class Estrutura
4. implements Serializable {
5.
6. private String nome,
7. endereco;
8.
9. //Construtor da classe Estrutura
10. public Estrutura() {
11.
12. }
13.
14. //método main
15. public static void main(String args[]) {
16. //Verifica a quantidade de argumentos passados
17. if (args.length == 3)
18. System.out.println(‘Testanto argumentos’);
19. else
20. System.out.println(‘ERRO!’);
21. }
22. }
Listagem 2. Código não comentado
1. import java.io.*;
2.
3. public class Estrutura implements Serializable {
4.
5. private String nome, endereco;
6.
7. public Estrutura() { }
8.
9. public static void main(String args[]) {
10. if (args.length == 3)
11. System.out.println(‘Testanto argumentos’);
12. else
13. System.out.println(‘ERRO!’);
14. }
15. }
A partir das Listagens 1 e 2, pode-se notar uma pequena diferença na quantidade de linhas, mas que, considerando que esse trecho de código faz parte de um sistema com centenas de classes, essa diferença vai se tornar significativa. Nestas listagens, também é possível notar diferenças no estilo de programação, como por exemplo, a quebra de linha.
A Tabela 1 apresenta algumas métricas de código fonte [8]. Em seguida, descrevemos três métricas bem conhecidas: tamanho, complexidade e coesão.
|
Métrica |
Aplicada em |
Atributo |
O que medem |
...