Skip to content

Instantly share code, notes, and snippets.

@eltonvs
Last active May 26, 2017 15:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eltonvs/9718c7120cdb85753caf08a6b38e05f5 to your computer and use it in GitHub Desktop.
Save eltonvs/9718c7120cdb85753caf08a6b38e05f5 to your computer and use it in GitHub Desktop.

Métricas de Software

Table of Contents

  1. Problema
  2. Definicao
  3. Metricas de complexidade
    1. Linhas de código) (SLOC)
    2. Complexidade ciclomática
    3. Fan-In/Fan-Out
    4. Métricas de Halstead
    5. Índice de Manutenibilidade
  4. Métricas de Software OO
    1. Métricas de Pacotes
      1. Acoplamento
      2. Instabilidade
      3. Grau de Abstração

Problema

  • A qualidade de código não é acompanhada durante o projeto
  • Software é entregue com má qualidade interna (Arquitetura frágil e design rígido dificultando a manutenção)

Solução

  • Coleta sistemática e periódica das medições de qualidade do código
  • Maximização da automação da coleta das métricas
  • Nortear análises de acordo com o objetivo (desempenho, reuso, simplicidade)

Definição

  • É uma convenção de medida do grau que um software tem para uma propriedade
  • Métricas: funções utilizadas para medir a propriedade em questão
  • Medidas: valores das métricas
  • Métricas e Medidas são só indicadores, e devem ser observados para cada contexto (em particular)
  • Complexidade: o grau de dificuldade para entender o projeto ou implementação de um sistema
    • Complicado: difícil compreensão, mas se torna compreensível com o tempo e esforço
    • Complexo: grande número de interações entre um dado número de entidades

Métricas de complexidade

  • Complexidade maior → dificuldade de utilização, entendimento e manutenção (aumenta os custos e o tempo necessário)
  • Códigos complexos possuem baixa rubustez e maior propensão a erros
  • As métricas de complexidade tentam traduzir de forma quantitativa a dificuldade de entendimento e o nível de complicação de um código

Métricas estáticas:

  • Linhas de Código (SLOC)
  • Complexidade ciclomática
  • Fan-in/Fan-out
  • Métricas de Halstead
  • Índice de manutenibilidade

Linhas de código (SLOC)

  • Calcula o volume de código de um sistema contabilizando o total de linhas de código no programa
    • Prícipio de que quanto maior um programa, mais complexo e propenso a erros ele provavelmente será
  • Utilizado pra predizer a quantidade de esforço necessário para o desenvolvimento, produtividade ou manutenibilidade de um software
    • Uma das métricas mais confiáveis
  • Variações:
    • Número total de linhas (incluindo comentários, linhas em branco, chaves e parênteses isolados)
    • Número de linhas de código efetivas (desconsiderando o que não é codigo)
    • Número de linhas de código que representem instruções completas finalizadas e com ";" ou outro símbolo que determine o fim de instrução
  • Pontos positivos:
    • Simplicidade
    • Fácil coleta (automatizada ou não)
    • Uso popular para medição de complexidade
  • Pontos negativos:
    • Falta de padronização
    • Desconsideração da qualidade e estruturação do código
    • Dependência de linguagem de programação

Complexidade ciclomática

  • Mede a complexidade estrutural de acordo com a quantidade de caminhos independentes que podem ser executados até o seu fim
  • Caminho independente é aquele que apresenta pelo menos uma nova condição (possibilidade de desvio de fluxo) ou um novo conjunto de comandos executados
    • Correlação com o grau de compreensão do programa
  • Usa um grafo de fluxo de controle (GFC), que é um grafo dirigido que representa todos os caminhos do programa que podem ser executados
    • Nós representam uma ou mais instruções sequenciais
    • Arestas representam o fluxo de controle entre blocos de instruções (nós)
    • Existe um nó de entrada (início) e um nó de saída (fim)
  • É necessário determinar os componentes conexos no GFC
    • Programa ou rotina únicos, número de componentes conexos é sempre igual a 1
  • Fórmula Geral: CC = E - N + (2 * P)
    • E → número de arestas
    • N → número de nós
    • P → número de componentes conexos
  • Caso o nó de saída seja conectado ao nó de entrada por meio de uma aresta, o GFC se torna fortemente conexo, e a complexidade ciclomática passa a ser calculada como CC = E - N + P
  • Interpretação:
    • 1 - 4: Simples (risco baixo)
    • 5 - 10: Bem estruturado e estável (risco baixo)
    • 11 - 20: Mais complexo (risco moderado)
    • 21 - 50: Complexo (risco alto)
    • 50+: Altamente problemático (risco muito alto)
  • Pontos Positivos:
    • Indicador de custo de manutenção e riscos
    • Indicador de qualidade em termos de complexidade
    • Ênfase em partes que demandariam mais testes e/ou eventuais refatorações
    • Utilização para programas procedurais ou OO
  • Pontos Negativos:
    • Medição apenas da complexidade do fluxo e não dos dados
    • Possibilidade de obtenção de diferentes medidas de acordo com a ferramenta
    • Complexidade para determinação do GFC

