Skip to content

Instantly share code, notes, and snippets.

@LaChivaloca69
Last active September 18, 2025 00:49
Show Gist options
  • Select an option

  • Save LaChivaloca69/dc79085cab926d7765a682f103df6f65 to your computer and use it in GitHub Desktop.

Select an option

Save LaChivaloca69/dc79085cab926d7765a682f103df6f65 to your computer and use it in GitHub Desktop.

Alvarado Cardona Antonio

Codigo con errores

using System;
using System.Collections.Generic;

namespace CafeteriaSingleton
{
    public class Pedido
    {
        public string Cliente { get; set; }
        public string Bebida { get; set; }

        public Pedido(string cliente, string bebida)
        {
            Cliente = cliente;
            Bebida = bebida;
        }
    }

    // ❌ Esta clase se instancia cada vez, perdiendo el control central
    public class RegistroPedidos
    {
        private List<Pedido> pedidos = new List<Pedido>();

        public void AgregarPedido(Pedido pedido)
        {
            pedidos.Add(pedido);
            Console.WriteLine($"📝 Pedido agregado: {pedido.Cliente} - {pedido.Bebida}");
        }

        public void MostrarPedidos()
        {
            Console.WriteLine("📋 Pedidos registrados:");
            foreach (var pedido in pedidos)
            {
                Console.WriteLine($"- {pedido.Cliente}: {pedido.Bebida}");
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // ☠️ Cada barista crea su propio registro
            var registroBarista1 = new RegistroPedidos();
            registroBarista1.AgregarPedido(new Pedido("Ana", "Latte"));

            var registroBarista2 = new RegistroPedidos();
            registroBarista2.AgregarPedido(new Pedido("Luis", "Café Americano"));

            // ❌ No se ven todos los pedidos, cada uno tiene su propia lista
            Console.WriteLine("\nRegistro del barista 1:");
            registroBarista1.MostrarPedidos();

            Console.WriteLine("\nRegistro del barista 2:");
            registroBarista2.MostrarPedidos();

            Console.WriteLine("\n¿Dónde está la lista completa? 🤔");
        }
    }
}

Detecta el problema

¿Por qué hay múltiples instancias de RegistroPedidos?

Porque la clase tenía un constructor público y en Main() cada "barista" hacía new RegistroPedidos(). Eso crea varias listas independientes en memoria.

¿Qué pasa con los pedidos registrados por diferentes baristas?

Cada barista agrega a su propia lista privada: las entradas quedan fragmentadas y nadie tiene la vista completa.

¿Cuál sería la consecuencia si varios hilos acceden a este objeto?

List no es thread-safe. Accesos concurrentes pueden provocar condiciones de carrera, excepciones (modificación durante enumeración), pérdidas de datos o duplicados inconsistentes.

Actividad Codigo con singleton

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace CafeteriaSingleton
{
    public class Pedido
    {
        public string Cliente { get; set; }
        public string Bebida { get; set; }

        public Pedido(string cliente, string bebida)
        {
            Cliente = cliente;
            Bebida = bebida;
        }

        public override string ToString() => $"{Cliente} - {Bebida}";
    }

    // Clase sellada para prevenir herencia indeseada
    public sealed class RegistroPedidos
    {
        // Estado interno (protegido)
        private readonly List<Pedido> pedidos = new List<Pedido>();
        private readonly object _listaLock = new object();

        // 3️⃣ Instancia estática privada
        private static RegistroPedidos _instancia;

        // 4️⃣ Candado para creación (lazy y thread-safe)
        private static readonly object _candado = new object();

        // 2️⃣ Constructor privado para evitar instancias externas
        private RegistroPedidos() { }

        // 5️⃣ Punto único de acceso (Singleton, con lock)
        public static RegistroPedidos ObtenerInstancia()
        {
            lock (_candado)
            {
                if (_instancia == null)
                {
                    _instancia = new RegistroPedidos();
                }
                return _instancia;
            }
        }

        // 8️⃣ Métodos que protegen el estado interno (locks y validaciones)
        public bool AgregarPedido(Pedido pedido)
        {
            if (pedido == null) return false;
            lock (_listaLock)
            {
                // Validar duplicados (misma combinacion Cliente+Bebida)
                if (pedidos.Any(p => p.Cliente == pedido.Cliente && p.Bebida == pedido.Bebida))
                {
                    Console.WriteLine($"⚠️ Pedido duplicado detectado (evitado): {pedido}");
                    return false;
                }
                pedidos.Add(pedido);
                Console.WriteLine($"📝 Pedido agregado: {pedido}");
                return true;
            }
        }

        public void MostrarPedidos()
        {
            lock (_listaLock)
            {
                Console.WriteLine("📋 Pedidos registrados:");
                if (pedidos.Count == 0)
                {
                    Console.WriteLine("- (vacío)");
                    return;
                }
                foreach (var pedido in pedidos)
                {
                    Console.WriteLine($"- {pedido.Cliente}: {pedido.Bebida}");
                }
            }
        }

        public bool BorrarPedido(string cliente, string bebida)
        {
            lock (_listaLock)
            {
                var p = pedidos.FirstOrDefault(x => x.Cliente == cliente && x.Bebida == bebida);
                if (p != null)
                {
                    pedidos.Remove(p);
                    Console.WriteLine($"🗑️ Pedido borrado: {p}");
                    return true;
                }
                return false;
            }
        }

        public void Reiniciar()
        {
            lock (_listaLock)
            {
                pedidos.Clear();
                Console.WriteLine("🔁 Registro reiniciado.");
            }
        }

        public string ExportarCSV()
        {
            lock (_listaLock)
            {
                var lines = pedidos.Select(p => $"{EscapeCsv(p.Cliente)},{EscapeCsv(p.Bebida)}");
                return "Cliente,Bebida\n" + string.Join("\n", lines);
            }
        }

        private string EscapeCsv(string s) => s?.Contains(",") == true ? $"\"{s.Replace("\"","\"\"")}\"" : s ?? "";
    }

    class Program
    {
        static void Main(string[] args)
        {
            // 6️⃣ Reemplazamos la creación directa por ObtenerInstancia()
            var registroBarista1 = RegistroPedidos.ObtenerInstancia();
            registroBarista1.AgregarPedido(new Pedido("Ana", "Latte"));

            var registroBarista2 = RegistroPedidos.ObtenerInstancia();
            registroBarista2.AgregarPedido(new Pedido("Luis", "Café Americano"));

            // 7️⃣ Verificación: ambos "baristas" deberían ver la misma lista unificada
            Console.WriteLine("\nRegistro (barista 1):");
            registroBarista1.MostrarPedidos();

            Console.WriteLine("\nRegistro (barista 2):");
            registroBarista2.MostrarPedidos();

            Console.WriteLine($"\n¿Misma instancia? {ReferenceEquals(registroBarista1, registroBarista2)}");

            Console.WriteLine("\nExportando CSV:");
            Console.WriteLine(RegistroPedidos.ObtenerInstancia().ExportarCSV());

            // Ejemplo corto de concurrencia: arrancamos varios hilos que agregan pedidos
            var threads = new List<Thread>();
            for (int i = 0; i < 5; i++)
            {
                int id = i;
                var t = new Thread(() =>
                {
                    var reg = RegistroPedidos.ObtenerInstancia();
                    reg.AgregarPedido(new Pedido($"Cliente{id}", $"Bebida{id}"));
                });
                threads.Add(t);
                t.Start();
            }
            foreach (var t in threads) t.Join();

            Console.WriteLine("\nRegistro final tras hilos:");
            RegistroPedidos.ObtenerInstancia().MostrarPedidos();
        }
    }
}

Reflexion

¿Qué ventajas trajo el Singleton?

Estado centralizado y consistente: todos los baristas ven la misma lista.

Control del acceso a la instancia (creación única).

Fácil de usar en código existente (llamadas estáticas).

¿Limitaciones / riesgos?

Introduce estado global: puede incrementar el acoplamiento y dificultar testing.

Puede ocultar dependencias; componentes que usan RegistroPedidos.ObtenerInstancia() quedan acoplados al Singleton.

El Singleton puede complicar la gestión del ciclo de vida (cuando necesitar liberar recursos o reconfigurar).

En diseños grandes, se prefiere inyección de dependencias (DI) para control y testabilidad.

¿Sería mejor usar Dependency Injection?

Sí, en muchos escenarios empresariales DI es preferible: permite cambiar implementaciones, facilita testing y controla scope/vida del objeto. El Singleton es útil cuando realmente necesitas una única instancia global y quieres simplicidad rápida.

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