Aplicar patrones de diseño del catálogo GoF (Gang of Four) para refactorizar un diseño rígido y acoplado, mejorando su arquitectura mediante el uso de abstracciones, principios SOLID y un patrón de creación (Factory Method).
Una tienda de música en línea administra un catálogo de instrumentos musicales (guitarras, pianos, baterías, etc.). Cada clase de instrumento tiene un método Play() que simula el sonido del instrumento.
La clase principal MusicStore decide qué instrumento crear basándose en una cadena de texto, con estructuras condicionales como:
if (instrument == "Guitar") new Guitar();
else if (instrument == "Piano") new Piano();
else if (instrument == "Drum") new Drum();Problemas Detectados
- Problema Descripción
- Uso excesivo de condicionales Cada nuevo instrumento requiere modificar MusicStore.
- Alto acoplamiento MusicStore depende directamente de clases concretas.
- Violación del principio Open/Closed No se puede extender sin modificar código existente.
- Falta de abstracción No hay una interfaz o clase base común.
- Baja escalabilidad Dificulta agregar nuevos instrumentos.
- Refactorización Aplicada
- Objetivos alcanzados
Eliminar condicionales usando una fábrica.
- Introducir una interfaz común IInstrument con el método Play().
- Reducir el acoplamiento entre MusicStore y las clases concretas.
- Aplicar el principio Open/Closed: agregar instrumentos sin modificar código existente.
- Implementar el patrón Factory Method.
- Mejorar cohesión y separación de responsabilidades.
- Facilitar pruebas unitarias.
- Preparar el sistema para futuras expansiones.
public interface IInstrument
{
string Name { get; }
void Play();
}Define el contrato que todos los instrumentos deben implementar.
public class Guitar : IInstrument
{
public string Name => "Guitar";
public void Play() => Console.WriteLine("🎸 Strum strum - playing the guitar!");
}
public class Piano : IInstrument
{
public string Name => "Piano";
public void Play() => Console.WriteLine("🎹 Plink plonk - playing the piano!");
}
public class Drum : IInstrument
{
public string Name => "Drum";
public void Play() => Console.WriteLine("🥁 Boom tch - playing the drums!");
}Cada instrumento implementa la interfaz IInstrument.
using System;
using System.Collections.Generic;
public class InstrumentFactory
{
private readonly Dictionary<string, Func<IInstrument>> _creators =
new(StringComparer.OrdinalIgnoreCase);
public void Register(string key, Func<IInstrument> creator)
{
_creators[key] = creator;
}
public IInstrument Create(string key)
{
if (!_creators.TryGetValue(key, out var creator))
throw new ArgumentException($"Instrument type '{key}' not registered.");
return creator();
}
public IEnumerable<string> RegisteredKeys => _creators.Keys;
}Esta fábrica elimina condicionales y permite registrar nuevos instrumentos dinámicamente mediante un diccionario.
public class MusicStore
{
private readonly InstrumentFactory _factory;
public MusicStore(InstrumentFactory factory)
{
_factory = factory;
}
public void PlayInstrument(string key)
{
var instrument = _factory.Create(key);
instrument.Play();
}
public IEnumerable<string> GetAvailableInstruments()
{
return _factory.RegisteredKeys;
}
}MusicStore solo conoce la interfaz IInstrument y la abstracción InstrumentFactory. Ya no depende de clases concretas ni necesita condicionales.
using System;
class Program
{
static void Main()
{
var factory = new InstrumentFactory();
factory.Register("Guitar", () => new Guitar());
factory.Register("Piano", () => new Piano());
factory.Register("Drum", () => new Drum());
var store = new MusicStore(factory);
Console.WriteLine("🎵 Available instruments:");
foreach (var k in store.GetAvailableInstruments())
Console.WriteLine("- " + k);
Console.WriteLine("\n🎶 Playing instruments:");
store.PlayInstrument("Guitar");
store.PlayInstrument("Piano");
store.PlayInstrument("Drum");
// Agregar uno nuevo sin tocar el código de MusicStore
factory.Register("ElectricGuitar", () => new Guitar());
Console.WriteLine("\n🎸 After registering ElectricGuitar:");
foreach (var k in store.GetAvailableInstruments())
Console.WriteLine("- " + k);
}
}Permite crear objetos sin especificar su clase concreta, eliminando los condicionales.
MusicStore recibe una fábrica como dependencia, sin conocer implementaciones concretas.