Fan-In/Fan-Out

  • Representa a quantidade de informação que flui como entrada e saída em uma programa, possibilitando determinar a interconexão das unidades lógicas
  • Fan-In: número de rotinas que chamam a rotina (entram na rotina)
    • Valor alto → alto acoplamento entre as rotinas, grandes efeitos colaterais
  • Fan-Out: número de rotinas chamadas pela rotina (saem da rotina)
    • Valor alto → alta complexidade da rotina
  • Fórmula: C = L * (Fi * Fo)²
    • L → Qualquer medida de tamanho (tipicamente SLOC)
    • Fi → Fan-In
    • Fo → Fan-Out
  • Pontos Fortes:
    • Considera o fluxo de dados na mensuração da complexidade
  • Pontos Fracos:
    • Possui valor zero quando os valores de Fan-In e/ou Fan-Out são 0

Métricas de Halstead

  • Partem da interpretação do código fonte como uma sequência de tokens classificados entre operadores e operandos
  • Medidas determinadas através da contagem dos operadores e operandos distintos de um programa, resultando em diferentes aspectos
    • Operandos → Variáveis, constantes e nomes de tipos (primitivo ou composto)
    • Operadores → quaisquer outros elementos
  • Contabilizam:
    • n1 → Número de operadores distintos
    • n2 → Número de operandos distintos
    • N1 → Número total de operadores
    • N2 → Número total de operandos
  • Computadas como:
    • Tamanho do programa (N) → N = N1 + N2
    • Tamanho do vocabulário (n) → n = n1 + n2
    • Volume do programa (V) → V = N * log2(n) (tamanho da implementação)
    • Nível de dificuldade (D) → n1/2 * N1/n2
    • Esforço (E) → E = D * V (esforço para implementação e compreensão)
    • Tempo para implementação (T) → E/18 (segundos)

