SlideShare uma empresa Scribd logo
1 de 68
Baixar para ler offline
Complexidade Ciclomática
Uma Métrica de Qualidade sobre a
Testabilidade e Legibilidade de um
Software
Douglas Siviotti
Outubro de 2017
Sobre esta Apresentação
2/68
1. Conteúdo: Complexidade Ciclomática
2. Área/Foco: Qualidade de Software, Manutenção e Testabilidade
3. Público alvo: Desenvolvedores de Software e Gestores
4. Conteúdo relacionado: Cobertura de Testes, Entregas Contínuas
Organização (+- 60 minutos)
Parte 1 – Introdução: Conceito e Regra de Cálculo
Parte 2 – Aplicação: Padrão de Qualidade, Importância e Utilidade
Parte 3 – Prática: Refatoração e Redução
Parte 4 – Conclusão:
Parte 1 - Introdução
3/68
Conceito e Regra de Cálculo
Conceito
4/68
Complexidade Ciclomática é uma métrica de software que
indica a complexidade de um programa* (McCabe, 1976).
* Programa, método, rotina etc
Ela mede a quantidade máxima de caminhos
linearmente independentes no código fonte.
Conceito
5/68
Caminho Linearmente Independente é qualquer
caminho do programa que introduz pelo menos um
novo conjunto de comandos de processamento ou
uma nova condição. Quando definido em termos de
grafo de fluxo, um caminho independente deve incluir
pelo menos uma aresta que não tenha sido
atravessada antes de o caminho ser definido
(Pressman).
1
B X
2
C Y
A
Independentes: A-1-B-2-C-D A-1-X-2-C-D A-1-B-2-Y-D
Não Independente: A-1-X-2-Y-D
D
Exemplo 1
6/68
public int sum(int a, int b) {
int result = a + b;
System.out.println(result);
return result;
}
1
result = a + b
println(result)
return result
O código apresenta três
linhas e um único caminho.
O código apresenta três
linhas e um único caminho.
Teste de Unidade do Exemplo 1
7/68
1
result = a + b
println(result)
return result
O teste de unidade precisa
somente de um cenário de teste
( qualquer a + qualquer b)
O teste de unidade precisa
somente de um cenário de teste
( qualquer a + qualquer b)
@Test
public void testSum() {
assertEquals(2, obj.sum(1, 1));
}
Exemplo 2 – Soma Somente Diferentes
8/68
public int sum(int a, int b) {
int result= 0;
if (a != b) {
result = a + b;
}
System.out.println(result);
return result;
}
1
result = 0
println(result)
Não
Dois caminhos são possíveis:
1. Onde ‘a’ e ‘b’ são iguais
2. Onde ‘a’ e ‘b’ são diferentes
Dois caminhos são possíveis:
1. Onde ‘a’ e ‘b’ são iguais
2. Onde ‘a’ e ‘b’ são diferentes
a != b
return result
Sim
2
result = a + b
Teste de Unidade do Exemplo 2
9/68
1
result = 0
println(result)
Não
a != b
return result
Sim
2
result = a + b
Um cenário pode cobrir todas
as linhas, mas é prudente
escrever mais de um
Um cenário pode cobrir todas
as linhas, mas é prudente
escrever mais de um
@Test
public void test() {
// [1] cobre um caminho a==b
// cobre 80% das linhas
assertEquals(0, obj.sum(1, 1));
// [2] cobre outro caminho a!=b
// cobre 100% das linhas
assertEquals(3, obj.sum(1, 2));
}
Exemplo 3 – Ano Bissexto
10/68
public boolean isBissexto(int ano){
if (ano % 400 == 0){
return true;
}
if (ano % 4 == 0 && ano % 100 != 0){
return true;
}
return false;
}
return false% 400 == 0 return true
2
Sim
% 4 == 0
Não
Sim
1
Não
return false
% 100 == 0
Não
Sim
return true
3
4
Há 4 caminhos possíveis:
1. Divisível por 400 : true
2. Nem por 400 nem 4: false
3. Divisível somente por 4: true
4. Divisível por 4 E 100 : false
Há 4 caminhos possíveis:
1. Divisível por 400 : true
2. Nem por 400 nem 4: false
3. Divisível somente por 4: true
4. Divisível por 4 E 100 : false
Cenários do Exemplo 3
11/68
return false% 400 == 0 return true
2
Sim
% 4 == 0
Não
Sim
1
Não
return false
% 100 == 0
Não
Sim
return true
3
4
Há 4 caminhos possíveis:
1. Divisível por 400 : true
2. Nem por 400 nem 4: false
3. Divisível somente por 4: true
4. Divisível por 4 E 100 : false
Há 4 caminhos possíveis:
1. Divisível por 400 : true
2. Nem por 400 nem 4: false
3. Divisível somente por 4: true
4. Divisível por 4 E 100 : false
// 4 caminhos independentes
assertTrue(obj.isBissexto(1600));
assertFalse(obj.isBissexto(2001));
assertTrue(obj.isBissexto(2004));
assertFalse(obj.isBissexto(1900));
Exemplo 4 – Sorveteria (Tipo/Pote/Cobertura)
12/68
public int precoSorvete(boolean premium,
boolean casquinha, int coberturas) {
int preco = 0;
if (premium) {
preco = 20;
} else {
preco = 15;
}
if (casquinha) {
preco = preco + 2;
} else {
preco = preco + 1;
}
if (coberturas > 1){
preco = preco + 2;
} else {
preco = preco + 1;
}
return preco;
}
if
15 20
if
+1 +2
if
+1 +2
P
0
Quantos
Caminhos?
Quantos
Caminhos?
Exemplo 4 – Sorveteria (8 Caminhos Possíveis)
13/68
if
15 20
if
+1 +2
if
+1 +2
17
0
if
15 20
if
+1 +2
if
+1 +2
18
0
if
15 20
if
+1 +2
if
+1 +2
18
0
if
15 20
if
+1 +2
if
+1 +2
19
0
if
15 20
if
+1 +2
if
+1 +2
22
0
if
15 20
if
+1 +2
if
+1 +2
23
0
if
15 20
if
+1 +2
if
+1 +2
23
0
if
15 20
if
+1 +2
if
+1 +2
24
0Premium
Copinho
1 Cobertura
Comum
Copinho
1 Cobertura
Comum
Copinho
2 Coberturas
Comum
Casquinha
1 Cobertura
Comum
Casquinha
2 Coberturas
Premium
Copinho
2 Coberturas
Premium
Casquinha
1 Cobertura
Premium
Casquinha
2 Coberturas
Questão 1
14/68
É razoável criar testes de unidade com um cenário
para cada caminho possível de um método?
assertEquals(17, sorveteria.precoSorvete(false, false, 1)); // Comum-Copinho-1Cob
assertEquals(18, sorveteria.precoSorvete(false, false, 2)); // Comum-Copinho-2Cob
assertEquals(18, sorveteria.precoSorvete(false, true, 1)); // Comum-Casquinha-1Cob
assertEquals(19, sorveteria.precoSorvete(false, true, 2)); // Comum-Casquinha-2Cob
assertEquals(22, sorveteria.precoSorvete(true, false, 1)); // Premium-Copinho-1Cob
assertEquals(23, sorveteria.precoSorvete(true, false, 2)); // Premium-Copinho-2Cob
assertEquals(23, sorveteria.precoSorvete(true, true, 1)); // Premium-Casquinha-1Cob
assertEquals(24, sorveteria.precoSorvete(true, true, 2)); // Premium-Casquinha-2Cob
Mais um IF-ELSE aumenta os cenários para 16!
Exemplo 4 – Sorveteria (Complexidade Ciclomática = 4)
15/68
if
15 20
if
+1 +2
if
+1 +2
17
0
if
15 20
if
+1 +2
if
+1 +2
22
0
if
15 20
if
+1 +2
if
+1 +2
18
0
if
15 20
if
+1 +2
if
+1 +2
18
0A B C D
Exemplo 4 – Sorveteria1 (2 Cenários Cobrem 100%)
16/68
assertEquals(17,
sorveteria.precoSorvete
(false, false, 1));
assertEquals(24,
sorveteria.precoSorvete
(true, true, 2));
if
15 20
if
+1 +2
if
+1 +2
17
0
if
15 20
if
+1 +2
if
+1 +2
22
0A B
Exemplo 4 – Resultado da Cobertura
17/68
Complexidade Ciclomática 4
sugere que 4 cenários (bem feitos)
são suficientes para boa cobertura
de todas as linhas e condições
(apenas 2 são necessários)
Complexidade Ciclomática 4
sugere que 4 cenários (bem feitos)
são suficientes para boa cobertura
de todas as linhas e condições
(apenas 2 são necessários)
Exemplo 5 – Sorveteria 2 (Ifs sem Else)
18/68
if
+5
+1
+1
if
+1
P
15
+1
if
Quantos caminhos?Quantos caminhos?
public int precoSorvete(boolean premium,
boolean casquinha, int coberturas) {
int preco = 15;
if (premium) {
preco = preco + 5;
}
preco = preco + 1; // pote
if (casquinha) {
preco = preco + 1;
}
preco = preco + 1; // cobertura
if (coberturas > 1){
preco = preco + 1;
}
return preco;
}
Exemplo 5 – Sorveteria 2 (8 Caminhos)
19/68
if
+5
+1
+1
if
+1
18
15
+1
if
if
+5
+1
+1
if
+1
17
15
+1
if
if
+5
+1
+1
if
+1
18
15
+1
if
if
+5
+1
+1
if
+1
19
15
+1
if
if
+5
+1
+1
if
+1
23
15
+1
if
if
+5
+1
+1
if
+1
22
15
+1
if
if
+5
+1
+1
if
+1
23
15
+1
if
if
+5
+1
+1
if
+1
24
15
+1
if
Com
Copo
1 Cob
Prem
Copo
1 Cob
Com
Copo
2 Cob
Com
Casc
1 Cob
Com
Casc
2 Cob
Prem
Copo
2 Cob
Prem
CAsc
2 Cob
Prem
Casc
2 Cob
Exemplo 5 – Sorveteria 2 (Complexidade Ciclomática = 4)
20/68
B
if
+5
+1
+1
if
+1
P
15
+1
if
A
if
+5
+1
+1
if
+1
P
15
+1
if
C
if
+5
+1
+1
if
+1
P
15
+1
if
D
if
+5
+1
+1
if
+1
P
15
+1
if
4 Caminhos linearmente independentes
Exemplo 5 – Sorveteria 2 (1 Cenário cobre 100%)
21/68
if
+5
+1
+1
if
+1
P
15
+1
if
assertEquals(24,
sorveteria.precoSorvete
(true, true, 2));
Exemplo 5 – Resultado da Cobertura
22/68
Complexidade Ciclomática 4
sugere que 4 cenários (bem feitos)
são suficientes para boa cobertura
de todas as linhas e condições
(apenas 1 é necessário)
Complexidade Ciclomática 4
sugere que 4 cenários (bem feitos)
são suficientes para boa cobertura
de todas as linhas e condições
(apenas 1 é necessário)
Exemplo 6 – Sorveteria 3 (Ifs Aninhados)
23/68
Quantos caminhos?Quantos caminhos?
if17 20
if
+1
+2
If
+1 +2
P
0
public int precoSorvete(boolean premium, boolean casquinha, int coberturas) {
int preco = 0;
if (premium) { // só premiun tem casquinha
preco = 20;
if (casquinha) { // só casq tem cobe
preco = preco + 2;
if (coberturas > 1){
preco = preco + 2;
} else {
preco = preco + 1;
}
} else {
preco = preco + 1;
}
} else {
preco = 15 + 1 + 1; // copo + 1 cob
}
return preco;
}
Exemplo 6 – Sorveteria 3 (Complexidade Ciclomática = 4)
24/68
if17 20
if
+1
+2
If
+1 +2
P
0A
if17 20
if
+1
+2
If
+1 +2
P
0B
if17 20
if
+1
+2
If
+1 +2
P
0
D C
Similar ao exemplo “Ano Bissexto”. Cada “preco=” poderia ser um “return”
4 Caminhos
linearmente
independentes
Exemplo 6 – Sorveteria 3 (2 Cenários cobrem 77,6%)
25/68
assertEquals(17,
sorveteria.precoSorvete
(false, false, 1));
assertEquals(24,
sorveteria.precoSorvete
(true, true, 2));
if17 20
if
+1
+2
If
+1 +2
0A
if17 20
if
+1
+2
If
+1 24
0
C
Exemplo 6 – Sorveteria 3 (3 Cenários Cobrem 88,9%)
26/68
17 = (false, false, 1) // 1
24 = (true, true, 2)) // 2
23 = (true, true, 1)) // 3
if17 20
if
+1
+2
If
+1 +2
0A
if17 20
if
+1
+2
If
23 24
0
CD
Exemplo 6 – Sorveteria 3 (4 Cenários Cobrem 100%)
27/68
17 = (false, false, 1) // 1
24 = (true, true, 2)) // 2
23 = (true, true, 1)) // 3
21 = (true, false, 2)) // 4
if17 20
if
+1
+2
If
+1 +2
0A
if17 20
if
21
+2
If
23 24
0
CD
B
Exemplo 6 – Resultado da Cobertura
28/68
Complexidade Ciclomática 4
sugere que 4 cenários (bem feitos)
são suficientes para boa cobertura
de todas as linhas e condições
(4 são necessários)
Complexidade Ciclomática 4
sugere que 4 cenários (bem feitos)
são suficientes para boa cobertura
de todas as linhas e condições
(4 são necessários)
Exemplos 4, 5 e 6 – Sorveteria 1 x 2 x 3 (CC = 4 em Todos)
29/68
if
15 20
if
+1 +2
if
+1 +2
P
0
if
+5
+1
+1
if
+1
P
15
+1
if
1 2
Sorveterias 1 e 2
8 Caminhos
4 Circuitos
1 = 2 Cenários
2 = 1 Cenário
Sorveteria 3
4 Caminhos
4 Circuitos
4 Cenários
if17 20
if
+1
+2
If
+1 +2
P
03
Exemplos 4, 5 e 6 – NPATH x Complexidade Ciclomática
30/68
@Test
public void testAllPaths() { // Baseado em NPATH
assertEquals(17, sorveteria.precoSorvete(false, false, 1)); // Comum-Copinho-1Cob
assertEquals(18, sorveteria.precoSorvete(false, false, 2)); // Comum-Copinho-2Cob
assertEquals(18, sorveteria.precoSorvete(false, true, 1)); // Comum-Casquinha-1Cob
assertEquals(19, sorveteria.precoSorvete(false, true, 2)); // Comum-Casquinha-2Cob
assertEquals(22, sorveteria.precoSorvete(true, false, 1)); // Premium-Copinho-1Cob
assertEquals(23, sorveteria.precoSorvete(true, false, 2)); // Premium-Copinho-2Cob
assertEquals(23, sorveteria.precoSorvete(true, true, 1)); // Premium-Casquinha-1Cob
assertEquals(24, sorveteria.precoSorvete(true, true, 2)); // Premium-Casquinha-2Cob
}
@Test
public void testIndependentPaths() { // Baseado em CC
assertEquals(17, sorveteria.precoSorvete(false, false, 1)); // Comum-Copinho-1Cob
assertEquals(23, sorveteria.precoSorvete(true, true, 1)); // Premium-Casquinha-1Cob
assertEquals(21, sorveteria.precoSorvete(true, false, 2)); // Premium-Copinho-2Cob
assertEquals(24, sorveteria.precoSorvete(true, true, 2)); // Premium-Casquinha-2Cob
}
A Sorveteria com 9 IF-ELSE teria 512 caminhos, mas CC = 10 (linhas do teste)
Limite Máximo de Cenários
31/68
Dependendo do algoritmo, a quantidade de
caminhos únicos pode variar, mas o valor de
complexidade ciclomática é o limite superior do
número de cenários necessários para cobrir 100%
das linhas e condições, se definidos corretamente
A Sorveteria com 9 IF-ELSE teria 512 caminhos, mas CC = 10
Correlação 1 - Testabilidade
32/68
Se não há impacto é porque não há teste de unidade
Quanto maior a complexidade ciclomática em
um método, maior tende a ser o número de
cenários necessários em um teste deste
mesmo método.
Correlação 2 - Manutenção
33/68
Quanto maior a complexidade ciclomática em um
método, maior será a dificuldade de entendimento
e o risco de inserção de novos defeitos neste
método, aumentando o esforço de manutenção.
Durante a construção inicial o impacto é mais baixo. O autor ainda entende o próprio código
Cálculo da Complexidade Ciclomática
34/68
Nó
Aresta
CC = Arestas – Nós + 1
if17 20
if
+1
+2
If
+1 +2
P
0
CC = 14 – 11 + 1 = 4
Preciso desenhar um
grafo para cada método
para calcular?
Preciso desenhar um
grafo para cada método
para calcular?
Regra de Cálculo Baseada em Keywords (Sonar)
35/68
1. Inicia em “Um” para o método (com ou sem retorno)
2. Adiciona “Um” para cada elemento de fluxo abaixo:
2.1 Seleção if, case
2.2 Loops for, while, do-while, break, e
continue.
2.3 Operadores &&, ||, ?
2.4 Exceções catch, finally, throw, ou throws
2.5 Fluxo return que não seja o último
(else, default, finally, : e return não incrementam o valor)
Regra de Cálculo Baseada em Keywords (Análise Estática)
36/68
Exercício de Contagem (Soma Diferentes – Exemplo 2)
37/68
?
public int sum(int a, int b) {
int result= 0;
if (a != b) {
result = a + b;
}
System.out.println(result);
return result;
}
Resultado
38/68
2
public int sum(int a, int b) {
int result= 0;
if (a != b) {
result = a + b;
}
System.out.println(result);
return result;
}
+1
+1
Exercício de Contagem (Ano Bissexto – Exemplo 3)
39/68
?
public boolean isBissexto(int ano){
if (ano % 400 == 0){
return true;
}
if (ano % 4 == 0 && ano % 100 != 0){
return true;
}
return false;
}
Resultado
40/68
6
public boolean isBissexto(int ano){
if (ano % 400 == 0){
return true;
}
if (ano % 4 == 0 && ano % 100 != 0){
return true;
}
return false;
}
+1
+1
+1
+1
+1
+1
Resultado para Implementação sem “Return”
41/68
4
public boolean isBissexto(int ano){
boolean result = false;
if (ano % 400 == 0){
result = true;
}
if (ano % 4 == 0 && ano % 100 != 0){
result = true;
}
return result;
}
+1
+1
+1
+1
Resultado para Implementação mais Enxuta
42/68
3
public boolean isBissexto(int ano){
return ano % 400 == 0 || (ano % 4 == 0 && ano % 100 != 0);
}
+1+1
+1
assertTrue(obj.isBissexto(1600));
assertFalse(obj.isBissexto(2001));
assertTrue(obj.isBissexto(2004));
assertFalse(obj.isBissexto(1900));
Modo de Uso
43/68
O número exato não é a coisa mais importante!
Complexidade Ciclomática serve para acompanhamento
da evolução do software durante a construção
Premissa: Ferramenta de Análise Estática de Código
Desafio de Contagem
44/68
?
Contador de Constantes
Inteiras
Resultado (Sonar)
45/68
21
Parte 2 – Aplicação
46/68
Padrão de Qualidade
Importância
Utilidade
Padrão de Qualidade sobre Complexidade Ciclomática (CC)
47/68
10
Quanto menor, melhor
Mais fácil de testar e manter
Valor limite do Sonar: até 10
(Acima de 10 gera uma violação)
Relação direta com casos de testes
Quanto mais alta, mais complexo
Mais responsabilidades por método
> 50 = quase impossível de manter
(até para o desenvolvedor original)
Alta CC sugere pouco OO
(+ procedural)
Padrão de Qualidade sobre Complexidade Ciclomática (CC)
48/68
<10!
10 é o limite, não o ideal (é alto)
CC alta é um ponto quente
- Maior necessidade de testes
- Maior probabilidade de erro
- Maior necessidade de
manutenção
A média deve tender a 1
Padrão de Qualidade sobre Complexidade Ciclomática (CC)
49/68
10
Segundo Thomas J. McCabe (1976):
01-10 Método Simples. Baixo Risco
11-20 Método razoavelmente complexo. Risco
moderado.
21-50 Método muito complexo. Risco elevado.
51-N Método de altíssimo risco e instável
Padrão de Qualidade sobre Complexidade Ciclomática (CC)
50/68
5
Segundo Steve McConnell (Code Complete 1993) :
De 0 a 5: Seu código está, provavelmente, ok.
Entre 6 e 10: Pense em maneiras de simplificar o
código
Maior que 10: Quebre o código em dois e insira uma
chamada da primeira parte para a segunda.
Padrão de Qualidade sobre Complexidade Ciclomática (CC)
51/68
Segundo SonarSource (sonarsource.com):
Gerencie como “Vazamento de Água” (Water Leak)
“Fix Issues Before They Exist” (Resolva antes que exista)
Utilização do Plugin SonarLint
A melhor abordagem sobre
complexidade é mantê-la baixa
Impactos Diretos da Complexidade Ciclomática
52/68
Complexidade
Ciclomática
Complexidade
Ciclomática
TestabilidadeTestabilidade
LegibilidadeLegibilidade
< Tamanho< Tamanho
GranularidadeGranularidade
CoesãoCoesão
Impactos Diretos e Indiretos da Complexidade Ciclomática
53/68
Complexidade
Ciclomática
Complexidade
Ciclomática
TestabilidadeTestabilidade
LegibilidadeLegibilidade
< Tamanho< Tamanho Facilidade
de Manutenção
Facilidade
de Manutenção
CoberturaCobertura
- Defeitos- Defeitos
GranularidadeGranularidade ReusoReuso
CoesãoCoesão < Acoplamento< Acoplamento
Impactos Diretos e Indiretos da Complexidade Ciclomática
54/68
Complexidade
Ciclomática
Complexidade
Ciclomática
TestabilidadeTestabilidade
LegibilidadeLegibilidade
< Tamanho< Tamanho Facilidade
de Manutenção
Facilidade
de Manutenção
CoberturaCobertura
- Defeitos- Defeitos
GranularidadeGranularidade
AutomaçãoAutomação
ReusoReuso
CoesãoCoesão < Acoplamento< Acoplamento
Capacidade
de Evolução
Capacidade
de Evolução
Impactos Diretos e Indiretos da Complexidade Ciclomática
55/68
Complexidade
Ciclomática
Complexidade
Ciclomática
TestabilidadeTestabilidade
LegibilidadeLegibilidade
< Tamanho< Tamanho Facilidade
de Manutenção
Facilidade
de Manutenção
CoberturaCobertura
Veloc. Entrega
Qualidade
Satisfação
Veloc. Entrega
Qualidade
Satisfação
- Defeitos- Defeitos
GranularidadeGranularidade
AutomaçãoAutomação
ReusoReuso
CoesãoCoesão < Acoplamento< Acoplamento
Capacidade
de Evolução
Capacidade
de Evolução
Ciclo Vicioso
56/68
Prazo
Curto
Horda de
Mongóis
Dívida
Técnica
Perda de
Qualidade
Quebra do Ciclo Vicioso
57/68
Prazo
Curto
KEEP
CALM
AND ...
Dívida
Técnica
Perda de
Qualidade
CC
Baixa
Teste de
Unidade
Confiança
Fluidez
Refatoração
Parte 3 – Prática
58/68
Refatoração e Redução
da Complexidade Ciclomática
Refatoração
59/68
Refatoração é uma técnica para reestruturar um
código existente, alterando seu estado interno
sem alterar seu comportamento externo.
Ela consiste em uma série de pequenas transformações com a preservação do
comportamento original. Cada transformação faz pouco, mas uma sequência de
transformações pode produzir uma reestruturação significativa. Uma vez que cada
refatoração é pequena, é menos provável que dê errado.
Vazamento de Água: Refatoração como Atividade Corriqueira
60/68
ConstruçãoConstrução
Análise EstáticaAnálise Estática
CC Alta?CC Alta?Não
RefatoraçãoRefatoração
Sim
Tratar?Tratar? Não
Dívida TécnicaDívida Técnica
Sim
Exercício de Refatoração
61/68
Necessidade: A partir de uma classe qualquer retorne a quantidade de
constantes (final static) inteiras* presentes nessa classe incluindo as
constantes herdadas das superclasses.
* Inteiros: Integer, int, Long, long, Short, short, Byte, byte e BigInteger
** Se a classe é nula, Enum ou abstrata deve ser retornado “-1”.
Solução Inicial
62/68
O que o método faz?
Como reduzir a
Complexidade Ciclomática
de 21 para menos de 10?
Porque deu 21?
Está muito procedural?
Pode ser desmembrado?
Análise do Método
63/68
2
1
3
O método faz quatro coisas:
1 – Pré-condição
2 – Obtém os campos
3 – Classifica Int Const
4 – Contabiliza
Decomposição funcional
Possivelmente recursivo
4
Primeira Refatoração
64/68
1
1
2
2
3
3
4
- Criação de 3 métodos
- CC do método “countIntConst”
desceu de 21 para 5
- Soma das CC dá 23 (4
métodos)
- método “isIntConst” deu 11
Apesar de totalizar 23, os
métodos ficaram muito mais
testáveis e com
responsabilidades únicas
Refatoração Final
65/68
- Troca de “Ous” por um conjunto
com os tipos inteiros
- Soma das CC dá 14 (4 métodos)
- “isIntConst” desceu de 11 para 3
3
3
5
3
Resultado da Refatoração
66/68
Um método (CC = 21) deu lugar a quatro métodos (CC = 5,3,3,3)
Os métodos extraídos fazem só uma coisa e são facilmente reusáveis
O esforço de construção e teste de unidade é mais ou menos o mesmo,
mas a manutenção será muito mais simples no futuro
O método principal ficou com a leitura mais fácil, pois ele orquestra a
funcionalidade invocando outras três, adicionando semântica
Dicas Finais
67/68
Assim como no exemplo, Complexidade Ciclomática alta é um indício de
uma provável necessidade e/ou oportunidade de refatoração
Complexidade Ciclomática alta é indício de baixa ou má cobertura de
testes de unidade. É difícil testar e cobrir um código complexo
Uso frequente de análise estática com refatoração ajuda a manter a
complexidade baixa. Trate o vazamento de água continuamente
Muito Obrigado!
Dúvidas?
Douglas Siviotti de Alcantara
douglas.siviotti@gmail.com
github.com/siviotti
www.artesoftware.com.br
Código fonte usado nesta apresentação:
https://github.com/siviotti/ops/tree/master/src/main/java/siviotti/ops/example/cc

