Complexidade Ciclomática

DouglasSiviotti 6,074 views 68 slides Sep 09, 2018
Slide 1
Slide 1 of 68
Slide 1
1
Slide 2
2
Slide 3
3
Slide 4
4
Slide 5
5
Slide 6
6
Slide 7
7
Slide 8
8
Slide 9
9
Slide 10
10
Slide 11
11
Slide 12
12
Slide 13
13
Slide 14
14
Slide 15
15
Slide 16
16
Slide 17
17
Slide 18
18
Slide 19
19
Slide 20
20
Slide 21
21
Slide 22
22
Slide 23
23
Slide 24
24
Slide 25
25
Slide 26
26
Slide 27
27
Slide 28
28
Slide 29
29
Slide 30
30
Slide 31
31
Slide 32
32
Slide 33
33
Slide 34
34
Slide 35
35
Slide 36
36
Slide 37
37
Slide 38
38
Slide 39
39
Slide 40
40
Slide 41
41
Slide 42
42
Slide 43
43
Slide 44
44
Slide 45
45
Slide 46
46
Slide 47
47
Slide 48
48
Slide 49
49
Slide 50
50
Slide 51
51
Slide 52
52
Slide 53
53
Slide 54
54
Slide 55
55
Slide 56
56
Slide 57
57
Slide 58
58
Slide 59
59
Slide 60
60
Slide 61
61
Slide 62
62
Slide 63
63
Slide 64
64
Slide 65
65
Slide 66
66
Slide 67
67
Slide 68
68

About This Presentation

Complexidade Ciclomática


Slide Content

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
0
Premium
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
0
A 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
0
A
if17 20
if
+1
+2
If
+1 +2
P
0
B
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
0
3

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

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çõescatch, 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
Testabilidade
Testabilidade
Legibilidade
Legibilidade
< Tamanho
< Tamanho
Granularidade
Granularidade
Coesão
Coesão

Impactos Diretos e Indiretos da Complexidade Ciclomática
53/68
Complexidade
Ciclomática
Complexidade
Ciclomática
Testabilidade
Testabilidade
Legibilidade
Legibilidade
< Tamanho
< Tamanho Facilidade
de Manutenção
Facilidade
de Manutenção
Cobertura
Cobertura
- Defeitos
- Defeitos
Granularidade
Granularidade
Reuso
Reuso
Coesão
Coesão
< Acoplamento
< Acoplamento

Impactos Diretos e Indiretos da Complexidade Ciclomática
54/68
Complexidade
Ciclomática
Complexidade
Ciclomática
Testabilidade
Testabilidade
Legibilidade
Legibilidade
< Tamanho
< Tamanho Facilidade
de Manutenção
Facilidade
de Manutenção
Cobertura
Cobertura
- Defeitos
- Defeitos
Granularidade
Granularidade
Automação
Automação
Reuso
Reuso
Coesão
Coesã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
Testabilidade
Testabilidade
Legibilidade
Legibilidade
< Tamanho
< Tamanho Facilidade
de Manutenção
Facilidade
de Manutenção
Cobertura
Cobertura
Veloc. Entrega
Qualidade
Satisfação
Veloc. Entrega
Qualidade
Satisfação
- Defeitos
- Defeitos
Granularidade
Granularidade
Automação
Automação
Reuso
Reuso
Coesão
Coesã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ção
Construção
Análise Estática
Análise Estática
CC Alta?
CC Alta?
Não
Refatoração
Refatoração
Sim
Tratar?
Tratar?
Não
Dívida Técnica
Dí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
[email protected]
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