Índice de Manutenibilidade

  • Métrica composta que indica o nível de dificuldade geral para a manutenção de um código fonte
  • Calculado como uma correlação entre o tamanho do programa (SLOC), Métricas de Halstead e Complexidade Ciclomática
  • Fórmula: MI = 171 - (5.2 * ln(V')) - (0.23 * C') - (16.2 * ln(L'))
    • V' → Média da métrica de Halstead de volume por módulo
    • C' → Média da complexidade ciclomática por módulo
    • L' → Número médio de linhas de código fonte (SLOC) por módulo
  • Interpretação:
    • 85+ → Boa manutenibilidade
    • 65 - 85 → Moderada manutenibilidade
    • 65- → Difícil manutenção

Métricas de Software OO

Conceitos importantes em OO:

  • Coesão → Grau de conectividade entre os elementos de uma classe ou objeto
  • Acoplamento → Grau de relacionamento ou interdependência de módulos, classes ou objetos
  • Herança → Compartilhamento de atributos e métodos entre classes
  • Encapsulamento → Abstração de dados para ocultar a especificação interna de um objeto e apenas exibir sua interface externa

Principais Métricas OO:

  • Métricas de pacotes
  • Métricas CK (Chidamber & Kemerer)
  • Métricas MOOD (Metrics for OO Design)

Métricas de Pacotes

Analisa classes e interfaces em pacotes quanto a:

  • Acoplamento/Dependência
  • Grau de abstração
  • Instabilidade

Acoplamento

  • Acoplamento aferente → número de classes de outros pacotes que dependem das classes do pacote analisado (nível de responsabilidade do pacote)
  • Acoplamento eferente → número de classes de outros pacotes que as classes do pacote analisado dependem (grau de dependências externas do pacote)

Instabilidade

  • Resiliencia de uma classe/pacote a eventuais mudanças (estabilidade é o esforço necessário para realizar mudanças em uma classe/pacote sem impactar outras classes/pacotes com os quais ela está relacionada)
  • Fórmula: I = Ce / (Ca + Ce)
    • Ca → Acoplamento Aferente
    • Ce → Acoplamento Eferente
  • A instabilidade I possui valor entre 0 e 1, sendo:
    • I = 0 → máxima estabilidade (Ce = 0 → não depende de pacotes externos)
    • I = 1 → máxima instabilidade (Ca = 0 → possui apenas dependência extena)
  • Interpretação
    • Classes/Pacotes com múltiplas dependências externas, porém com poucas dependências deles são menos estáveis devido a consequências de mudanças
    • Classes/Pacotes com mais dependências deles são mais estáveis por serem de difícil mudança

Grau de abstração

  • Mede a extensibilidade de um pacote, ou seja, o quão abstratas são suas classes para permitir extensões
  • O grau de abstração A de um pacote P é calculado como: A = Na/Nr
    • Na → número de classes abstratas contidas no pacote
    • Nr → número total de classes contidas no pacote
  • O grau de Abstração A possui valor entre 0 e 1, sendo:
    • A = 0 → completamente concreto (Na = 0 → não possui classes abstratas)
    • A = 1 → completamente abstrato (Na = Nr → não possui classes concretas)
  • Interpretação:
    • Grau de abstração mais próximo de 0 indica que o pacote é mais aberto a mudanças na implentação de suas classes
    • Deve ser correlacionado com a medida de instabilidade

Métricas CK

Representa o conjunto mais popular de métricas utilizadas para analisar programas OO:

  • Identificação de classes
  • Identificação da semantica de classes
  • Identificação dos relacionamentos entre classes
  • Implementação de classes

Método ponderado por classes (WMC - Weighted Method per Class)

  • Métrica originalmente proposta como a soma das medidas de complexidade ciclomática dos métodos da classe
  • Interpretação
    • WMC Maior → Maior a complexidade
    • Muitos métodos → Maior o potencial de impacto em possíveis subclasses

Profundidade da árvore de herança (DIT - Deep of Inheritance Tree)

  • Representa o número de níveis na hierarquia de classes
  • Calculada pela contabilização do número de classes que ela deriva (Indica o número de superclasses que podem afetar potencialmente a classe em questão)
  • Interpretação:
    • Maior a profundidade de uma classe na hierarquia (DIT maior) → maior o número de métodos que provavelmente ela irá herdar, tornando mais complexo compreender seu comportamento e maior o potencial de reuso dos métodos herdados
    • DIT maior → maior a complexidade do projeto (+ classes/métodos envolvidos)

Número de filhos (NOC - Number of Children)

  • É a medida do número de subclasses imediatas de uma classe, medindo sua ramificação na hierarquia de classes
  • Interpretação:
    • NOC alto → maior reuso (herança pode ser uma forma de reuso)
    • Quanto mais alto o NOC → maior a probabilidade de abstrações impróprias
    • Ele provê indícios da influência potencial de uma classe no projeto, demandando assim um maior número de testes

Acoplamento entre classes (CBO - Coupling of Object Classes)

É a medida do grau de acoplamento entre classes (Uma classe é acoplada a outra quando uma utiliza métodos ou atributos da outra)

  • Calculado pela contabilização do número de classes acopladas a ela
    • Contabilização bidirecional → o CBO de uma classe A é o tamanho do conjunto de classes que A referencia e das classes que fazem referência a A
    • Cada classe é contabilizada apenas uma vez, mesmo se a referência entre elas for bidirecional
  • Interpretação:
    • COB alto → classes altamente dependentes, menos modulares
    • Maior acoplamento → menor manutenibilidade, pois mudanças em uma classe impactarão (negativamente) nas demais
    • Maior número de relacionamentos entre classes → maior complexidade do código e da dificuldade para realização de testes

Resposta para uma Classe (RFC - Response for a Class)

  • É a medida do número de métodos invocados em resposta a uma mensagem recebida por um objeto dessa classe
  • Interpretação:
    • Maior o número de métodos → maior a complicação em teste e depuração
    • Maior RFC → maior a complexidade e a propensão a erros

Falta de coesão em métodos (LCOM - Lock of Cohesion in Methods)

  • Originalmente calculada como a diferença entre o número de pares de métodos sem atributos compartilhados (não similaridade)
  • Motivo de controvérsia
    • Diversas variações para medição
    • Não há evidências sobre a sua real utilidade em comparação às demais métricas CK
  • Interpretação:
    • Falta de coesão → classe deveria ser subdividida em duas ou mais subclasses
    • Baixo LCOM → boa subdivisão da classe analisada (simples, + reuso, + coesa)
    • Alto LCOM → maior complexidade e propensão a erros

Métricas MOOD

Conjunto complementar/alternativo às métricas CK para analisar programas OO em:

  • Encapsulamento
  • Herança
  • Polimorfismo
  • Troca de Mensagens

Fator de encapsulamento de método (MHF - Method Hiding Factor)

  • Definido como a soma das invisibilidades de todos os métodos das classes
    • A invisibilidade de um método diz respeito ao percentual da classe do qual o método é ocultado
  • Interpretação:
    • Maior MHF → mais métodos privados, diminuindo potencial de reuso
    • Baixo MHF → maior parte dos métodos possui visibilidade públic ou protected

Fator de encapsulamento de atributo (AHF - Attribute Hiding Factor)

  • Definido como a soma das invisibilidades de todos os atributos das classes
  • Interpretação:
    • Maior AHF → mais atributos privados, menor potencial de reuso
    • Baixo AHF → maior parte dos atributos possui visib. pública ou protectegida

Fator de herança de método (MIF - Method Inheritance Factor)

  • Definido como a soma dos métodos herdados em todas as classes

Fator de herança de atributo (AIF - Attribute Inheritance Factor)

  • Definido como a soma dos atributos herdados em todas as classes

Fator de Polimorfismo (PF - Polymorphism Factor)

  • Refere-se à razão entre o número de métodos que redefinem métodos herdados e o número máximo de possíveis situações polimórficas distintas
    • Caso em que todos os novos métodos em uma superclasse são sobrescritos
  • Interpretação:
    • Baixo PF → pouco ou nenhum uso de polimorfismo
    • Alto PF → maior número de métodos de uma superclasse são sobrescritos

Fator de Acoplamento (CF - Coupling Factor)

  • Refere-se à determinação do grau de acoplamento entre classes, desconsiderando os relacionamentos de herança
  • Interpretação:
    • Alto CF → maior complexidade → pior compreensão, manutenibilidade e reuso
    • Deve-se reduzir acoplamento o quanto possível

CK vs MOOD

Ambas tratam sobre classes, atributos, métodos, acoplamento e herança, mas apenas o MOOD trata sobre encapsulamento e polimorfismo

Programação Defensiva

  • É um conjunto de técnicas de projeto e programação objetivando a estabilidade e a segurança de um software independentemente de seu imprevisível
  • Pode ser vista como forma de reduzir ou eliminar os efeitos da Lei de Murphy
  • Consiste em cuidadosamente pensar antes de codificar e estabelecer todas as situações que poderão ocorrer no código antes de fazê-lo
  • Promove melhoria do software e seu código em termos de:
    • Qualidade Geral → reduzindo o número de bugs e problemas
    • Grau de compreensão → código legível e compreensível
    • Previsibilidade → software se comporte de forma previsível mesmo com entradas ou ações do usuário inesperadas

Técnicas de Programação defensiva:

  • Proteção contra entradas inválidas
  • "Programas problemáticos não mentem"
  • Design by Contract (DbC)
  • Assertivas
  • Alocação/Liberação
  • Manipulação de erros
  • Uso e tratamento de exceções

Proteção contra entradas inválidas

Verificar os valores de todos os dados vindos de fontes externas (entrada do usuário, arquivos, etc...) em termos de:

  • Intervalos
  • Tipos de dados
  • Tamanho de dados
  • Caracteres Inválidos
  • Expressões Válidas

Assim, é necessário:

  • Verificar os valores de todos os parmetros passados a uma rotina
  • Decidir adequadamente o que fazer em caso de entradas inválidas

"Programas Problemáticos não mentem"

  • Se em algum momento o programa apresentou um comportamento inesperado, há uma falha no código
    • Se um arquivo não pode ser aberto, ele não deve ter sido fechado corretamente em outro ponto do programa
    • Se não foi possível alocar memória, o programa não deve estar a liberando
  • Nunca ignorar mensagens de avisos (warnings) emitidas em tempo de compilação

Design by Contract (DbC)

  • Foca na documentação e verificação dos direitos e deveres de cada módulo de software, em termos de:
    • Pré-condições → condições para que a rotina possa ser chamada
    • Pós-condições → o que deve ser verdadeiro após a rotina ser executada
    • Invariantes → o que deve ser verdadeiro antes, durante e após a execução

Assertivas

  • É um código usado durante o desenvolvimento para confirmar que o programa está executando como esperado
    • Não possui o objetivo de substituir if/else ou try/catch
  • Assertivas devem ser usadas somente para situações que nunca devem acontecer
    • No ambiente de debug, não de produção

Manipulação de erros

  • Retornar valor padrão (neutro)
  • Substituir pelo próximo dado válido
  • Retornar a última resposta válida
  • Substituir por um valor válido mais próximo
  • Registrar um log em um arquivo (e continuar operando)
  • Retornar um código de erro para indicar o erro a quem chamou, se não for tratado localmente
  • Centralizar o tratamento de erros em uma rotina
  • Tratar os erros localmente, na rotina que produziu o erro
  • Mostrar uma mensagem de erro significativa (informativa)
  • Abortar o programa se um erro severo ocorreu

Uso e tratamento de exceções

  • Exceções são um meio específico pelo qual o código pode passar erros ou eventos excepcionais ao código que o chamou
  • Exceções, quando usadas com sabedoria, reduzem a complexidade, porém, quando usadas imprudentemente podem tornar o código quase impossível de seguir
  • Devem ser usadas para notificar outras partes do programa sobre erros que não devem ser ignorados
  • Exceções devem ser lançadas apenas para as condições que sejam realmente excepcionais
  • Incluir na mensagem de exceção todas as informações que levam à exceção
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment