Los patrones de diseño son: soluciones habituales a problemas comunes en el diseño de software.
Cada patrón es como un plano que se puede personalizar para resolver un problema de diseño particular de tu código.
La siguiente imagen muestra los patrones de diseño en C#.
Patrones de diseño en .NET (C#)
Podemos ver tres tipos y sus divisiones.
- Patrones creacionales: proporcionan varios mecanismos de creación de objetos que incrementan la flexibilidad y la reutilización del código existente.
- Patrones estructurales: estructurales explican cómo ensamblar objetos y clases en estructuras más grandes, a la vez que se mantiene la flexibilidad y eficiencia de estas estructuras.
- Patrones de comportamiento: los que tratan con algoritmos y la asignación de responsabilidades entre objetos.
Patrones Creacionales (Creational Patterns)
Factory method
Permite crear familias de objetos relacionados sin especificar sus clases concretas.
Ejemplo:
// Producto abstracto public abstract class Documento { public abstract void Mostrar(); } // Productos concretos public class PDF : Documento { public override void Mostrar() => Console.WriteLine("Mostrando documento PDF"); } public class Word : Documento { public override void Mostrar() => Console.WriteLine("Mostrando documento Word"); } // Creador abstracto public abstract class CreadorDocumento { public abstract Documento CrearDocumento(); } // Creador concreto public class CreadorPDF : CreadorDocumento { public override Documento CrearDocumento() => new PDF(); } public class CreadorWord : CreadorDocumento { public override Documento CrearDocumento() => new Word(); } // Uso del patrón class Program { static void Main() { CreadorDocumento creador = new CreadorPDF(); Documento doc = creador.CrearDocumento(); doc.Mostrar(); creador = new CreadorWord(); doc = creador.CrearDocumento(); doc.Mostrar(); } }
Abstract factory
Permite crear familias de objetos relacionados sin especificar sus clases concretas.
Ejemplo:
// Productos abstractos public interface ISilla { void Sentarse(); } public interface IMesa { void Usar(); } // Productos concretos public class SillaModerna : ISilla { public void Sentarse() => Console.WriteLine("Sentado en una silla moderna."); } public class MesaModerna : IMesa { public void Usar() => Console.WriteLine("Usando una mesa moderna."); } public class SillaClasica : ISilla { public void Sentarse() => Console.WriteLine("Sentado en una silla clásica."); } public class MesaClasica : IMesa { public void Usar() => Console.WriteLine("Usando una mesa clásica."); } // Fábrica abstracta public interface IFabricaMuebles { ISilla CrearSilla(); IMesa CrearMesa(); } // Fábricas concretas public class FabricaModerna : IFabricaMuebles { public ISilla CrearSilla() => new SillaModerna(); public IMesa CrearMesa() => new MesaModerna(); } public class FabricaClasica : IFabricaMuebles { public ISilla CrearSilla() => new SillaClasica(); public IMesa CrearMesa() => new MesaClasica(); } // Uso del patrón class Program { static void Main() { IFabricaMuebles fabrica = new FabricaModerna(); ISilla silla = fabrica.CrearSilla(); IMesa mesa = fabrica.CrearMesa(); silla.Sentarse(); mesa.Usar(); fabrica = new FabricaClasica(); silla = fabrica.CrearSilla(); mesa = fabrica.CrearMesa(); silla.Sentarse(); mesa.Usar(); } }
Builder
Permite construir objetos complejos paso a paso. Este patrón permite generar diferentes tipos y representaciones de un objeto utilizando el mismo código de construcción.
Ejemplo:
// Producto complejo public class Computadora { public string CPU { get; set; } public string RAM { get; set; } public string Almacenamiento { get; set; } public void Mostrar() { Console.WriteLine($"CPU: {CPU}, RAM: {RAM}, Almacenamiento: {Almacenamiento}"); } } // Builder abstracto public interface IBuilderComputadora { void ConstruirCPU(); void ConstruirRAM(); void ConstruirAlmacenamiento(); Computadora ObtenerComputadora(); } // Builder concreto public class BuilderComputadoraGaming : IBuilderComputadora { private Computadora computadora = new Computadora(); public void ConstruirCPU() => computadora.CPU = "Intel Core i9"; public void ConstruirRAM() => computadora.RAM = "32 GB"; public void ConstruirAlmacenamiento() => computadora.Almacenamiento = "1 TB SSD"; public Computadora ObtenerComputadora() => computadora; } public class BuilderComputadoraOficina : IBuilderComputadora { private Computadora computadora = new Computadora(); public void ConstruirCPU() => computadora.CPU = "Intel Core i5"; public void ConstruirRAM() => computadora.RAM = "8 GB"; public void ConstruirAlmacenamiento() => computadora.Almacenamiento = "500 GB HDD"; public Computadora ObtenerComputadora() => computadora; } // Director public class Director { public void Construir(IBuilderComputadora builder) { builder.ConstruirCPU(); builder.ConstruirRAM(); builder.ConstruirAlmacenamiento(); } } // Uso del patrón class Program { static void Main() { Director director = new Director(); IBuilderComputadora builderGaming = new BuilderComputadoraGaming(); director.Construir(builderGaming); Computadora pcGaming = builderGaming.ObtenerComputadora(); pcGaming.Mostrar(); IBuilderComputadora builderOficina = new BuilderComputadoraOficina(); director.Construir(builderOficina); Computadora pcOficina = builderOficina.ObtenerComputadora(); pcOficina.Mostrar(); } }
Prototype
Te permite copiar objetos existentes sin que tu código dependa de sus clases.
Ejemplo:
using System; // Interfaz Prototype public interface IFigura { IFigura Clonar(); void Mostrar(); } // Clase concreta public class Circulo : IFigura { public int Radio { get; set; } public Circulo(int radio) { Radio = radio; } public IFigura Clonar() { // Clonación superficial return new Circulo(this.Radio); } public void Mostrar() { Console.WriteLine($"Círculo con radio: {Radio}"); } } public class Cuadrado : IFigura { public int Lado { get; set; } public Cuadrado(int lado) { Lado = lado; } public IFigura Clonar() { return new Cuadrado(this.Lado); } public void Mostrar() { Console.WriteLine($"Cuadrado con lado: {Lado}"); } } // Uso del patrón class Program { static void Main() { Circulo c1 = new Circulo(10); Circulo c2 = (Circulo)c1.Clonar(); c1.Mostrar(); c2.Mostrar(); Cuadrado q1 = new Cuadrado(5); Cuadrado q2 = (Cuadrado)q1.Clonar(); q1.Mostrar(); q2.Mostrar(); } }
Singleton
Permite garantizar que una clase tenga una sola instancia, a la vez que proporciona un punto de acceso global a dicha instancia.
Ejemplo:
using System; public sealed class Configuracion { // Instancia única (lazy initialization con thread-safety) private static readonly Lazy<Configuracion> instancia = new Lazy<Configuracion>(() => new Configuracion()); // Propiedad pública para acceder a la instancia public static Configuracion Instancia => instancia.Value; // Propiedades de ejemplo public string NombreAplicacion { get; private set; } // Constructor privado para evitar instanciación externa private Configuracion() { NombreAplicacion = "MiAplicacion v1.0"; } // Método de ejemplo public void Mostrar() { Console.WriteLine($"Configuración cargada: {NombreAplicacion}"); } } class Program { static void Main() { // Acceder a la única instancia Configuracion c1 = Configuracion.Instancia; c1.Mostrar(); // Intentar obtener otra instancia devuelve la misma Configuracion c2 = Configuracion.Instancia; Console.WriteLine(Object.ReferenceEquals(c1, c2)); // True } }
Patrones Estructurales (Structural Patterns)
Adapter
Permite que objetos con interfaces incompatibles colaboren.
Ejemplo:
using System; // Interfaz esperada por el cliente public interface IReproductorAudio { void Reproducir(string archivo); } // Clase existente con una interfaz diferente public class ServicioMp3 { public void PlayMp3(string nombreArchivo) { Console.WriteLine($"Reproduciendo archivo MP3: {nombreArchivo}"); } } // Adaptador que traduce entre la interfaz esperada y la clase existente public class AdaptadorMp3 : IReproductorAudio { private readonly ServicioMp3 servicioMp3; public AdaptadorMp3(ServicioMp3 servicio) { servicioMp3 = servicio; } public void Reproducir(string archivo) { // Traduce la llamada servicioMp3.PlayMp3(archivo); } } // Uso del patrón class Program { static void Main() { // Cliente espera trabajar con IReproductorAudio IReproductorAudio reproductor = new AdaptadorMp3(new ServicioMp3()); reproductor.Reproducir("cancion.mp3"); } }
Facade
Proporciona una interfaz simplificada para una biblioteca, un marco de trabajo o cualquier otro conjunto complejo de clases.
Ejemplo:
using System; // Subsistemas public class DVDPlayer { public void Encender() => Console.WriteLine("DVD Player encendido"); public void Reproducir() => Console.WriteLine("Reproduciendo DVD..."); } public class Proyector { public void Encender() => Console.WriteLine("Proyector encendido"); public void ApagarLuces() => Console.WriteLine("Luces apagadas para la proyección"); } public class SistemaSonido { public void Encender() => Console.WriteLine("Sistema de sonido encendido"); public void AjustarVolumen(int nivel) => Console.WriteLine($"Volumen ajustado a {nivel}"); } // Facade public class HomeTheaterFacade { private DVDPlayer dvd; private Proyector proyector; private SistemaSonido sonido; public HomeTheaterFacade(DVDPlayer dvd, Proyector proyector, SistemaSonido sonido) { this.dvd = dvd; this.proyector = proyector; this.sonido = sonido; } public void VerPelicula() { Console.WriteLine("Preparando el sistema para ver la película..."); proyector.Encender(); proyector.ApagarLuces(); sonido.Encender(); sonido.AjustarVolumen(10); dvd.Encender(); dvd.Reproducir(); } } // Uso del patrón class Program { static void Main() { DVDPlayer dvd = new DVDPlayer(); Proyector proyector = new Proyector(); SistemaSonido sonido = new SistemaSonido(); HomeTheaterFacade homeTheater = new HomeTheaterFacade(dvd, proyector, sonido); homeTheater.VerPelicula(); } }
Brigde
Permite dividir una clase grande o un conjunto de clases estrechamente relacionadas en dos jerarquías separadas —abstracción e implementación— que pueden desarrollarse independientemente una de la otra.
Ejemplo:
using System; // Implementador public interface IDispositivo { void Encender(); void Apagar(); } // Implementaciones concretas public class Televisor : IDispositivo { public void Encender() => Console.WriteLine("Televisor encendido"); public void Apagar() => Console.WriteLine("Televisor apagado"); } public class Radio : IDispositivo { public void Encender() => Console.WriteLine("Radio encendida"); public void Apagar() => Console.WriteLine("Radio apagada"); } // Abstracción public abstract class ControlRemoto { protected IDispositivo dispositivo; protected ControlRemoto(IDispositivo dispositivo) { this.dispositivo = dispositivo; } public abstract void BotonEncender(); public abstract void BotonApagar(); } // Abstracción refinada public class ControlBasico : ControlRemoto { public ControlBasico(IDispositivo dispositivo) : base(dispositivo) { } public override void BotonEncender() => dispositivo.Encender(); public override void BotonApagar() => dispositivo.Apagar(); } // Uso del patrón class Program { static void Main() { IDispositivo tv = new Televisor(); ControlRemoto controlTv = new ControlBasico(tv); controlTv.BotonEncender(); controlTv.BotonApagar(); IDispositivo radio = new Radio(); ControlRemoto controlRadio = new ControlBasico(radio); controlRadio.BotonEncender(); controlRadio.BotonApagar(); } }
Composite
Permite componer objetos en estructuras de árbol y luego trabajar con estas estructuras como si fueran objetos individuales.
Ejemplo:
using System; using System.Collections.Generic; // Componente public abstract class MenuComponent { public abstract void Mostrar(); } // Hoja (Leaf) public class Plato : MenuComponent { private string nombre; public Plato(string nombre) { this.nombre = nombre; } public override void Mostrar() { Console.WriteLine($"Plato: {nombre}"); } } // Compuesto (Composite) public class Menu : MenuComponent { private string nombre; private List<MenuComponent> elementos = new List<MenuComponent>(); public Menu(string nombre) { this.nombre = nombre; } public void Agregar(MenuComponent componente) { elementos.Add(componente); } public override void Mostrar() { Console.WriteLine($"Menú: {nombre}"); foreach (var elemento in elementos) { elemento.Mostrar(); } } } // Uso del patrón class Program { static void Main() { // Crear platos Plato sopa = new Plato("Sopa de verduras"); Plato pasta = new Plato("Pasta al pesto"); Plato postre = new Plato("Tiramisú"); // Crear menú principal Menu menuPrincipal = new Menu("Menú Principal"); menuPrincipal.Agregar(sopa); menuPrincipal.Agregar(pasta); // Crear menú de postres Menu menuPostres = new Menu("Menú de Postres"); menuPostres.Agregar(postre); // Agregar menú de postres al menú principal menuPrincipal.Agregar(menuPostres); // Mostrar todo menuPrincipal.Mostrar(); } }
Proxy
Permite proporcionar un sustituto o marcador de posición para otro objeto. Un proxy controla el acceso al objeto original, lo que permite realizar alguna acción antes o después de que la solicitud llegue al objeto original.
Ejemplo:
using System; // Interfaz común public interface IImagen { void Mostrar(); } // Clase real (objeto pesado) public class ImagenReal : IImagen { private string archivo; public ImagenReal(string archivo) { this.archivo = archivo; CargarDesdeDisco(); } private void CargarDesdeDisco() { Console.WriteLine($"Cargando imagen desde disco: {archivo}"); } public void Mostrar() { Console.WriteLine($"Mostrando imagen: {archivo}"); } } // Proxy public class ProxyImagen : IImagen { private string archivo; private ImagenReal imagenReal; public ProxyImagen(string archivo) { this.archivo = archivo; } public void Mostrar() { // Carga diferida: solo se crea la imagen real cuando se necesita if (imagenReal == null) { imagenReal = new ImagenReal(archivo); } imagenReal.Mostrar(); } } // Uso del patrón class Program { static void Main() { IImagen imagen = new ProxyImagen("foto_vacaciones.jpg"); // La imagen aún no se carga desde disco Console.WriteLine("Proxy creado, aún no se carga la imagen..."); // Se carga y muestra solo cuando se necesita imagen.Mostrar(); } }
Patrones de comportamiento (Behavioral Patterns)
Command
Convierte una solicitud en un objeto independiente que contiene toda la información sobre la misma. Esta transformación permite pasar solicitudes como argumentos de un método, retrasar o poner en cola la ejecución de una solicitud y admitir operaciones que no se pueden deshacer.
Ejemplo:
using System; // Interfaz Command public interface ICommand { void Ejecutar(); } // Receptor public class Luz { public void Encender() => Console.WriteLine("La luz está encendida"); public void Apagar() => Console.WriteLine("La luz está apagada"); } // Comandos concretos public class EncenderLuzCommand : ICommand { private Luz luz; public EncenderLuzCommand(Luz luz) { this.luz = luz; } public void Ejecutar() { luz.Encender(); } } public class ApagarLuzCommand : ICommand { private Luz luz; public ApagarLuzCommand(Luz luz) { this.luz = luz; } public void Ejecutar() { luz.Apagar(); } } // Invocador public class ControlRemoto { private ICommand comando; public void SetComando(ICommand comando) { this.comando = comando; } public void PresionarBoton() { comando.Ejecutar(); } } // Uso del patrón class Program { static void Main() { Luz sala = new Luz(); ICommand encender = new EncenderLuzCommand(sala); ICommand apagar = new ApagarLuzCommand(sala); ControlRemoto control = new ControlRemoto(); control.SetComando(encender); control.PresionarBoton(); // La luz está encendida control.SetComando(apagar); control.PresionarBoton(); // La luz está apagada } }
Observer
Permite definir un mecanismo de suscripción para notificar a varios objetos sobre cualquier evento que ocurra en el objeto que están observando.
Ejemplo:
using System; using System.Collections.Generic; // Interfaz del observador public interface IObservador { void Actualizar(float temperatura); } // Sujeto public class EstacionClima { private List<IObservador> observadores = new List<IObservador>(); private float temperatura; public void Registrar(IObservador observador) { observadores.Add(observador); } public void Remover(IObservador observador) { observadores.Remove(observador); } public void SetTemperatura(float nuevaTemperatura) { temperatura = nuevaTemperatura; Notificar(); } private void Notificar() { foreach (var obs in observadores) { obs.Actualizar(temperatura); } } } // Observadores concretos public class PantallaCelular : IObservador { public void Actualizar(float temperatura) { Console.WriteLine($"[Celular] Temperatura actual: {temperatura}°C"); } } public class PantallaPC : IObservador { public void Actualizar(float temperatura) { Console.WriteLine($"[PC] Temperatura actual: {temperatura}°C"); } } // Uso del patrón class Program { static void Main() { EstacionClima estacion = new EstacionClima(); IObservador celular = new PantallaCelular(); IObservador pc = new PantallaPC(); estacion.Registrar(celular); estacion.Registrar(pc); estacion.SetTemperatura(25.5f); estacion.SetTemperatura(30.0f); } }
Strategy
Permite definir una familia de algoritmos, colocar cada uno de ellos en una clase separada y hacer que sus objetos sean intercambiables.
Ejemplo:
using System; // Estrategia (Strategy) public interface IPago { void Pagar(double monto); } // Estrategias concretas public class PagoTarjeta : IPago { public void Pagar(double monto) { Console.WriteLine($"Pagando {monto} con tarjeta de crédito."); } } public class PagoPayPal : IPago { public void Pagar(double monto) { Console.WriteLine($"Pagando {monto} con PayPal."); } } public class PagoEfectivo : IPago { public void Pagar(double monto) { Console.WriteLine($"Pagando {monto} en efectivo."); } } // Contexto public class CarritoCompras { private IPago estrategiaPago; public void SetEstrategiaPago(IPago estrategia) { estrategiaPago = estrategia; } public void ProcesarCompra(double monto) { if (estrategiaPago == null) { Console.WriteLine("No se ha definido una estrategia de pago."); return; } estrategiaPago.Pagar(monto); } } // Uso del patrón class Program { static void Main() { CarritoCompras carrito = new CarritoCompras(); carrito.SetEstrategiaPago(new PagoTarjeta()); carrito.ProcesarCompra(500); carrito.SetEstrategiaPago(new PagoPayPal()); carrito.ProcesarCompra(300); carrito.SetEstrategiaPago(new PagoEfectivo()); carrito.ProcesarCompra(150); } }
State
Permite que un objeto altere su comportamiento cuando cambia su estado interno. Parece como si el objeto hubiera cambiado de clase.
Ejemplo:
using System; // Interfaz Estado public interface IEstadoCafe { void PresionarBoton(); } // Estados concretos public class EstadoSinMoneda : IEstadoCafe { public void PresionarBoton() { Console.WriteLine("Inserta una moneda primero."); } } public class EstadoConMoneda : IEstadoCafe { public void PresionarBoton() { Console.WriteLine("Preparando café..."); } } // Contexto public class MaquinaCafe { private IEstadoCafe estado; public MaquinaCafe() { estado = new EstadoSinMoneda(); } public void InsertarMoneda() { Console.WriteLine("Moneda insertada."); estado = new EstadoConMoneda(); } public void PresionarBoton() { estado.PresionarBoton(); // Después de servir café, vuelve al estado inicial estado = new EstadoSinMoneda(); } } // Uso del patrón class Program { static void Main() { MaquinaCafe maquina = new MaquinaCafe(); maquina.PresionarBoton(); // Inserta una moneda primero maquina.InsertarMoneda(); // Moneda insertada maquina.PresionarBoton(); // Preparando café... maquina.PresionarBoton(); // Inserta una moneda primero } }
Template Method
Define el esqueleto de un algoritmo en la superclase, pero permite que las subclases anulen pasos específicos del algoritmo sin cambiar su estructura.
Ejemplo:
using System; // Clase abstracta con el método plantilla public abstract class BebidaCaliente { // Método plantilla public void Preparar() { HervirAgua(); PrepararInfusion(); ServirEnTaza(); AgregarExtras(); } private void HervirAgua() { Console.WriteLine("Hirviendo agua..."); } private void ServirEnTaza() { Console.WriteLine("Sirviendo en la taza..."); } // Métodos abstractos que las subclases deben implementar protected abstract void PrepararInfusion(); protected abstract void AgregarExtras(); } // Subclase concreta: Té public class Te : BebidaCaliente { protected override void PrepararInfusion() { Console.WriteLine("Colocando bolsita de té..."); } protected override void AgregarExtras() { Console.WriteLine("Agregando limón."); } } // Subclase concreta: Café public class Cafe : BebidaCaliente { protected override void PrepararInfusion() { Console.WriteLine("Agregando café molido al filtro..."); } protected override void AgregarExtras() { Console.WriteLine("Agregando azúcar y leche."); } } // Uso del patrón class Program { static void Main() { BebidaCaliente te = new Te(); Console.WriteLine("Preparando té:"); te.Preparar(); Console.WriteLine(); BebidaCaliente cafe = new Cafe(); Console.WriteLine("Preparando café:"); cafe.Preparar(); } }
Visitor
Permite separar los algoritmos de los objetos sobre los que operan.
Ejemplo:
using System; using System.Collections.Generic; // Interfaz del Visitor public interface IVisitor { void VisitarCirculo(Circulo circulo); void VisitarCuadrado(Cuadrado cuadrado); } // Elemento abstracto public interface IFigura { void Aceptar(IVisitor visitor); } // Elementos concretos public class Circulo : IFigura { public int Radio { get; set; } public Circulo(int radio) { Radio = radio; } public void Aceptar(IVisitor visitor) { visitor.VisitarCirculo(this); } } public class Cuadrado : IFigura { public int Lado { get; set; } public Cuadrado(int lado) { Lado = lado; } public void Aceptar(IVisitor visitor) { visitor.VisitarCuadrado(this); } } // Visitor concreto: calcular área public class VisitorArea : IVisitor { public void VisitarCirculo(Circulo circulo) { double area = Math.PI * circulo.Radio * circulo.Radio; Console.WriteLine($"Área del círculo: {area}"); } public void VisitarCuadrado(Cuadrado cuadrado) { double area = cuadrado.Lado * cuadrado.Lado; Console.WriteLine($"Área del cuadrado: {area}"); } } // Uso del patrón class Program { static void Main() { List<IFigura> figuras = new List<IFigura> { new Circulo(5), new Cuadrado(4) }; IVisitor visitorArea = new VisitorArea(); foreach (var figura in figuras) { figura.Aceptar(visitorArea); } } }
Chain of Responsibility
Permite que las solicitudes se transmitan a través de una cadena de manejadores. Al recibir una solicitud, cada manejador decide si la procesa o la pasa al siguiente manejador de la cadena.
Ejemplo:
using System; // Interfaz Handler public abstract class Aprobador { protected Aprobador siguiente; public void SetSiguiente(Aprobador aprobador) { siguiente = aprobador; } public abstract void ProcesarSolicitud(double monto); } // Handlers concretos public class Jefe : Aprobador { public override void ProcesarSolicitud(double monto) { if (monto <= 1000) { Console.WriteLine($"Jefe aprueba gasto de {monto}."); } else if (siguiente != null) { siguiente.ProcesarSolicitud(monto); } } } public class Gerente : Aprobador { public override void ProcesarSolicitud(double monto) { if (monto <= 5000) { Console.WriteLine($"Gerente aprueba gasto de {monto}."); } else if (siguiente != null) { siguiente.ProcesarSolicitud(monto); } } } public class Director : Aprobador { public override void ProcesarSolicitud(double monto) { if (monto <= 20000) { Console.WriteLine($"Director aprueba gasto de {monto}."); } else { Console.WriteLine($"Solicitud de {monto} requiere junta directiva."); } } } // Uso del patrón class Program { static void Main() { // Crear la cadena de responsabilidad Aprobador jefe = new Jefe(); Aprobador gerente = new Gerente(); Aprobador director = new Director(); jefe.SetSiguiente(gerente); gerente.SetSiguiente(director); // Probar solicitudes jefe.ProcesarSolicitud(500); // Jefe aprueba jefe.ProcesarSolicitud(3000); // Gerente aprueba jefe.ProcesarSolicitud(15000); // Director aprueba jefe.ProcesarSolicitud(50000); // Junta directiva } }
Memento
Permite guardar y restaurar el estado anterior de un objeto sin revelar los detalles de su implementación.
Ejemplo:
using System; using System.Collections.Generic; // Memento: guarda el estado public class Memento { public string Estado { get; } public Memento(string estado) { Estado = estado; } } // Originator: crea y restaura mementos public class EditorTexto { public string Texto { get; set; } public Memento Guardar() { return new Memento(Texto); } public void Restaurar(Memento memento) { Texto = memento.Estado; } } // Caretaker: administra los mementos public class Historial { private Stack<Memento> estados = new Stack<Memento>(); public void Agregar(Memento memento) { estados.Push(memento); } public Memento Deshacer() { return estados.Pop(); } } // Uso del patrón class Program { static void Main() { EditorTexto editor = new EditorTexto(); Historial historial = new Historial(); editor.Texto = "Versión 1"; historial.Agregar(editor.Guardar()); editor.Texto = "Versión 2"; historial.Agregar(editor.Guardar()); editor.Texto = "Versión 3"; Console.WriteLine($"Texto actual: {editor.Texto}"); // Deshacer editor.Restaurar(historial.Deshacer()); Console.WriteLine($"Después de deshacer: {editor.Texto}"); editor.Restaurar(historial.Deshacer()); Console.WriteLine($"Después de deshacer otra vez: {editor.Texto}"); } }
Importancia de los patrones de diseño
- Ayudan a resolver problemas comunes de forma probada y reutilizable, además hacen que el código sea más limpio, flexible y profesional.
- Mejoran la organización del código.
- Facilitan el mantenimiento y la escalabilidad.
- Promueven buenas prácticas de desarrollo.
- Permiten que otros desarrolladores entiendan más rápido el sistema.
Continuaremos con este tema en próximas entregas.
Enlaces:
https://comunidadcityjavamex.blogspot.com/2026/06/java-tip-32-patrones-de-diseno-en-java.htmlhttps://refactoring.guru/design-patterns/csharp

