Métricas de Código Fonte
Esta página corresponde a um capítulo retirado diretamente de um trabalho de conclusão de curso, de engenharia de software da Universidade de Brasília, que discute um pouco acerca de métricas de código fonte. Para ler o trabalho completo, clique aqui.
Métricas de Código
editarA ISO/IEC 9126, reunida agora na ISO/IEC 25000, apresenta características de qualidade e um guia para o uso dessas características, de forma a auxiliar e padronizar o processo de avaliação de qualidade de produtos de software. Ela separa qualidade de software essencialmente em qualidade interna e qualidade externa. Qualidade externa é a visão do produto de software de uma perspectiva externa, resumindo-se basicamente em qualidade do sistema em execução. Já a qualidade interna trata da visão interna do produto de software, podendo incluir documentos, modelos e o código fonte. A qualidade interna pode começar a ser medida em estágios mais iniciais do desenvolvimento por não haver ainda nesses estágios uma visão externa do sistema. Esses valores de qualidade interna podem ser utilizados para prever os valores de qualidade externa que o produto vai apresentar. É importante perceber essa relação entre qualidade interna e externa para perceber que qualidade interna impacta fortemente na qualidade externa de um produto, e então observar a importância de começar esforços de medição e acompanhamento da qualidade interna desde o início do projeto. A avaliação de qualidade de código fonte no início do desenvolvimento pode ser de grande valia para auxiliar equipes inexperientes durante as etapas seguintes do desenvolvimento de software (MEIRELLES, 2013).
Métricas de código fonte selecionadas
editarMétricas de código fonte caracterizam bem o produto de software em qualquer estado do seu desenvolvimento. Existem vários tipos de métricas de código fonte. Entre as principais categorias, temos métricas de tamanho, complexidade, acoplamento e coesão.
Várias métricas de tamanho podem ser utilizadas para avaliar quantidade de código fonte, como por exemplo Linhas de código - Lines of Code (LOC). Manter o monitoramento de métricas de volume de código em conjunto com outras métricas é importante para que comparações sejam feitas para sistemas de tamanho semelhante. A exemplo disso temos métricas de complexidade como relacionadas ao tamanho do software. Essas métricas de tamanho devem ser consideradas para que as comparações sejam feitas de forma adaptada para a “escala” de tamanho dos softwares sendo comparados. AMLOC (Média de linhas de código por método - Average Methods Lines Of Code) é semelhante a uma combinação de LOC e NOM (Número de métodos de uma classe - Number of Methods), duas métricas de tamanho, é uma boa escolha em termos de tamanho de código para o início deste trabalho devido a sua simplicidade, embora este estudo possa ser futuramente expandido com a utilização de métricas adicionais, para complementar ou substituir essa métrica.
R.Basili, Briand e Melo (1995) apresenta um estudo com métricas CK, introduzidas por Chidamber e Kemerer (1994), que são métricas para sistemas orientados a objetos, para avaliação da arquitetura do sistema e qualidade do código fonte. São essencialmente métricas que verificam coesão e acoplamento de classes, além de complexidade de estrutura hierárquica de objetos, característica de projetos OO. Essas métricas são bastante úteis para prever estados futuros já nas primeiras etapas do ciclo de vida. Tais métricas podem ser bastante úteis como indicadores de qualidade de sistemas orientados a objetos(R.BASILI; BRIAND; MELO, 1995).
Métricas de complexidade também dão uma visão do estado atual do software no que diz respeito a escolhas arquiteturais e facilidade de compreensão do mesmo e também tem impacto direto na manutenibilidade. A métrica de complexidade ciclomática, por exemplo, introduzida por McCabe (1976), foi criada para avaliar a quantidade de caminhos que a execução do software pode seguir em sua execução, indicando complexidade de leitura e entendimento do código, assim como esforço para testá-lo.
Todas as métricas que serão descritas aqui tem a interpretação geral de ter o valor tão pequeno quanto possível, indicando simplicidade no projeto. Simplicidade no projeto é geralmente característica de uma boa arquitetura.
Ferramenta de captura de métricas
editarO Analizo é uma ferramenta livre e extensível para análise de código com suporte a várias linguagens, incluindo Java, que será o foco da análise neste trabalho. Uma grande quantidade de métricas são coletadas pela ferramenta, embora apenas algumas sejam utilizadas para esta análise. Um dos motivos da escolha do sistema Debian para execução das análises foi a facilidade de instalação da ferramenta.
A saída da ferramenta é um arquivo CSV (Comma-Separated Values - valores separados por vírgula) para cada projeto ou versão a ser analisada, assim como um arquivo CSV que centraliza os valores de cada métrica em nível de projeto para cada um dos projetos/versões. Isso pode ser interessante quanto utilizado com o mesmo projeto em diferentes versões para verificar o avanço de algumas métricas juntamente com a evolução do sistema.
Trabalhos com análise de intervalos de métricas como as teses de Meirelles (2013) e Oliveira (2013), que utilizam a ferramenta Analizo, nos ajudam na escolha dessa ferramenta para comparação direta com as discussões nessas teses, uma vez que pode haver pequenas variações nos cálculos das métricas em diferentes ferramentas. Por exemplo algumas ferramentas devolvem sempre um DIT (Profundidade na árvore de herança - Depth In Tree) mínimo de 1 para classes Java, que herdam de Object, enquanto o Analizo não contabiliza essa herança.
É importante ressaltar que as métricas discutidas aqui, utilizadas durante todo o estudo, são coletadas de forma unificada pela ferramenta Analizo. As métricas finais escolhidas estão listadas a seguir:
- Média de linhas de código por método - Average Method Lines Of Code (AMLOC)
- Média de complexidade ciclomática por método - Average Cyclomatic Complexity per Method (ACCM)
- Resposta para uma classe - Response For a Class (RFC)
- Profundidade na árvore de herança - Depth in Inheritance Tree (DIT)
- Número de subclasses - Number of Children (NOC)
- Falta de coesão em métodos - Lack of Cohesion in Methods (LCOM4)
- Conexões aferentes de uma classe - Afferent Connections per Class (ACC)
- Fator de acoplamento - Coupling Factor (COF)
Descrição das métricas
editarLinhas de Código (LOC) / Média de linhas de código por método (AMLOC)
editarLOC representa o número de linhas de código fonte de uma classe, enquanto AMLOC a média do número de linhas dos métodos daquela classe. Número de métodos (Number of Methods - NOM) conta o número de métodos de uma classe e também é uma métrica de tamanho (SHARMA et al., 2012).
A primeira observação que deve ser feita quando analisando LOC nesse contexto é a diferenciação das linguagens. Embora um módulo em C seja mapeado para uma classe, arquivos fonte em C tendem a ser maiores que uma classe em Java, por exemplo, devido aos diferentes paradigmas que essas linguagem utilizam. Arquivos em C++ e Java também podem ter valores bem distintos para a mesma funcionalidade devido ao número de bibliotecas padrões que a linguagem apresenta e a natureza da própria sintaxe da linguagem. Dessa forma, comparações dessa métrica devem ser feitas somente dentro da mesma linguagem.
A métrica LOC por si só não será discutida aqui, pois seu valor é totalmente independente e deve ser comparado com outras métricas para ter significado mais completo. AMLOC tem significado semelhante a uma combinação de LOC e NOM, e utilizá-la pode ser considerado uma utilização indireta dessas métricas. AMLOC apresenta uma interpretação mais concisa que as demais, uma vez que métodos grandes “abrem espaço” para problemas de complexidade excessiva.
Em suma, a análise de outras métricas podem abranger as explicações relacionadas a métrica LOC e também a NOM, então essas métricas de tamanho não serão explanadas em separado, mas ocasionalmente citadas na explicação de outras métricas.
Média de complexidade ciclomática por método (ACCM)
editarComplexidade ciclomática nada mais é do que o número de caminhos independentes que um software pode seguir em sua execução, calculado a partir da representação em grafo das estruturas de controle (SHEPPERD, 1988). Na prática, cada condicional dentro do sistema incrementa o valor desta métrica em 1, uma vez que divide a execução em um caminho de execução se a expressão condicional for válida, ou um segundo caminho caso não seja. Complexidade ciclomática é calculada em nível de método, e o valor de ACCM para uma classe corresponde a média dos valores de complexidade ciclomática de cada um dos seus métodos.
A interpretação do valor de complexidade ciclomática é relativamente simples: O valor 1 é o valor mínimo e ideal para se ter como resultado, pois significa que o software tem apenas uma forma de executar e será executado necessariamente daquela forma e naquela sequência. Como consequência disso, se tem um software que pode ser mais facilmente lido e modificado. A implicação dessa métrica é mais notada na atividade de testes do código fonte, pois além de dificultar a compreensão dos possíveis comportamentos de um pedaço de código, cada caminho adicional que pode ser seguido é um trecho diferenciado que deve ser testado. Isso quer dizer que o esforço de teste é diretamente proporcional ao resultado dessa métrica, pois para garantir o funcionamento correto do sistema, todos as possibilidades devem ser devidamente testadas. Em termos práticos, atingir uma cobertura de código de 100% é uma tarefa árdua quando há um valor muito grande de complexidade ciclomática.
Inserida então no contexto de manutenção e testes, ACCM é uma excelente candidata para ser constantemente monitorada ao longo da evolução do código fonte. Embora não tenha muita relação com outras métricas OO, ela tem uma relação óbvia do número de linhas de código de um método, pois um método com poucas linhas de código não tem possibilidade de ter um valor muito alto de complexidade ciclomática. Entretanto, essa relação não faz com que elas possam ser utilizado para propósitos semelhantes (WATSON; MCCABE; WALLACE, 1996).
Resposta para uma classe (RFC)
editarResponse for a Class é uma métrica que conta o número de métodos que podem ser executados a partir de uma mensagem enviada a um objeto dessa classe (CHIDAMBER; KEMERER, 1994). O valor então é calculado pelo somatório de todos os métodos daquela classe, e todos os métodos chamados diretamente por essa classe. Uma classe com alto valor de RFC pode ser uma classe com um número muito grande de métodos, e/ou uma classe bastante dependente de outra(s) classe(s). Um valor alto de RFC então pode indicar baixa coesão e alto acoplamento.
Profundidade na árvore de herança (DIT) / Número de subclasses (NOC)
editarDIT é uma métrica que mede a profundidade que uma classe se contra na árvore de herança, e caso haja herança múltipla, DIT mede a distancia máxima até o nó raiz da árvore de herança (CHIDAMBER; KEMERER, 1994). Se ela não herda nada, tem DIT igual a 0. Se herda de uma classe, a profundidade é 1, e assim por diante.
NOC mede a quantidade de filhos que uma classe tem (CHIDAMBER; KEMERER, 1994). Caso ninguém herde dela, o valor é 0, e aumenta em 1 para cada classe que a estende diretamente, ou seja, filhos de filhos não são contabilizados.
DIT e NOC são métricas relativamente semelhantes por trabalhar com a árvore de herança, entretanto tem interpretações diferentes. São métricas que indicam complexidade no design, assim como a maioria das métricas OO.
Altos valores de DIT indicam que a classe herda de várias outras recursivamente, podendo tornar seu comportamento mais imprevisível, pois não se sabe todos os seus possíveis comportamentos sem analisar as demais. Classes com alto DIT tendem a ser mais complexas por adicionar o comportamento de todas suas classes precursoras. Entretanto, por se tratar de herança, altos valores de DIT também indicam maior reuso de código fonte.
NOC também indica maior potencial de reuso em altos valores, assim como na métrica DIT. Entretanto, ela também indica a importância de uma classe e seus comportamentos no design. Um NOC alto significa que uma mudança na classe pode ter consequências graves, pois seus métodos são utilizados em muitos filhos. Consequentemente é recomendado que classes com altos valores de NOC sejam muito bem testadas.
Falta de coesão em métodos (LCOM)
editarLCOM é uma métrica que mede coesão de uma classe. Existem algumas variações da métrica LCOM definida por Chidamber e Kemerer (1994) criadas por outros estudos que não serão abordadas neste estudo. A variação calculada pela ferramenta Analizo e utilizada neste trabalho é a LCOM4 (HITZ; MONTAZERI, 1995).
LCOM4 gira em torno da ideia de que os métodos da classe estão coesos se eles utilizam os mesmos atributos dentro dessa classe. Se algum método não utiliza nada da classe, ou utiliza apenas métodos/atributos de outra classe, ele provavelmente está no lugar errado.
A métrica então calcula quantos conjuntos de métodos relacionados existem dentro dessa classe, isto é, métodos que compartilham utilização de algum atributo ou que se referenciam. Caso existam 2 conjuntos de métodos distintos, ou seja, cada conjunto utiliza um conjunto diferente de atributos e um conjunto não utiliza nenhum método do outro, o valor de LCOM4 é 2, e significa que essa classe pode ser dividida em 2 para aumentar a coesão. O valor ideal de LCOM4 é 1, que representa a maior coesão possível, e valores maiores que isso podem indicar que a classe está com muita responsabilidade, tentando alcançar muitos propósitos distintos.
É possível notar, pela própria definição da métrica, que LCOM4 é limitada pelo número de métodos da classe (NOM), embora não sejam diretamente relacionadas. Uma classe com 2 métodos não pode ter mais que 2 conjuntos distintos de métodos relacionados, então seu LCOM4 não passa de 2. Essa observação apenas quer dizer que classes pequenas tendem a ter menores valores de LCOM4.
Acoplamento entre Objetos (CBO) / Conexões aferentes de uma classe (ACC)
editarA métrica de acoplamento entre objetos (CBO), definida por Chidamber e Kemerer (1994), calcula as conexões de entrada e de saída de uma classe, isto é, para uma classe A, são contabilizadas classes que utilizam algum método ou variável de A, como também todas as classes que A referencia. Entretanto neste trabalho não a utilizaremos por problemas encontrados na coleta, ficando com ACC como métrica de acoplamento.
ACC é um valor parcial de uma das métricas MOOD (Metrics for Object Oriented Design) propostas por Abreu e Carapuça (1994). É um o resultado de um cálculo intermediário para calcular o fator de acoplamento (COF).
ACC mede o nível de acoplamento de uma classe através do número de outras classes que fazem referencia a ela, por meio da utilização de algum método ou atributo. Apenas as conexões de entrada são contabilizadas, então, diferente de CBO que faz uma contagem bidirecional, ACC só contabiliza a quantidade de classes clientes de uma classe A qualquer, ou seja, que referenciam A, não importando quantas classes A referencia.
Uma classe com altos valores de ACC é utilizada em muitas outras. A interpretação dessa métrica é semelhante a métrica NOC no que diz respeito a impacto de mudanças. Ter muitas classes clientes indica que é necessário um maior cuidado ao realizar edições nessa classe, uma vez que impactos dessas modificações podem ocorrer em um número de classes tão grande quanto o valor de ACC.
De forma geral, deseja-se ter classes tão independentes quanto possível, levando ovalor de ACC para baixo.
Fator de acoplamento (COF)
editarCOF é uma métrica MOOD proposta por Abreu e Carapuça (1994), e é nada mais é que uma relativização do valor de ACC explicado na seção anterior para o tamanho do projeto, sendo então um valor apenas para todo o código fonte desse projeto. ACC calcula as conexões que uma classe tem, enquanto COF soma todas essas conexões de todas as classes e divide pelo total de conexões possíveis, resultando e um valor que pode variar de 0 a 1. Caso todas as X conexões possíveis aconteçam em um software, COF para ele será X/X, que é igual a 1. O ideal então como acoplamento para um projeto qualquer é que o valor de COF esteja tão próximo de zero quanto possível, indicando que as classes são mais independentes e desacopladas.
Naturalmente o incremento no número de classes de um projeto tende a fazer com que o valor da métrica caia, embora não seja sempre inversamente proporcional a ponto de esperar que projetos distintos com números maiores de classes sempre tenham menores valores de COF, pois depende bastante do design de sua arquitetura.