Skip to content

Instantly share code, notes, and snippets.

@Sh0cko
Last active October 10, 2025 00:37
Show Gist options
  • Select an option

  • Save Sh0cko/c132ece6d3cdd5db220cc406b47a2ae2 to your computer and use it in GitHub Desktop.

Select an option

Save Sh0cko/c132ece6d3cdd5db220cc406b47a2ae2 to your computer and use it in GitHub Desktop.

Refactorización de Código Defectuoso con Patrones de Diseño GoF

Caso: Tienda de Música Digital (C#)

Joel Cuevas Estrada - 22210298

Objetivo de la práctica

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).

Escenario Original

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.

Solución Final

1. Interfaz Común

public interface IInstrument
{
    string Name { get; }
    void Play();
}

Define el contrato que todos los instrumentos deben implementar.

2. Clases Concretas

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.

3. Fábrica de Instrumentos (Factory Method)

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.

4. Clase Principal (MusicStore)

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.

5. Programa Principal

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);
    }
}

Patrones GoF Usados

Factory Method:

Permite crear objetos sin especificar su clase concreta, eliminando los condicionales.

Inversión de Dependencias (IoC / DI):

MusicStore recibe una fábrica como dependencia, sin conocer implementaciones concretas.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment