Skip to content

Instantly share code, notes, and snippets.

@Sh0cko
Last active October 6, 2025 01:39
Show Gist options
  • Select an option

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

Select an option

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

Joel Cuevas Estrada - 22210298

Clínica Veterinaria y Hotel de Mascotas

Práctica de Patrones de Diseño Creacionales

Objetivo de la práctica

Simular el diseño y posterior refactorización de una aplicación que gestiona una clínica veterinaria y un hotel de mascotas, aplicando patrones de diseño creacionales (Factory Method, Abstract Factory, Singleton, Builder, Prototype).

Se busca que el alumno identifique problemas en un código espagueti inicial y proponga soluciones con base en los patrones GoF.

Código espagueti inicial (problemas detectados)

El proyecto inicial presenta múltiples fallos de diseño:

  • Código duplicado al crear instancias de diferentes tipos de mascotas.
  • Alta dependencia entre clases concretas.
  • Mezcla de lógica de clínica, hotel y facturación en Main().
  • No existe un punto central de configuración.
  • Clases monolíticas con múltiples responsabilidades.
  • Métodos largos y difíciles de probar.
  • Enumeraciones usadas incorrectamente para distinguir servicios.
  • Propiedades públicas expuestas sin encapsulación.
  • No hay fábrica para habitaciones del hotel.
  • Sin separación de capas (todo está en el mismo archivo).

Refactorización propuesta

Para resolver los problemas anteriores, se aplicaron los siguientes patrones creacionales:

Abstract Factory – Creación de mascotas

  • Permite crear familias de objetos (Perro, Gato, Ave) sin depender de clases concretas.
  • Ventaja: evita código duplicado y facilita extensión con nuevas especies.
public interface IMascotaFactory { IMascota CrearMascota(string nombre); }
public class PerroFactory : IMascotaFactory { public IMascota CrearMascota(string n) => new Perro(n); }
public class GatoFactory : IMascotaFactory { public IMascota CrearMascota(string n) => new Gato(n); }
var perroFac = MascotaFactoryProvider.GetFactory("perro");
var perro = perroFac.CrearMascota("Firulais");

Factory Method – Habitaciones de hotel

  • Permite instanciar distintos tipos de habitación (Estándar, Suite, Aislada) según la necesidad.
  • Ventaja: encapsula la lógica de creación, facilita la extensión.
public abstract class HotelCreator {
    public abstract Habitacion CrearHabitacion(string tipo);
}
public class HotelConcretoCreator : HotelCreator {
    public override Habitacion CrearHabitacion(string tipo) {
        return tipo == "suite" ? new HabitacionSuite(1) : new HabitacionEstandar(1);
    }
}

Singleton – Gestor del hotel

  • Garantiza una única instancia del hotel en toda la aplicación.
  • Se implementó con Lazy para hacerlo thread-safe.
  public sealed class HotelManager {
    private static readonly Lazy<HotelManager> instancia = new Lazy<HotelManager>(() => new HotelManager());
    public static HotelManager Instance => instancia.Value;
    private HotelManager() { }
}

Builder – Facturación

  • Permite construir facturas paso a paso (consulta, vacunas, servicios extra).
  • Evita constructores con demasiados parámetros.
public interface IFacturaBuilder {
    IFacturaBuilder ParaMascota(IMascota mascota);
    IFacturaBuilder AgregarItem(string item, decimal precio);
    Factura Build();
}

Uso:

var factura = new FacturaBuilder()
                .ParaMascota(perro)
                .AgregarItem("Consulta general", 30)
                .Build();

Prototype – Rutinas de servicio

  • Se pueden clonar rutinas médicas o de cuidado (ej. “Chequeo básico”) y adaptarlas a cada mascota.
  • Ahorra tiempo y evita repetir pasos.
public class RutinaServicio : ICloneable {
    public List<string> Pasos { get; set; } = new();
    public object Clone() => new RutinaServicio { Pasos = new List<string>(this.Pasos) };
}

Uso:

var rutinaBase = new RutinaServicio { Pasos = {"Revisión", "Vacunación"} };
var rutinaClon = (RutinaServicio)rutinaBase.Clone();
rutinaClon.Pasos.Add("Control de peso");

Codigo final con todo aplicado

