|
1 |
| -. |
| 1 | + |
| 2 | + |
| 3 | +## ☕ Práctica: Refactorización con Singleton en una Cafetería |
| 4 | + |
| 5 | +### 🔍 Escenario del Mundo Real |
| 6 | + |
| 7 | +La cafetería "Café Singleton" tiene un sistema interno que utiliza una clase para llevar el **registro central de pedidos del día**. Sin embargo, cada vez que un barista toma un pedido, se crea una nueva instancia de `RegistroPedidos`, provocando inconsistencias y pérdidas de información. |
| 8 | + |
| 9 | +### 🎯 Objetivo |
| 10 | + |
| 11 | +Identificar los problemas en el diseño y aplicar el patrón **Singleton** para que toda la cafetería use la **misma instancia compartida del registro de pedidos**. |
| 12 | + |
| 13 | +--- |
| 14 | + |
| 15 | +### 🧩 Categoría del patrón: **Creacional** |
| 16 | + |
| 17 | +El patrón **Singleton** pertenece a los patrones **Creacionales**, ya que su objetivo es **controlar la instancia** de un objeto que solo debe existir **una vez en toda la aplicación**. |
| 18 | + |
| 19 | +--- |
| 20 | + |
| 21 | +### 📋 Actividades |
| 22 | + |
| 23 | +1. **Detecta los Code Smells** en el diseño inicial: |
| 24 | + |
| 25 | + * ¿Cuántas instancias se crean? |
| 26 | + * ¿Hay duplicidad de pedidos? |
| 27 | + * ¿Hay acoplamiento innecesario? |
| 28 | + |
| 29 | +2. **Refactoriza aplicando el patrón Singleton:** |
| 30 | + |
| 31 | + * Asegura que `RegistroPedidos` tenga **una sola instancia global y segura**. |
| 32 | + * Implementa la **versión thread-safe** para evitar errores en concurrencia. |
| 33 | + |
| 34 | +3. **Evalúa ventajas y riesgos del Singleton en este caso:** |
| 35 | + |
| 36 | + * ¿Podrías testear esta clase? |
| 37 | + * ¿Qué pasaría si decides reiniciar el conteo de pedidos? |
| 38 | + |
| 39 | +--- |
| 40 | + |
| 41 | +### 🧪 Sugerencias de patrones complementarios: |
| 42 | + |
| 43 | +* **Observer**: si quieres notificar a otros módulos cuando se agrega un pedido. |
| 44 | +* **Command**: si cada pedido se representa como un comando a ejecutar. |
| 45 | +* **Memento**: si quieres guardar el estado del registro a intervalos. |
| 46 | + |
| 47 | +--- |
| 48 | + |
| 49 | +### 🧾 Ansible Playbook de la práctica |
| 50 | + |
| 51 | +```yaml |
| 52 | +--- |
| 53 | +- name: Instalar .NET 8 y desplegar simulador de cafetería con Singleton |
| 54 | + hosts: localhost |
| 55 | + become: yes |
| 56 | + tasks: |
| 57 | + - name: Instalar dotnet-sdk-8.0 |
| 58 | + apt: |
| 59 | + name: dotnet-sdk-8.0 |
| 60 | + state: present |
| 61 | + |
| 62 | + - name: Crear carpeta de la práctica |
| 63 | + file: |
| 64 | + path: /home/ubuntu/cafeteria_singleton |
| 65 | + state: directory |
| 66 | + |
| 67 | + - name: Crear proyecto |
| 68 | + command: dotnet new console -n CafeteriaSingleton |
| 69 | + args: |
| 70 | + chdir: /home/ubuntu/cafeteria_singleton |
| 71 | + |
| 72 | + - name: Copiar código con errores de diseño |
| 73 | + copy: |
| 74 | + dest: /home/ubuntu/cafeteria_singleton/CafeteriaSingleton/Program.cs |
| 75 | + content: | |
| 76 | + // Código con múltiples instancias de RegistroPedidos |
| 77 | + // TU MISIÓN: Detectar, refactorizar, aplicar Singleton |
| 78 | +
|
| 79 | + - name: Ejecutar el código para observar problemas |
| 80 | + command: dotnet run |
| 81 | + args: |
| 82 | + chdir: /home/ubuntu/cafeteria_singleton/CafeteriaSingleton |
| 83 | +``` |
| 84 | +
|
| 85 | +--- |
| 86 | +
|
| 87 | +☕ Código inicial con errores (sin Singleton) |
| 88 | +
|
| 89 | +Aquí tienes el **código inicial defectuoso** del sistema de pedidos de una cafetería. Está **intencionalmente mal diseñado** para que puedas **practicar la refactorización aplicando el patrón Singleton**. |
| 90 | +
|
| 91 | +--- |
| 92 | +
|
| 93 | +## ☕ Código inicial con errores (sin Singleton) |
| 94 | +
|
| 95 | +📁 **Ubicación sugerida:** `/home/ubuntu/cafeteria_singleton/CafeteriaSingleton/Program.cs` |
| 96 | + |
| 97 | +```csharp |
| 98 | +using System; |
| 99 | +using System.Collections.Generic; |
| 100 | +
|
| 101 | +namespace CafeteriaSingleton |
| 102 | +{ |
| 103 | + public class Pedido |
| 104 | + { |
| 105 | + public string Cliente { get; set; } |
| 106 | + public string Bebida { get; set; } |
| 107 | +
|
| 108 | + public Pedido(string cliente, string bebida) |
| 109 | + { |
| 110 | + Cliente = cliente; |
| 111 | + Bebida = bebida; |
| 112 | + } |
| 113 | + } |
| 114 | +
|
| 115 | + // ❌ Esta clase se instancia cada vez, perdiendo el control central |
| 116 | + public class RegistroPedidos |
| 117 | + { |
| 118 | + private List<Pedido> pedidos = new List<Pedido>(); |
| 119 | +
|
| 120 | + public void AgregarPedido(Pedido pedido) |
| 121 | + { |
| 122 | + pedidos.Add(pedido); |
| 123 | + Console.WriteLine($"📝 Pedido agregado: {pedido.Cliente} - {pedido.Bebida}"); |
| 124 | + } |
| 125 | +
|
| 126 | + public void MostrarPedidos() |
| 127 | + { |
| 128 | + Console.WriteLine("📋 Pedidos registrados:"); |
| 129 | + foreach (var pedido in pedidos) |
| 130 | + { |
| 131 | + Console.WriteLine($"- {pedido.Cliente}: {pedido.Bebida}"); |
| 132 | + } |
| 133 | + } |
| 134 | + } |
| 135 | +
|
| 136 | + class Program |
| 137 | + { |
| 138 | + static void Main(string[] args) |
| 139 | + { |
| 140 | + // ☠️ Cada barista crea su propio registro |
| 141 | + var registroBarista1 = new RegistroPedidos(); |
| 142 | + registroBarista1.AgregarPedido(new Pedido("Ana", "Latte")); |
| 143 | +
|
| 144 | + var registroBarista2 = new RegistroPedidos(); |
| 145 | + registroBarista2.AgregarPedido(new Pedido("Luis", "Café Americano")); |
| 146 | +
|
| 147 | + // ❌ No se ven todos los pedidos, cada uno tiene su propia lista |
| 148 | + Console.WriteLine("\nRegistro del barista 1:"); |
| 149 | + registroBarista1.MostrarPedidos(); |
| 150 | +
|
| 151 | + Console.WriteLine("\nRegistro del barista 2:"); |
| 152 | + registroBarista2.MostrarPedidos(); |
| 153 | +
|
| 154 | + Console.WriteLine("\n¿Dónde está la lista completa? 🤔"); |
| 155 | + } |
| 156 | + } |
| 157 | +} |
| 158 | +``` |
| 159 | + |
| 160 | +--- |
| 161 | + |
| 162 | +### 🚨 Code Smells Detectados |
| 163 | + |
| 164 | +* Se crean múltiples instancias de `RegistroPedidos`. |
| 165 | +* No hay un punto de acceso único ni control de concurrencia. |
| 166 | +* El estado de los pedidos **no se comparte** entre baristas. |
| 167 | +* Violación del principio **Single Source of Truth**. |
| 168 | + |
| 169 | +--- |
| 170 | + |
| 171 | +### 🧠 Desafío |
| 172 | + |
| 173 | +Refactoriza `RegistroPedidos` aplicando el patrón **Singleton (versión thread-safe con lazy initialization)** para que **todos los baristas usen la misma instancia compartida**. |
| 174 | + |
| 175 | +¿Listo para intentarlo? |
| 176 | +¿Quieres que también te sugiera pasos guía para la refactorización? |
0 commit comments