Mais conteúdo relacionado

Mais procurados

Exercício resolvido normalização
Exercício resolvido normalizaçãoExercício resolvido normalização
Exercício resolvido normalização
Ticianne Darin
 
O comparativo de arquiteturas de software monolíticas em relação a arquitetur...
O comparativo de arquiteturas de software monolíticas em relação a arquitetur...O comparativo de arquiteturas de software monolíticas em relação a arquitetur...
O comparativo de arquiteturas de software monolíticas em relação a arquitetur...
Emmanuel Neri
 
Introdução a estruturas de dados em python
Introdução a estruturas de dados em pythonIntrodução a estruturas de dados em python
Introdução a estruturas de dados em python
Alvaro Oliveira
 

Mais procurados (20)

Fundamentos de arquitetura Web
Fundamentos de arquitetura WebFundamentos de arquitetura Web
Fundamentos de arquitetura Web
 
Curso Treinamento Automação de testes com Selenium Qualister
Curso Treinamento Automação de testes com Selenium QualisterCurso Treinamento Automação de testes com Selenium Qualister
Curso Treinamento Automação de testes com Selenium Qualister
 
Exercício resolvido normalização
Exercício resolvido normalizaçãoExercício resolvido normalização
Exercício resolvido normalização
 
Java: Composicao e Array List
Java: Composicao e Array ListJava: Composicao e Array List
Java: Composicao e Array List
 
