desvantagens dessa abordagem.
3.1. O problema dos números romanos
Casa do Código
3.1
O problema dos números romanos
Numerais romanos foram criados na Roma Antiga e eles foram utilizados em todo o
seu império. Os números eram representados por sete diferentes símbolos, listados
na tabela a seguir.
I, unus, 1, (um)
V, quinque, 5 (cinco)
X, decem, 10 (dez)
L, quinquaginta, 50 (cinquenta)
C, centum, 100 (cem)
D, quingenti, 500 (quinhentos)
M, mille, 1.000 (mil)
Para representar outros números, os romanos combinavam estes símbolos, co-
meçando do algarismo de maior valor e seguindo a regra:
Algarismos de menor ou igual valor à direita são somados ao algarismo de
maior valor;
Algarismos de menor valor à esquerda são subtraídos do algarismo de maior
valor.
Por exemplo, XV representa 15 (10 + 5) e o número XXVIII representa 28 (10 + 10
+ 5 + 1 + 1 + 1). Há ainda uma outra regra: nenhum símbolo pode ser repetido lado
a lado por mais de 3 vezes. Por exemplo, o número 4 é representado pelo número IV
(5 - 1) e não pelo número IIII.
Existem outras regras (especialmente para números maiores, que podem ser li-
das aqui [33]), mas em linhas gerais, este é o problema a ser resolvido. Dado um
numeral romano, o programa deve convertê-lo para o número inteiro correspon-
dente.
20
Casa do Código
Capítulo 3. Introdução ao Test-Driven Development
3.2
O primeiro teste
Conhecendo o problema dos numerais romanos, é possível levantar os diferentes
cenários que precisam ser aceitos pelo algoritmo: um símbolo, dois símbolos iguais,
três símbolos iguais, dois símbolos diferentes do maior para o menor, quatro símbo-
los dois a dois, e assim por diante. Dado todos estes cenários, uns mais simples que
os outros, começaremos pelo mais simples: um único símbolo.
Começando pelo teste deve entender o símbolo I. A classe responsável pela con-
versão pode ser chamada, por exemplo, de ConversorDeNumeroRomano, e o método
converte(), recebendo uma String com o numeral romano e devolvendo o valor in-
teiro representado por aquele número:
[TestFixture]
public class ConversorDeNumeroRomanoTest
{
[Test]
public void DeveEntenderOSimboloI()
{
ConversorDeNumeroRomano romano = new ConversorDeNumeroRomano();
int numero = romano.Converte("I");
Assert.AreEqual(1, numero);
}
}
Veja que nesse momento, esse código não compila; a classe ConversorDeNume-
roRomano, bem como o método Converte() não existem. Para resolver o erro de
compilação, é necessário criar classe, mesmo que sem uma implementação real:
public class ConversorDeNumeroRomano
{
public int Converte(string numeroEmRomano)
{
return 0;
}
}
21
3.2. O primeiro teste
Casa do Código
De volta ao teste, ele agora compila. Ao executá-lo, o teste falha. Mas não há pro-
blema; isso já era esperado. Para fazê-lo passar, introduziremos ainda uma segunda
regra: o código escrito deve ser sempre o mais simples possível.
Com essa regra em mente, o código mais simples que fará o teste passar é fazer
simplesmente o método converte() retornar o número 1:
public int Converte(string numeroEmRomano)
{
return 1;
}
Desenvolvedores, muito provavelmente, não ficarão felizes com essa implemen-
tação, afinal ela funciona apenas para um caso. Mas isso não é problema, afinal a
implementação não está pronta; ainda estamos trabalhando nela.
Um próximo cenário seria o símbolo V. Nesse caso, o algoritmo deve retornar 5.
Novamente começando pelo teste:
[Test]
public void DeveEntenderOSimboloV()
{
ConversorDeNumeroRomano romano = new ConversorDeNumeroRomano();
int numero = romano.Converte("V");
Assert.AreEqual(5, numero);
}
Esse teste também falha. Novamente faremos a implementação mais simples que
resolverá o problema. Podemos, por exemplo, fazer com que o método converte()
verifique o conteúdo do número a ser convertido: se o valor for I, o método retorna 1; se o valor for V, o método retorna 5:
public int Converte(string numeroEmRomano)
{
if(numeroEmRomano.Equals("I")) return 1;
else if(numeroEmRomano.Equals("V")) return 5;
return 0;
}
Os testes passam (os dois que temos). Poderíamos repetir o mesmo teste e a
mesma implementação para os símbolos que faltam (X, L, C, M, ...). Mas nesse mo-
mento, já temos uma primeira definição sobre nosso algoritmo: quando o numeral
romano possui apenas um símbolo, basta devolvermos o inteiro associado a ele.
22
Casa do Código
Capítulo 3. Introdução ao Test-Driven Development
Ao invés de escrever um monte de ifs para cada símbolo, é possível usar um
switch, que corresponde melhor ao nosso cenário. Melhorando ainda mais, ao invés
do switch, é possível guardar os símbolos em uma tabela, ou no C#, em um mapa
entre o algarismo e o inteiro correspondente à ele. Sabendo disso, vamos nesse mo-
mento alterar o código para refletir a solução:
public class ConversorDeNumeroRomano
{
private static Dictionary<string, int> tabela =
new Dictionary<string, int>() {
{"I", 1},
{"V", 5},
{"X", 10},
{"L", 50},
{"C", 100},