Entrada numérica
El sistema recibe un número y evalúa si es divisible por los divisores configurados.
Proyecto FizzBuzz · C# · SOLID
Esta página resume el funcionamiento del juego FizzBuzz, una forma ordenada de resolverlo en C# y la diferencia entre dos variantes del algoritmo, junto con los diagramas de clases y de flujo del servicio.
1. El juego
FizzBuzz es un ejercicio clásico de programación. Se toma una secuencia de números y se sustituyen ciertos valores por palabras según reglas de divisibilidad. En el caso tradicional, los múltiplos de 3 se convierten en Fizz, los de 5 en Buzz y los que cumplen ambas reglas en FizzBuzz.
Aunque parece un problema simple, es muy útil para evaluar claridad mental, diseño de condiciones y organización del código. En este proyecto, además, se convierte en una excusa perfecta para aplicar principios de diseño limpio y arquitectura mantenible.
El sistema recibe un número y evalúa si es divisible por los divisores configurados.
Si el número es múltiplo de ambos divisores, devuelve FizzBuzz. Si solo cumple uno, devuelve Fizz o Buzz.
La clase FizzBuzz resuelve el tipo de número y ResultHandler se encarga del conjunto de resultados.
El servicio responde al cliente y delega la escritura para evitar bloqueos innecesarios.
2. Resolución planteada
En lugar de resolver todo con una sola función grande, el proyecto organiza la lógica en clases e interfaces. De ese modo, cada pieza cumple una tarea concreta y el sistema queda preparado para crecer.
Define el contrato NumberType(int number). Esto permite sustituir implementaciones sin cambiar al consumidor.
Contiene la lógica principal para clasificar un número. No asume responsabilidades de iteración ni de lectura de archivos.
Genera respuestas simples o múltiples usando una implementación de IDivisibilityCalculator.
Aísla la lectura y escritura del fichero, dejando el acceso a disco fuera del núcleo del algoritmo.
3. Diseño de clases
El esquema visual muestra cómo se relacionan FizzBuzz, ResultHandler, ServiceController, FileManager y las interfaces del sistema.
4. Código principal
La clase FizzBuzz resuelve un único número. ResultHandler se ocupa del rango o de la respuesta unitaria. FileManager encapsula la escritura y lectura del fichero, manteniendo el acceso a disco fuera del algoritmo central.
public class FizzBuzz : IDivisibilityCalculator
{
private int fizz, buzz, fizzBuzz;
public FizzBuzz(int fizz, int buzz)
{
this.fizz = fizz;
this.buzz = buzz;
this.fizzBuzz = fizz * buzz;
}
public string NumberType(int number)
{
if (number % this.fizzBuzz == 0) return "FizzBuzz";
if (number % this.fizz == 0) return "Fizz";
if (number % this.buzz == 0) return "Buzz";
return number.ToString();
}
}
public class ResultHandler : IMultipleResult, ISingleResult
{
private readonly IDivisibilityCalculator gameFizzBuzz;
public ResultHandler(IDivisibilityCalculator gameFizzBuzz)
{
this.gameFizzBuzz = gameFizzBuzz;
}
public string[] MultipleResult(int LIMIT, int startNumber)
{
string[] numberList = new string[(LIMIT - startNumber) + 1];
int i = 0;
while (startNumber <= LIMIT)
{
numberList[i] = this.gameFizzBuzz.NumberType(startNumber);
startNumber++;
i++;
}
return numberList;
}
}
public class FileManager : IReadFromFile, IWriteToFile
{
private string filePath;
public FileManager(string filePath)
{
this.filePath = filePath;
}
public void WriteContent(string[] contentSet)
{
using (StreamWriter streamWriter = new StreamWriter(this.filePath, true))
{
foreach (string auxNumber in contentSet)
{
streamWriter.Write(auxNumber + " ");
}
}
}
}
5. Comparativa del algoritmo
Las dos versiones resuelven el mismo problema, pero la segunda simplifica la primera comprobación. Esa diferencia es precisamente la optimización destacada en la memoria del proyecto.
Comprobación doble con &&
Comprobación directa con fizzBuzz
En la versión A, la primera condición combina dos operaciones de módulo con un operador lógico. En la versión B, se aprovecha que FizzBuzz = Fizz × Buzz, así que basta con comprobar si el número es divisible por ese producto para detectar el caso combinado desde el primer paso.
El resultado es un algoritmo más directo, con menos trabajo en la rama inicial y una lectura más limpia del código.
6. Servicio y concurrencia
El servicio puede recibir múltiples clientes. Sin embargo, escribir todos a la vez sobre el mismo fichero sería problemático. Por eso se introduce un Lock sobre el recurso compartido y se divide la carga usando Task, de forma que la respuesta al cliente no se quede esperando innecesariamente.
7. Excepciones
El proyecto contempla errores críticos y advertencias. Para ello utiliza clases específicas de excepción, separando los casos que impiden procesar la petición de aquellos que solo afectan al registro en fichero.
Esta idea ayuda a mantener la estabilidad del servicio y a registrar incidentes sin mezclar lógica de negocio con gestión de logs.
8. Valor del proyecto
Aquí FizzBuzz deja de ser un simple reto de entrevista y se transforma en una demostración de diseño: interfaces, responsabilidades claras, posibilidad de extensión, mejora algorítmica y tratamiento de concurrencia.
La clave no está solo en que funcione, sino en que la solución sea comprensible, mantenible y preparada para evolucionar.