Verificação, Validação e Teste de Software
Verificação, Validação e Teste de SoftwareVerificação, Validação e Teste de Software
Verificação, Validação e Teste de Software
 
Banco de Dados II Aula 14 - Projeto de Banco de Dados e Estudo de Caso (Postg...
Banco de Dados II Aula 14 - Projeto de Banco de Dados e Estudo de Caso (Postg...Banco de Dados II Aula 14 - Projeto de Banco de Dados e Estudo de Caso (Postg...
Banco de Dados II Aula 14 - Projeto de Banco de Dados e Estudo de Caso (Postg...
 
O comparativo de arquiteturas de software monolíticas em relação a arquitetur...
O comparativo de arquiteturas de software monolíticas em relação a arquitetur...O comparativo de arquiteturas de software monolíticas em relação a arquitetur...
O comparativo de arquiteturas de software monolíticas em relação a arquitetur...
 
Conceitos e fundamentos sobre testes de software e garantia da qualidade
Conceitos e fundamentos sobre testes de software e garantia da qualidadeConceitos e fundamentos sobre testes de software e garantia da qualidade
Conceitos e fundamentos sobre testes de software e garantia da qualidade
 
인프콘 2022 - Rust 크로스 플랫폼 프로그래밍
인프콘 2022 - Rust 크로스 플랫폼 프로그래밍인프콘 2022 - Rust 크로스 플랫폼 프로그래밍
인프콘 2022 - Rust 크로스 플랫폼 프로그래밍
 
Classes e Objectos JAVA
Classes e Objectos JAVAClasses e Objectos JAVA
Classes e Objectos JAVA
 
Aula 02 - UML e Padrões de Projeto
Aula 02 - UML e Padrões de ProjetoAula 02 - UML e Padrões de Projeto
Aula 02 - UML e Padrões de Projeto
 
Introdução a estruturas de dados em python
Introdução a estruturas de dados em pythonIntrodução a estruturas de dados em python
Introdução a estruturas de dados em python
 
Introdução a ciência de dados com Python
Introdução a ciência de dados com PythonIntrodução a ciência de dados com Python
Introdução a ciência de dados com Python
 
Minicurso PostgreSQl
Minicurso PostgreSQlMinicurso PostgreSQl
Minicurso PostgreSQl
 
DB1 Unidad 1: Sistemas de almacenamiento de la información
DB1 Unidad 1: Sistemas de almacenamiento de la informaciónDB1 Unidad 1: Sistemas de almacenamiento de la información
DB1 Unidad 1: Sistemas de almacenamiento de la información
 
Introdução ao Teste de Software - Uma abordagem prática
Introdução ao Teste de Software - Uma abordagem práticaIntrodução ao Teste de Software - Uma abordagem prática
Introdução ao Teste de Software - Uma abordagem prática
 
Testes de Software
Testes de SoftwareTestes de Software
Testes de Software
 
Html Básico
Html BásicoHtml Básico
Html Básico
 
Teste de software
Teste de softwareTeste de software
Teste de software
 
Mpsbr
MpsbrMpsbr
Mpsbr
 

Semelhante a Complexidade Ciclomática

Controle de Fluxo, Exceções, Assertivas
Controle de Fluxo, Exceções, AssertivasControle de Fluxo, Exceções, Assertivas
Controle de Fluxo, Exceções, Assertivas
Elenilson Vieira
 
Técnicas de modelagem de teste (parte 2)
Técnicas de modelagem de teste (parte 2)Técnicas de modelagem de teste (parte 2)
Técnicas de modelagem de teste (parte 2)
Fabrício Campos
 
T08_T09_LM3: Estruturas de repetição (2013-2014)
T08_T09_LM3: Estruturas de repetição (2013-2014)T08_T09_LM3: Estruturas de repetição (2013-2014)
T08_T09_LM3: Estruturas de repetição (2013-2014)
Carlos Santos
 
(A09) LabMM3 - JavaScript - Estruturas de repetição
(A09) LabMM3 - JavaScript - Estruturas de repetição(A09) LabMM3 - JavaScript - Estruturas de repetição
(A09) LabMM3 - JavaScript - Estruturas de repetição
Carlos Santos
 

Semelhante a Complexidade Ciclomática (20)

Exercicios de exameti sistemas_2013_sol
Exercicios de  exameti sistemas_2013_solExercicios de  exameti sistemas_2013_sol
Exercicios de exameti sistemas_2013_sol
 
Exercicios de exameti sistemas_2013_sol
Exercicios de exameti sistemas_2013_solExercicios de exameti sistemas_2013_sol
Exercicios de exameti sistemas_2013_sol
 
Controle de Fluxo, Exceções, Assertivas
Controle de Fluxo, Exceções, AssertivasControle de Fluxo, Exceções, Assertivas
Controle de Fluxo, Exceções, Assertivas
 
TDC2013 Otimizando-C
TDC2013 Otimizando-CTDC2013 Otimizando-C
TDC2013 Otimizando-C
 
tdc-2023-bh-ciclomatica-ou-cognitiva.pdf
tdc-2023-bh-ciclomatica-ou-cognitiva.pdftdc-2023-bh-ciclomatica-ou-cognitiva.pdf
tdc-2023-bh-ciclomatica-ou-cognitiva.pdf
 
Pós Ruy - 2 e 3 Camadas - Teste de componentes
Pós Ruy - 2 e 3 Camadas - Teste de componentesPós Ruy - 2 e 3 Camadas - Teste de componentes
Pós Ruy - 2 e 3 Camadas - Teste de componentes
 
Aula 4 poe
Aula 4 poeAula 4 poe
Aula 4 poe
 
Grafos e Algoritimos - Dr. Julio Cesar de Araujo Menezes
Grafos e Algoritimos - Dr. Julio Cesar de Araujo MenezesGrafos e Algoritimos - Dr. Julio Cesar de Araujo Menezes
Grafos e Algoritimos - Dr. Julio Cesar de Araujo Menezes
 
Técnicas de modelagem de teste (parte 2)
Técnicas de modelagem de teste (parte 2)Técnicas de modelagem de teste (parte 2)
Técnicas de modelagem de teste (parte 2)
 
Cálculo numérico
Cálculo numéricoCálculo numérico
Cálculo numérico
 
T08_T09_LM3: Estruturas de repetição (2013-2014)
T08_T09_LM3: Estruturas de repetição (2013-2014)T08_T09_LM3: Estruturas de repetição (2013-2014)
T08_T09_LM3: Estruturas de repetição (2013-2014)
 
(A09) LabMM3 - JavaScript - Estruturas de repetição
(A09) LabMM3 - JavaScript - Estruturas de repetição(A09) LabMM3 - JavaScript - Estruturas de repetição
(A09) LabMM3 - JavaScript - Estruturas de repetição
 
Métodos Quantitativos Aplicados à Logística
Métodos Quantitativos Aplicados à LogísticaMétodos Quantitativos Aplicados à Logística
Métodos Quantitativos Aplicados à Logística
 
DOJO - TDD com C++
DOJO - TDD com C++DOJO - TDD com C++
DOJO - TDD com C++
 
TDD em C++
TDD em C++TDD em C++
TDD em C++
 
GOTEST-Aula4-Automacao-Parte1.pdf
GOTEST-Aula4-Automacao-Parte1.pdfGOTEST-Aula4-Automacao-Parte1.pdf
GOTEST-Aula4-Automacao-Parte1.pdf
 
Algoritimo de threads
Algoritimo de threadsAlgoritimo de threads
Algoritimo de threads
 
8a. aula -_estrutura_de_controle_de_repeticao_-_while_java
8a. aula -_estrutura_de_controle_de_repeticao_-_while_java8a. aula -_estrutura_de_controle_de_repeticao_-_while_java
8a. aula -_estrutura_de_controle_de_repeticao_-_while_java
 
Aula3
Aula3Aula3
Aula3
 
Complexidade Cognitiva
Complexidade CognitivaComplexidade Cognitiva
Complexidade Cognitiva
 

Mais de Douglas Siviotti

Mais de Douglas Siviotti (19)

tdc-2022-poa-quem-tem-medo-low-code.pdf
tdc-2022-poa-quem-tem-medo-low-code.pdftdc-2022-poa-quem-tem-medo-low-code.pdf
tdc-2022-poa-quem-tem-medo-low-code.pdf
 
tdc-2022-poa-lgpd-metaverso.pdf
tdc-2022-poa-lgpd-metaverso.pdftdc-2022-poa-lgpd-metaverso.pdf
tdc-2022-poa-lgpd-metaverso.pdf
 
TDC Future 2021 - Privacy After Design
TDC Future 2021 - Privacy After DesignTDC Future 2021 - Privacy After Design
TDC Future 2021 - Privacy After Design
 
TDC Connections 2021 Clausula de Guarda
TDC Connections 2021 Clausula de GuardaTDC Connections 2021 Clausula de Guarda
TDC Connections 2021 Clausula de Guarda
 
TDC Connections 2021 Artigo 37 da LGPD
TDC Connections 2021 Artigo 37 da LGPDTDC Connections 2021 Artigo 37 da LGPD
TDC Connections 2021 Artigo 37 da LGPD
 
Como o SERPRO Atende os Direitos dos Titulares
Como o SERPRO Atende os Direitos dos TitularesComo o SERPRO Atende os Direitos dos Titulares
Como o SERPRO Atende os Direitos dos Titulares
 
Tdc 2021-innovation-lgpd-dados-pessoais
Tdc 2021-innovation-lgpd-dados-pessoaisTdc 2021-innovation-lgpd-dados-pessoais
Tdc 2021-innovation-lgpd-dados-pessoais
 
Artesoftware Explicando LGPD
Artesoftware Explicando LGPDArtesoftware Explicando LGPD
Artesoftware Explicando LGPD
 
tdc-2020-poa-pedra-tesoura-papel
tdc-2020-poa-pedra-tesoura-papeltdc-2020-poa-pedra-tesoura-papel
tdc-2020-poa-pedra-tesoura-papel
 
Tdc 2020-poa-data-protection-full-stack
Tdc 2020-poa-data-protection-full-stackTdc 2020-poa-data-protection-full-stack
Tdc 2020-poa-data-protection-full-stack
 
tdc-recife-2020-complexidade-cognitiva
tdc-recife-2020-complexidade-cognitivatdc-recife-2020-complexidade-cognitiva
tdc-recife-2020-complexidade-cognitiva
 
tdc-recife-2020-lgpd-para-desenvolvedores
tdc-recife-2020-lgpd-para-desenvolvedorestdc-recife-2020-lgpd-para-desenvolvedores
tdc-recife-2020-lgpd-para-desenvolvedores
 
Privacidade By Design no Ciclo de Vida do Produto
Privacidade By Design no Ciclo de Vida do ProdutoPrivacidade By Design no Ciclo de Vida do Produto
Privacidade By Design no Ciclo de Vida do Produto
 
clean code
clean codeclean code
clean code
 
Privacidade By Design
Privacidade By DesignPrivacidade By Design
Privacidade By Design
 
LGPD Lei Geral de Proteção de Dados Pessoais
LGPD Lei Geral de Proteção de Dados PessoaisLGPD Lei Geral de Proteção de Dados Pessoais
LGPD Lei Geral de Proteção de Dados Pessoais
 
Clean Code na Prática
Clean Code na PráticaClean Code na Prática
Clean Code na Prática
 
Negócio Escrito em Código
Negócio Escrito em CódigoNegócio Escrito em Código
Negócio Escrito em Código
 
Dívida Técnica
Dívida TécnicaDívida Técnica
Dívida Técnica
 

Complexidade Ciclomática

  • 1. Complexidade Ciclomática Uma Métrica de Qualidade sobre a Testabilidade e Legibilidade de um Software Douglas Siviotti Outubro de 2017
  • 2. Sobre esta Apresentação 2/68 1. Conteúdo: Complexidade Ciclomática 2. Área/Foco: Qualidade de Software, Manutenção e Testabilidade 3. Público alvo: Desenvolvedores de Software e Gestores 4. Conteúdo relacionado: Cobertura de Testes, Entregas Contínuas Organização (+- 60 minutos) Parte 1 – Introdução: Conceito e Regra de Cálculo Parte 2 – Aplicação: Padrão de Qualidade, Importância e Utilidade Parte 3 – Prática: Refatoração e Redução Parte 4 – Conclusão:
  • 3. Parte 1 - Introdução 3/68 Conceito e Regra de Cálculo
  • 4. Conceito 4/68 Complexidade Ciclomática é uma métrica de software que indica a complexidade de um programa* (McCabe, 1976). * Programa, método, rotina etc Ela mede a quantidade máxima de caminhos linearmente independentes no código fonte.
  • 5. Conceito 5/68 Caminho Linearmente Independente é qualquer caminho do programa que introduz pelo menos um novo conjunto de comandos de processamento ou uma nova condição. Quando definido em termos de grafo de fluxo, um caminho independente deve incluir pelo menos uma aresta que não tenha sido atravessada antes de o caminho ser definido (Pressman). 1 B X 2 C Y A Independentes: A-1-B-2-C-D A-1-X-2-C-D A-1-B-2-Y-D Não Independente: A-1-X-2-Y-D D
  • 6. Exemplo 1 6/68 public int sum(int a, int b) { int result = a + b; System.out.println(result); return result; } 1 result = a + b println(result) return result O código apresenta três linhas e um único caminho. O código apresenta três linhas e um único caminho.
  • 7. Teste de Unidade do Exemplo 1 7/68 1 result = a + b println(result) return result O teste de unidade precisa somente de um cenário de teste ( qualquer a + qualquer b) O teste de unidade precisa somente de um cenário de teste ( qualquer a + qualquer b) @Test public void testSum() { assertEquals(2, obj.sum(1, 1)); }
  • 8. Exemplo 2 – Soma Somente Diferentes 8/68 public int sum(int a, int b) { int result= 0; if (a != b) { result = a + b; } System.out.println(result); return result; } 1 result = 0 println(result) Não Dois caminhos são possíveis: 1. Onde ‘a’ e ‘b’ são iguais 2. Onde ‘a’ e ‘b’ são diferentes Dois caminhos são possíveis: 1. Onde ‘a’ e ‘b’ são iguais 2. Onde ‘a’ e ‘b’ são diferentes a != b return result Sim 2 result = a + b
  • 9. Teste de Unidade do Exemplo 2 9/68 1 result = 0 println(result) Não a != b return result Sim 2 result = a + b Um cenário pode cobrir todas as linhas, mas é prudente escrever mais de um Um cenário pode cobrir todas as linhas, mas é prudente escrever mais de um @Test public void test() { // [1] cobre um caminho a==b // cobre 80% das linhas assertEquals(0, obj.sum(1, 1)); // [2] cobre outro caminho a!=b // cobre 100% das linhas assertEquals(3, obj.sum(1, 2)); }
  • 10. Exemplo 3 – Ano Bissexto 10/68 public boolean isBissexto(int ano){ if (ano % 400 == 0){ return true; } if (ano % 4 == 0 && ano % 100 != 0){ return true; } return false; } return false% 400 == 0 return true 2 Sim % 4 == 0 Não Sim 1 Não return false % 100 == 0 Não Sim return true 3 4 Há 4 caminhos possíveis: 1. Divisível por 400 : true 2. Nem por 400 nem 4: false 3. Divisível somente por 4: true 4. Divisível por 4 E 100 : false Há 4 caminhos possíveis: 1. Divisível por 400 : true 2. Nem por 400 nem 4: false 3. Divisível somente por 4: true 4. Divisível por 4 E 100 : false
  • 11. Cenários do Exemplo 3 11/68 return false% 400 == 0 return true 2 Sim % 4 == 0 Não Sim 1 Não return false % 100 == 0 Não Sim return true 3 4 Há 4 caminhos possíveis: 1. Divisível por 400 : true 2. Nem por 400 nem 4: false 3. Divisível somente por 4: true 4. Divisível por 4 E 100 : false Há 4 caminhos possíveis: 1. Divisível por 400 : true 2. Nem por 400 nem 4: false 3. Divisível somente por 4: true 4. Divisível por 4 E 100 : false // 4 caminhos independentes assertTrue(obj.isBissexto(1600)); assertFalse(obj.isBissexto(2001)); assertTrue(obj.isBissexto(2004)); assertFalse(obj.isBissexto(1900));
  • 12. Exemplo 4 – Sorveteria (Tipo/Pote/Cobertura) 12/68 public int precoSorvete(boolean premium, boolean casquinha, int coberturas) { int preco = 0; if (premium) { preco = 20; } else { preco = 15; } if (casquinha) { preco = preco + 2; } else { preco = preco + 1; } if (coberturas > 1){ preco = preco + 2; } else { preco = preco + 1; } return preco; } if 15 20 if +1 +2 if +1 +2 P 0 Quantos Caminhos? Quantos Caminhos?
  • 13. Exemplo 4 – Sorveteria (8 Caminhos Possíveis) 13/68 if 15 20 if +1 +2 if +1 +2 17 0 if 15 20 if +1 +2 if +1 +2 18 0 if 15 20 if +1 +2 if +1 +2 18 0 if 15 20 if +1 +2 if +1 +2 19 0 if 15 20 if +1 +2 if +1 +2 22 0 if 15 20 if +1 +2 if +1 +2 23 0 if 15 20 if +1 +2 if +1 +2 23 0 if 15 20 if +1 +2 if +1 +2 24 0Premium Copinho 1 Cobertura Comum Copinho 1 Cobertura Comum Copinho 2 Coberturas Comum Casquinha 1 Cobertura Comum Casquinha 2 Coberturas Premium Copinho 2 Coberturas Premium Casquinha 1 Cobertura Premium Casquinha 2 Coberturas
  • 14. Questão 1 14/68 É razoável criar testes de unidade com um cenário para cada caminho possível de um método? assertEquals(17, sorveteria.precoSorvete(false, false, 1)); // Comum-Copinho-1Cob assertEquals(18, sorveteria.precoSorvete(false, false, 2)); // Comum-Copinho-2Cob assertEquals(18, sorveteria.precoSorvete(false, true, 1)); // Comum-Casquinha-1Cob assertEquals(19, sorveteria.precoSorvete(false, true, 2)); // Comum-Casquinha-2Cob assertEquals(22, sorveteria.precoSorvete(true, false, 1)); // Premium-Copinho-1Cob assertEquals(23, sorveteria.precoSorvete(true, false, 2)); // Premium-Copinho-2Cob assertEquals(23, sorveteria.precoSorvete(true, true, 1)); // Premium-Casquinha-1Cob assertEquals(24, sorveteria.precoSorvete(true, true, 2)); // Premium-Casquinha-2Cob Mais um IF-ELSE aumenta os cenários para 16!
  • 15. Exemplo 4 – Sorveteria (Complexidade Ciclomática = 4) 15/68 if 15 20 if +1 +2 if +1 +2 17 0 if 15 20 if +1 +2 if +1 +2 22 0 if 15 20 if +1 +2 if +1 +2 18 0 if 15 20 if +1 +2 if +1 +2 18 0A B C D
  • 16. Exemplo 4 – Sorveteria1 (2 Cenários Cobrem 100%) 16/68 assertEquals(17, sorveteria.precoSorvete (false, false, 1)); assertEquals(24, sorveteria.precoSorvete (true, true, 2)); if 15 20 if +1 +2 if +1 +2 17 0 if 15 20 if +1 +2 if +1 +2 22 0A B
  • 17. Exemplo 4 – Resultado da Cobertura 17/68 Complexidade Ciclomática 4 sugere que 4 cenários (bem feitos) são suficientes para boa cobertura de todas as linhas e condições (apenas 2 são necessários) Complexidade Ciclomática 4 sugere que 4 cenários (bem feitos) são suficientes para boa cobertura de todas as linhas e condições (apenas 2 são necessários)
  • 18. Exemplo 5 – Sorveteria 2 (Ifs sem Else) 18/68 if +5 +1 +1 if +1 P 15 +1 if Quantos caminhos?Quantos caminhos? public int precoSorvete(boolean premium, boolean casquinha, int coberturas) { int preco = 15; if (premium) { preco = preco + 5; } preco = preco + 1; // pote if (casquinha) { preco = preco + 1; } preco = preco + 1; // cobertura if (coberturas > 1){ preco = preco + 1; } return preco; }
  • 19. Exemplo 5 – Sorveteria 2 (8 Caminhos) 19/68 if +5 +1 +1 if +1 18 15 +1 if if +5 +1 +1 if +1 17 15 +1 if if +5 +1 +1 if +1 18 15 +1 if if +5 +1 +1 if +1 19 15 +1 if if +5 +1 +1 if +1 23 15 +1 if if +5 +1 +1 if +1 22 15 +1 if if +5 +1 +1 if +1 23 15 +1 if if +5 +1 +1 if +1 24 15 +1 if Com Copo 1 Cob Prem Copo 1 Cob Com Copo 2 Cob Com Casc 1 Cob Com Casc 2 Cob Prem Copo 2 Cob Prem CAsc 2 Cob Prem Casc 2 Cob
  • 20. Exemplo 5 – Sorveteria 2 (Complexidade Ciclomática = 4) 20/68 B if +5 +1 +1 if +1 P 15 +1 if A if +5 +1 +1 if +1 P 15 +1 if C if +5 +1 +1 if +1 P 15 +1 if D if +5 +1 +1 if +1 P 15 +1 if 4 Caminhos linearmente independentes
  • 21. Exemplo 5 – Sorveteria 2 (1 Cenário cobre 100%) 21/68 if +5 +1 +1 if +1 P 15 +1 if assertEquals(24, sorveteria.precoSorvete (true, true, 2));
  • 22. Exemplo 5 – Resultado da Cobertura 22/68 Complexidade Ciclomática 4 sugere que 4 cenários (bem feitos) são suficientes para boa cobertura de todas as linhas e condições (apenas 1 é necessário) Complexidade Ciclomática 4 sugere que 4 cenários (bem feitos) são suficientes para boa cobertura de todas as linhas e condições (apenas 1 é necessário)
  • 23. Exemplo 6 – Sorveteria 3 (Ifs Aninhados) 23/68 Quantos caminhos?Quantos caminhos? if17 20 if +1 +2 If +1 +2 P 0 public int precoSorvete(boolean premium, boolean casquinha, int coberturas) { int preco = 0; if (premium) { // só premiun tem casquinha preco = 20; if (casquinha) { // só casq tem cobe preco = preco + 2; if (coberturas > 1){ preco = preco + 2; } else { preco = preco + 1; } } else { preco = preco + 1; } } else { preco = 15 + 1 + 1; // copo + 1 cob } return preco; }
  • 24. Exemplo 6 – Sorveteria 3 (Complexidade Ciclomática = 4) 24/68 if17 20 if +1 +2 If +1 +2 P 0A if17 20 if +1 +2 If +1 +2 P 0B if17 20 if +1 +2 If +1 +2 P 0 D C Similar ao exemplo “Ano Bissexto”. Cada “preco=” poderia ser um “return” 4 Caminhos linearmente independentes
  • 25. Exemplo 6 – Sorveteria 3 (2 Cenários cobrem 77,6%) 25/68 assertEquals(17, sorveteria.precoSorvete (false, false, 1)); assertEquals(24, sorveteria.precoSorvete (true, true, 2)); if17 20 if +1 +2 If +1 +2 0A if17 20 if +1 +2 If +1 24 0 C
  • 26. Exemplo 6 – Sorveteria 3 (3 Cenários Cobrem 88,9%) 26/68 17 = (false, false, 1) // 1 24 = (true, true, 2)) // 2 23 = (true, true, 1)) // 3 if17 20 if +1 +2 If +1 +2 0A if17 20 if +1 +2 If 23 24 0 CD
  • 27. Exemplo 6 – Sorveteria 3 (4 Cenários Cobrem 100%) 27/68 17 = (false, false, 1) // 1 24 = (true, true, 2)) // 2 23 = (true, true, 1)) // 3 21 = (true, false, 2)) // 4 if17 20 if +1 +2 If +1 +2 0A if17 20 if 21 +2 If 23 24 0 CD B
  • 28. Exemplo 6 – Resultado da Cobertura 28/68 Complexidade Ciclomática 4 sugere que 4 cenários (bem feitos) são suficientes para boa cobertura de todas as linhas e condições (4 são necessários) Complexidade Ciclomática 4 sugere que 4 cenários (bem feitos) são suficientes para boa cobertura de todas as linhas e condições (4 são necessários)
  • 29. Exemplos 4, 5 e 6 – Sorveteria 1 x 2 x 3 (CC = 4 em Todos) 29/68 if 15 20 if +1 +2 if +1 +2 P 0 if +5 +1 +1 if +1 P 15 +1 if 1 2 Sorveterias 1 e 2 8 Caminhos 4 Circuitos 1 = 2 Cenários 2 = 1 Cenário Sorveteria 3 4 Caminhos 4 Circuitos 4 Cenários if17 20 if +1 +2 If +1 +2 P 03
  • 30. Exemplos 4, 5 e 6 – NPATH x Complexidade Ciclomática 30/68 @Test public void testAllPaths() { // Baseado em NPATH assertEquals(17, sorveteria.precoSorvete(false, false, 1)); // Comum-Copinho-1Cob assertEquals(18, sorveteria.precoSorvete(false, false, 2)); // Comum-Copinho-2Cob assertEquals(18, sorveteria.precoSorvete(false, true, 1)); // Comum-Casquinha-1Cob assertEquals(19, sorveteria.precoSorvete(false, true, 2)); // Comum-Casquinha-2Cob assertEquals(22, sorveteria.precoSorvete(true, false, 1)); // Premium-Copinho-1Cob assertEquals(23, sorveteria.precoSorvete(true, false, 2)); // Premium-Copinho-2Cob assertEquals(23, sorveteria.precoSorvete(true, true, 1)); // Premium-Casquinha-1Cob assertEquals(24, sorveteria.precoSorvete(true, true, 2)); // Premium-Casquinha-2Cob } @Test public void testIndependentPaths() { // Baseado em CC assertEquals(17, sorveteria.precoSorvete(false, false, 1)); // Comum-Copinho-1Cob assertEquals(23, sorveteria.precoSorvete(true, true, 1)); // Premium-Casquinha-1Cob assertEquals(21, sorveteria.precoSorvete(true, false, 2)); // Premium-Copinho-2Cob assertEquals(24, sorveteria.precoSorvete(true, true, 2)); // Premium-Casquinha-2Cob } A Sorveteria com 9 IF-ELSE teria 512 caminhos, mas CC = 10 (linhas do teste)
  • 31. Limite Máximo de Cenários 31/68 Dependendo do algoritmo, a quantidade de caminhos únicos pode variar, mas o valor de complexidade ciclomática é o limite superior do número de cenários necessários para cobrir 100% das linhas e condições, se definidos corretamente A Sorveteria com 9 IF-ELSE teria 512 caminhos, mas CC = 10
  • 32. Correlação 1 - Testabilidade 32/68 Se não há impacto é porque não há teste de unidade Quanto maior a complexidade ciclomática em um método, maior tende a ser o número de cenários necessários em um teste deste mesmo método.
  • 33. Correlação 2 - Manutenção 33/68 Quanto maior a complexidade ciclomática em um método, maior será a dificuldade de entendimento e o risco de inserção de novos defeitos neste método, aumentando o esforço de manutenção. Durante a construção inicial o impacto é mais baixo. O autor ainda entende o próprio código
  • 34. Cálculo da Complexidade Ciclomática 34/68 Nó Aresta CC = Arestas – Nós + 1 if17 20 if +1 +2 If +1 +2 P 0 CC = 14 – 11 + 1 = 4 Preciso desenhar um grafo para cada método para calcular? Preciso desenhar um grafo para cada método para calcular?
  • 35. Regra de Cálculo Baseada em Keywords (Sonar) 35/68 1. Inicia em “Um” para o método (com ou sem retorno) 2. Adiciona “Um” para cada elemento de fluxo abaixo: 2.1 Seleção if, case 2.2 Loops for, while, do-while, break, e continue. 2.3 Operadores &&, ||, ? 2.4 Exceções catch, finally, throw, ou throws 2.5 Fluxo return que não seja o último (else, default, finally, : e return não incrementam o valor)
  • 36. Regra de Cálculo Baseada em Keywords (Análise Estática) 36/68
  • 37. Exercício de Contagem (Soma Diferentes – Exemplo 2) 37/68 ? public int sum(int a, int b) { int result= 0; if (a != b) { result = a + b; } System.out.println(result); return result; }
  • 38. Resultado 38/68 2 public int sum(int a, int b) { int result= 0; if (a != b) { result = a + b; } System.out.println(result); return result; } +1 +1
  • 39. Exercício de Contagem (Ano Bissexto – Exemplo 3) 39/68 ? public boolean isBissexto(int ano){ if (ano % 400 == 0){ return true; } if (ano % 4 == 0 && ano % 100 != 0){ return true; } return false; }
  • 40. Resultado 40/68 6 public boolean isBissexto(int ano){ if (ano % 400 == 0){ return true; } if (ano % 4 == 0 && ano % 100 != 0){ return true; } return false; } +1 +1 +1 +1 +1 +1
  • 41. Resultado para Implementação sem “Return” 41/68 4 public boolean isBissexto(int ano){ boolean result = false; if (ano % 400 == 0){ result = true; } if (ano % 4 == 0 && ano % 100 != 0){ result = true; } return result; } +1 +1 +1 +1
  • 42. Resultado para Implementação mais Enxuta 42/68 3 public boolean isBissexto(int ano){ return ano % 400 == 0 || (ano % 4 == 0 && ano % 100 != 0); } +1+1 +1 assertTrue(obj.isBissexto(1600)); assertFalse(obj.isBissexto(2001)); assertTrue(obj.isBissexto(2004)); assertFalse(obj.isBissexto(1900));
  • 43. Modo de Uso 43/68 O número exato não é a coisa mais importante! Complexidade Ciclomática serve para acompanhamento da evolução do software durante a construção Premissa: Ferramenta de Análise Estática de Código
  • 44. Desafio de Contagem 44/68 ? Contador de Constantes Inteiras
  • 46. Parte 2 – Aplicação 46/68 Padrão de Qualidade Importância Utilidade
  • 47. Padrão de Qualidade sobre Complexidade Ciclomática (CC) 47/68 10 Quanto menor, melhor Mais fácil de testar e manter Valor limite do Sonar: até 10 (Acima de 10 gera uma violação) Relação direta com casos de testes Quanto mais alta, mais complexo Mais responsabilidades por método > 50 = quase impossível de manter (até para o desenvolvedor original) Alta CC sugere pouco OO (+ procedural)
  • 48. Padrão de Qualidade sobre Complexidade Ciclomática (CC) 48/68 <10! 10 é o limite, não o ideal (é alto) CC alta é um ponto quente - Maior necessidade de testes - Maior probabilidade de erro - Maior necessidade de manutenção A média deve tender a 1
  • 49. Padrão de Qualidade sobre Complexidade Ciclomática (CC) 49/68 10 Segundo Thomas J. McCabe (1976): 01-10 Método Simples. Baixo Risco 11-20 Método razoavelmente complexo. Risco moderado. 21-50 Método muito complexo. Risco elevado. 51-N Método de altíssimo risco e instável
  • 50. Padrão de Qualidade sobre Complexidade Ciclomática (CC) 50/68 5 Segundo Steve McConnell (Code Complete 1993) : De 0 a 5: Seu código está, provavelmente, ok. Entre 6 e 10: Pense em maneiras de simplificar o código Maior que 10: Quebre o código em dois e insira uma chamada da primeira parte para a segunda.
  • 51. Padrão de Qualidade sobre Complexidade Ciclomática (CC) 51/68 Segundo SonarSource (sonarsource.com): Gerencie como “Vazamento de Água” (Water Leak) “Fix Issues Before They Exist” (Resolva antes que exista) Utilização do Plugin SonarLint A melhor abordagem sobre complexidade é mantê-la baixa
  • 52. Impactos Diretos da Complexidade Ciclomática 52/68 Complexidade Ciclomática Complexidade Ciclomática TestabilidadeTestabilidade LegibilidadeLegibilidade < Tamanho< Tamanho GranularidadeGranularidade CoesãoCoesão
  • 53. Impactos Diretos e Indiretos da Complexidade Ciclomática 53/68 Complexidade Ciclomática Complexidade Ciclomática TestabilidadeTestabilidade LegibilidadeLegibilidade < Tamanho< Tamanho Facilidade de Manutenção Facilidade de Manutenção CoberturaCobertura - Defeitos- Defeitos GranularidadeGranularidade ReusoReuso CoesãoCoesão < Acoplamento< Acoplamento
  • 54. Impactos Diretos e Indiretos da Complexidade Ciclomática 54/68 Complexidade Ciclomática Complexidade Ciclomática TestabilidadeTestabilidade LegibilidadeLegibilidade < Tamanho< Tamanho Facilidade de Manutenção Facilidade de Manutenção CoberturaCobertura - Defeitos- Defeitos GranularidadeGranularidade AutomaçãoAutomação ReusoReuso CoesãoCoesão < Acoplamento< Acoplamento Capacidade de Evolução Capacidade de Evolução
  • 55. Impactos Diretos e Indiretos da Complexidade Ciclomática 55/68 Complexidade Ciclomática Complexidade Ciclomática TestabilidadeTestabilidade LegibilidadeLegibilidade < Tamanho< Tamanho Facilidade de Manutenção Facilidade de Manutenção CoberturaCobertura Veloc. Entrega Qualidade Satisfação Veloc. Entrega Qualidade Satisfação - Defeitos- Defeitos GranularidadeGranularidade AutomaçãoAutomação ReusoReuso CoesãoCoesão < Acoplamento< Acoplamento Capacidade de Evolução Capacidade de Evolução
  • 57. Quebra do Ciclo Vicioso 57/68 Prazo Curto KEEP CALM AND ... Dívida Técnica Perda de Qualidade CC Baixa Teste de Unidade Confiança Fluidez Refatoração
  • 58. Parte 3 – Prática 58/68 Refatoração e Redução da Complexidade Ciclomática
  • 59. Refatoração 59/68 Refatoração é uma técnica para reestruturar um código existente, alterando seu estado interno sem alterar seu comportamento externo. Ela consiste em uma série de pequenas transformações com a preservação do comportamento original. Cada transformação faz pouco, mas uma sequência de transformações pode produzir uma reestruturação significativa. Uma vez que cada refatoração é pequena, é menos provável que dê errado.
  • 60. Vazamento de Água: Refatoração como Atividade Corriqueira 60/68 ConstruçãoConstrução Análise EstáticaAnálise Estática CC Alta?CC Alta?Não RefatoraçãoRefatoração Sim Tratar?Tratar? Não Dívida TécnicaDívida Técnica Sim
  • 61. Exercício de Refatoração 61/68 Necessidade: A partir de uma classe qualquer retorne a quantidade de constantes (final static) inteiras* presentes nessa classe incluindo as constantes herdadas das superclasses. * Inteiros: Integer, int, Long, long, Short, short, Byte, byte e BigInteger ** Se a classe é nula, Enum ou abstrata deve ser retornado “-1”.
  • 62. Solução Inicial 62/68 O que o método faz? Como reduzir a Complexidade Ciclomática de 21 para menos de 10? Porque deu 21? Está muito procedural? Pode ser desmembrado?
  • 63. Análise do Método 63/68 2 1 3 O método faz quatro coisas: 1 – Pré-condição 2 – Obtém os campos 3 – Classifica Int Const 4 – Contabiliza Decomposição funcional Possivelmente recursivo 4
  • 64. Primeira Refatoração 64/68 1 1 2 2 3 3 4 - Criação de 3 métodos - CC do método “countIntConst” desceu de 21 para 5 - Soma das CC dá 23 (4 métodos) - método “isIntConst” deu 11 Apesar de totalizar 23, os métodos ficaram muito mais testáveis e com responsabilidades únicas
  • 65. Refatoração Final 65/68 - Troca de “Ous” por um conjunto com os tipos inteiros - Soma das CC dá 14 (4 métodos) - “isIntConst” desceu de 11 para 3 3 3 5 3
  • 66. Resultado da Refatoração 66/68 Um método (CC = 21) deu lugar a quatro métodos (CC = 5,3,3,3) Os métodos extraídos fazem só uma coisa e são facilmente reusáveis O esforço de construção e teste de unidade é mais ou menos o mesmo, mas a manutenção será muito mais simples no futuro O método principal ficou com a leitura mais fácil, pois ele orquestra a funcionalidade invocando outras três, adicionando semântica
  • 67. Dicas Finais 67/68 Assim como no exemplo, Complexidade Ciclomática alta é um indício de uma provável necessidade e/ou oportunidade de refatoração Complexidade Ciclomática alta é indício de baixa ou má cobertura de testes de unidade. É difícil testar e cobrir um código complexo Uso frequente de análise estática com refatoração ajuda a manter a complexidade baixa. Trate o vazamento de água continuamente
  • 68. Muito Obrigado! Dúvidas? Douglas Siviotti de Alcantara douglas.siviotti@gmail.com github.com/siviotti www.artesoftware.com.br Código fonte usado nesta apresentação: https://github.com/siviotti/ops/tree/master/src/main/java/siviotti/ops/example/cc