-
-
Save ldaniel/2149474 to your computer and use it in GitHub Desktop.
Pessoal, gostaria de saber como vocês endereçam as questões que farei após explicar o cenário | |
abaixo. Pra mim, é muito claro que todas podem ser respondidas com um belo "depende", contudo, | |
vamos focar as respostas no que atenderia a maioria dos casos (lembrando que esse é um cenário | |
muito simplório). | |
-- CENÁRIO ----------------------------------------------------------------------------------------- | |
Um desenvolvedor codificou inicialmente a classe (escrita em C#) no gist "exemplo_antes.cs". | |
Após aplicar um refactoring, ele chegou no resultado mostrado no gist "exemplo_depois.cs". | |
Algumas métricas foram coletadas antes e depois do refactoring (vide gist "métricas.txt"). | |
Obs.: Esse código foi tirado daqui -> | |
http://whileicompile.wordpress.com/2010/09/14/what-is-too-simple-and-small-to-refactor-as-clean-code | |
-- QUESTÕES ---------------------------------------------------------------------------------------- | |
1) De modo geral, podemos ter como prática que o set de testes deve, no mínimo, ter o mesmo número | |
de testes que o resultado do indicador CC (cyclomatic complexity)? Por exemplo, se um método tem | |
5 de CC, ele deve ter no mínimo 5 testes escritos para ele. | |
Notas: a) estou considerando apenas um assert por método de test. | |
b) não considerando a eficácia do teste escrito. | |
2) Membros privados podem ser ignorados nos testes? Devemos garantir os testes pelos membros | |
públicos observando a cobertura de código dos testes. Isso é suficiente? | |
Nota: sem entrar no mérito da necessidade ou não de 100% de cobertura. A intenção aqui é | |
deduzir uma relação entre métricas (total de membros privados/publicos) e testes. | |
3) Quando, através de métricas, chegamos a conclusão que o código ficou mais complexo, é uma boa | |
estratégia considerar LoC (lines of code) como indicador para comparar "antes" e "depois"? Que | |
outras métricas vocês considerariam? | |
4) Qual a melhor "unidade" para orientarmos a escrita de testes (de unidade, claro): método, classe, | |
assembly, assunto de negócio ou outra? (estou falando aqui de "Testes de Unidade": qual unidade | |
você comumente utiliza?) |
/* Classe inicial, antes do refatoring */ | |
using System; | |
namespace CleanCode.Before | |
{ | |
public class WageCalculator | |
{ | |
public static float Calculate(float hours, float rate, bool isHourlyWorker) | |
{ | |
if (hours < 0 || hours > 80) | |
throw new ArgumentException(); | |
float wages = 0; | |
if (hours > 40) | |
{ | |
var overTimeHours = hours - 40; | |
if (isHourlyWorker) | |
wages += (overTimeHours * 1.5f) * rate; | |
else | |
wages += overTimeHours * rate; | |
hours -= overTimeHours; | |
} | |
wages += hours * rate; | |
return wages; | |
} | |
} | |
} |
/* Classes geradas, após refactoring */ | |
using System; | |
namespace CleanCode.After | |
{ | |
public abstract class WageCalculatorBase | |
{ | |
public float HoursWorked { get; protected set; } | |
public float HourlyRate { get; protected set; } | |
public WageCalculatorBase(float hours, float hourlyRate) | |
{ | |
if (hours < 0 || hours > 80) | |
throw new | |
ArgumentOutOfRangeException("Hours must be between 0 and 80."); | |
HoursWorked = hours; | |
HourlyRate = hourlyRate; | |
} | |
public abstract float Calculate(); | |
} | |
public class WageCalculatorForContractor : WageCalculatorBase | |
{ | |
public WageCalculatorForContractor(float hours, float hourlyRate) | |
: base(hours, hourlyRate) | |
{ | |
} | |
public override float Calculate() | |
{ | |
return HoursWorked * HourlyRate; | |
} | |
public static float Calculate(float hours, float hourlyRate) | |
{ | |
WageCalculatorForContractor payCalc = new | |
WageCalculatorForContractor(hours, hourlyRate); | |
return payCalc.Calculate(); | |
} | |
} | |
public class WageCalculatorForEmployee : WageCalculatorBase | |
{ | |
public WageCalculatorForEmployee(float hours, float hourlyRate) | |
: base(hours, hourlyRate) | |
{ | |
} | |
public override float Calculate() | |
{ | |
if (IsOvertimeRequired) | |
return CalculateWithOvertime(); | |
return CalculateWithoutOvertime(); | |
} | |
protected bool IsOvertimeRequired | |
{ | |
get | |
{ | |
return HoursWorked > 40; | |
} | |
} | |
protected float CalculateWithoutOvertime() | |
{ | |
return HoursWorked * HourlyRate; | |
} | |
protected float CalculateWithOvertime() | |
{ | |
float overTimeHours = HoursWorked - 40; | |
return (overTimeHours * 1.5f + 40) * HourlyRate; | |
} | |
public static float Calculate(float hours, float hourlyRate) | |
{ | |
WageCalculatorForEmployee payCalc = new | |
WageCalculatorForEmployee(hours, hourlyRate); | |
return payCalc.Calculate(); | |
} | |
} | |
} |
Type Member CC LoC | |
-------------------------------------------------------------------- | |
CleanCode.After........................................[18] [27] | |
WageCalculatorBase..............................(8) (9) | |
Calculate() 1 0 | |
HourlyRate.get() 1 1 | |
HourlyRate.set(float) 1 1 | |
HoursWorked.get() 1 1 | |
HoursWorked.set(float) 1 1 | |
WageCalculatorBase(float, float) 3 5 | |
WageCalculatorForContractor.....................(3) (5) | |
Calculate() 1 2 | |
Calculate(float, float) 1 2 | |
WageCalculatorForContractor(float,float) 1 1 | |
WageCalculatorForEmployee.......................(7) (13) | |
Calculate() 2 3 | |
Calculate(float, float) 1 2 | |
CalculateWithoutOvertime() 1 2 | |
CalculateWithOvertime() 1 3 | |
IsOvertimeRequired.get() 1 2 | |
WageCalculatorForEmployee(float, float) 1 1 | |
CleanCode.Before........................................[6] [14] | |
WageCalculator..................................(6) (14) | |
Calculate(float, float, bool) 5 13 | |
WageCalculator() 1 1 |
Nunca fiz um estudo formal para tentar confirmar isso, mas tenho a sincera impressão que desenvolvedores despreparados assim geram mais prejuízo que valor nos produtos em que desenvolvem.
Não que desenvolvedores iniciantes não possam trabalhar, mas por esse motivo sou a favor de code review ou (para os mais xp-fans) pair programming. É claro que se a proporção de desenvolvedores iniciantes for absurdamente maior que a de experientes, o custo disso fica inviável. E talvez seja irreal na maior parte das empresas com o mercado que temos.
Curiosamente, sem querer puxar sardinha, eu vejo que a comunidade Java tem muito mais apego às métricas que a .NET. Em .NET você tem dificuldade até em encontrar ferramentas adequadas para tirar essas métricas.
compartilho da sua impressão. Tanto é que ultimamente adotei a postura de aceitar mais trabalho (não tanto assim :P) ao invés de contratar gente despreparada que vai gerar mais trabalho do que já temos.
foi por isso que falei que sou velho chato. Com certeza não é motivo pra desanimar. Essa imagem de desanimado minha é mesmo uma imagem. Apesar dos pesares, ainda tenho energia pra tentar promover um monte de coisa bacana.
Quanto a equipes boas, sei que elas existem. Não em tantos lugares, mas também não estão em extinção. Só não consegui unir o útil ao agradável ainda ($$$). Infelizmente empresas ruins pagam mais, afinal sempre precisam de gente pra resolver problemas, né?
Acho que cheguei só um pouco atrasado kkk, mesmo recebendo no twitter o link uma refatoração de um código legado não me permitiu participar :D
@ldaniel interessante a discussão, a única refatoração que gostei foi a do @juanplopes e agora minhas respostas.
1- Sim, no mínimo, quando preciso testar códigos assim geralmente verifico a cobertura de código para garantir que passei por todo lugar.
2 - Essa depende mesmo, testo métodos privados mas ai tenho que concordar com o @juanplopes talvez tenha um probleminha em precisar testar esses caras :)
3 - Cara não sei, acho que LoC não vai ajudar a dizer que ficou mais complexo, muita linha de código simples é melhor que pouca linha de código complexo :D. E ai depende do "feeling" que o @vquaiato disse. Agora pensando... e se além de LoC considerar número de testes? Acho que nesse exemplo o número de testes para o código refatorado acabaria maior do que com o código sem refatorar.
4 - Classe
Mais uma vez parabéns, sensacional a discussão aqui, todos foram geniais
@mauricioaniche:
É "impossível" porque o desenvolvedor que ta ali do meu lado todo dia tem dificuldade em compreender como funciona um ORM, javascript básico e até HTML e CSS. Outro dia pedi pra um cara mudar uma classe CSS e ele não sabia como fazer. E isso não é nem aqui no Brasil somente. Esse caso em especifico aconteceu nos EUA então esse problema não é exclusividade nossa.
Imagina falar pra ele que o código dele tem uma complexidade enorme por causa dos caminhos que os IF's geram? Ou pedir pro cara refatorar pra deixar mais coeso? Não é prático.
Se olharmos pro indivíduo dentro do grupo, talvez até seja possível, mas disseminar esse comportamento pra um grupo de desenvolvedores que tem outras motivações é impraticável.
@ldaniel, mais uma vez vou ter que concordar com você que a discussão está interessante. Fazia tempo que eu não via algo assim, onde o pessoal não da carteirada pra fugir do assunto e provar um ponto. Way to go!