- name: Instalar .NET 8 y preparar entorno para Clínica Veterinaria y Hotel de Mascotas
  hosts: localhost
  become: yes
  tasks:
    - name: Actualizar sistema
      apt:
        update_cache: yes
        upgrade: dist

    - name: Instalar herramientas necesarias
      apt:
        name:
          - apt-transport-https
          - ca-certificates
          - curl
          - software-properties-common
        state: present

    - name: Descargar repositorio de Microsoft
      get_url:
        url: https://packages.microsoft.com/config/ubuntu/24.04/packages-microsoft-prod.deb
        dest: /tmp/packages-microsoft-prod.deb

    - name: Instalar repositorio de Microsoft
      apt:
        deb: /tmp/packages-microsoft-prod.deb

    - name: Actualizar caché apt
      apt:
        update_cache: yes

    - name: Instalar .NET SDK 8.0
      apt:
        name: dotnet-sdk-8.0
        state: present

    - name: Crear directorio del proyecto
      file:
        path: /home/ubuntu/clinica_mascotas_app
        state: directory

    - name: Crear archivo del proyecto
      copy:
        dest: /home/ubuntu/clinica_mascotas_app/clinica_mascotas_app.csproj
        content: |
          <Project Sdk="Microsoft.NET.Sdk.Web">
            <PropertyGroup>
              <TargetFramework>net8.0</TargetFramework>
            </PropertyGroup>
          </Project>

    - name: Crear código base refactorizado con patrones de diseño
      copy:
        dest: /home/ubuntu/clinica_mascotas_app/Program.cs
        content: |
          using System;
          using System.Collections.Generic;
          using System.Threading;

          namespace ClinicaMascotas
          {
              // Singleton Thread-Safe para registro de mascotas
              public sealed class RegistroMascotas
              {
                  private static readonly Lazy<RegistroMascotas> instancia =
                      new Lazy<RegistroMascotas>(() => new RegistroMascotas(), LazyThreadSafetyMode.ExecutionAndPublication);

                  private List<Mascota> mascotas = new List<Mascota>();

                  private RegistroMascotas() {}

                  public static RegistroMascotas Instancia => instancia.Value;

                  public void RegistrarMascota(Mascota mascota)
                  {
                      mascotas.Add(mascota);
                      Console.WriteLine($"Mascota registrada: {mascota.Nombre} ({mascota.Tipo})");
                  }
              }

              // Entidad Mascota
              public class Mascota
              {
                  public string Nombre { get; }
                  public string Tipo { get; }

                  public Mascota(string nombre, string tipo)
                  {
                      Nombre = nombre;
                      Tipo = tipo;
                  }
              }

              // Abstract Factory para Alimentos, Bebidas y Suplementos
              public interface INutricionFactory
              {
                  IAlimento CrearAlimento();
                  IBebida CrearBebida();
                  ISuplemento CrearSuplemento();
              }

              public interface IAlimento { string Describir(); }
              public interface IBebida { string Describir(); }
              public interface ISuplemento { string Describir(); }

              // Implementación de Nutrición Canina
              public class NutricionCaninaFactory : INutricionFactory
              {
                  public IAlimento CrearAlimento() => new Croquetas();
                  public IBebida CrearBebida() => new Agua();
                  public ISuplemento CrearSuplemento() => new Vitaminas();
              }

              public class Croquetas : IAlimento { public string Describir() => "Croquetas premium"; }
              public class Agua : IBebida { public string Describir() => "Agua fresca"; }
              public class Vitaminas : ISuplemento { public string Describir() => "Vitaminas para energía"; }

              // Builder para Rutinas de Alimentación
              public class RutinaAlimentacion
              {
                  public List<string> Pasos { get; } = new List<string>();
                  public void Mostrar()
                  {
                      Console.WriteLine("Rutina de Alimentación:");
                      foreach (var paso in Pasos) Console.WriteLine($"- {paso}");
                  }
              }

              public interface IRutinaBuilder
              {
                  void PrepararAlimento();
                  void PrepararBebida();
                  void PrepararSuplemento();
                  RutinaAlimentacion ObtenerRutina();
              }

              public class RutinaCaninaBuilder : IRutinaBuilder
              {
                  private RutinaAlimentacion rutina = new RutinaAlimentacion();
                  private readonly INutricionFactory factory;

                  public RutinaCaninaBuilder(INutricionFactory factory)
                  {
                      this.factory = factory;
                  }

                  public void PrepararAlimento() => rutina.Pasos.Add(factory.CrearAlimento().Describir());
                  public void PrepararBebida() => rutina.Pasos.Add(factory.CrearBebida().Describir());
                  public void PrepararSuplemento() => rutina.Pasos.Add(factory.CrearSuplemento().Describir());
                  public RutinaAlimentacion ObtenerRutina() => rutina;
              }

              // Prototype para clonar rutinas
              public class RutinaPrototype : ICloneable
              {
                  public RutinaAlimentacion Rutina { get; set; }
                  public object Clone()
                  {
                      var nuevaRutina = new RutinaAlimentacion();
                      nuevaRutina.Pasos.AddRange(this.Rutina.Pasos);
                      return new RutinaPrototype { Rutina = nuevaRutina };
                  }
              }

              // Factory Method para dietas
              public abstract class DietaCreator
              {
                  public abstract string CrearDieta();
              }

              public class DietaCanina : DietaCreator
              {
                  public override string CrearDieta() => "Dieta balanceada para perro";
              }

              public class DietaFelina : DietaCreator
              {
                  public override string CrearDieta() => "Dieta balanceada para gato";
              }

              // Servicios
              public class ServicioVeterinario
              {
                  public void RegistrarConsulta(Mascota mascota) =>
                      Console.WriteLine($"Consulta registrada para {mascota.Nombre}");
                  public void RegistrarVacuna(Mascota mascota) =>
                      Console.WriteLine($"Vacuna aplicada a {mascota.Nombre}");
              }

              public class HotelMascotas
              {
                  public void AsignarHabitacion(Mascota mascota) =>
                      Console.WriteLine($"Habitación asignada a {mascota.Nombre}");
              }

              // Programa principal
              public class Program
              {
                  public static void Main(string[] args)
                  {
                      var dog = new Mascota("Firulais", "Perro");
                      var cat = new Mascota("Mishi", "Gato");

                      RegistroMascotas.Instancia.RegistrarMascota(dog);
                      RegistroMascotas.Instancia.RegistrarMascota(cat);

                      var vet = new ServicioVeterinario();
                      vet.RegistrarConsulta(dog);
                      vet.RegistrarVacuna(cat);

                      var hotel = new HotelMascotas();
                      hotel.AsignarHabitacion(dog);
                      hotel.AsignarHabitacion(cat);

                      var factory = new NutricionCaninaFactory();
                      var builder = new RutinaCaninaBuilder(factory);
                      builder.PrepararAlimento();
                      builder.PrepararBebida();
                      builder.PrepararSuplemento();
                      var rutina = builder.ObtenerRutina();
                      rutina.Mostrar();

                      var rutinaPrototype = new RutinaPrototype { Rutina = rutina };
                      var rutinaClonada = (RutinaPrototype)rutinaPrototype.Clone();
                      rutinaClonada.Rutina.Mostrar();

                      DietaCreator dieta = new DietaCanina();
                      Console.WriteLine(dieta.CrearDieta());
                  }
              }
          }

    - name: Compilar la aplicación
      command: dotnet build /home/ubuntu/clinica_mascotas_app/clinica_mascotas_app.csproj
      args:
        chdir: /home/ubuntu/clinica_mascotas_app

    - name: Ejecutar la aplicación
      command: dotnet run
      args:
        chdir: /home/ubuntu/clinica_mascotas_app
      async: 10
      poll: 0
    
@IoTeacher
Copy link

🧾 Propuesta de calificación
Dado que es una práctica inicial, y considerando el dominio más amplio que Joel abordó, la integración funcional demostrada y los recursos aplicados:
Calificación sugerida: 90 / 100
Razonamiento:
Joel aplica correctamente todos los patrones requeridos (Abstract Factory, Factory Method, Singleton, Builder, Prototype).
Logra integrarlos en un escenario más complejo que solo “granja”, lo cual es un plus.
La propuesta de diseño parte de un diagnóstico claro del problema inicial.
Las áreas de mejora identificadas (tests, documentación interna, extensibilidad, validaciones) son mejoras esperables a futuro, no debilitan la solución principal.

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