Introducción a los patrones de diseño
Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy. Lección 85 de 107.
Los patrones de diseño son soluciones reutilizables para problemas comunes que surgen en el diseño de software. No son código que copies directamente, sino plantillas que guían la forma en que estructuras tus tipos y sus interacciones para resolver desafíos específicos.
Los patrones de diseño normalmente se agrupan en tres categorías:
| Categoría | Propósito | Ejemplos |
|---|---|---|
| Creacionales | Mecanismos de creación de objetos | Singleton, Factory, Builder |
| Estructurales | Composición de tipos en estructuras más grandes | Adapter, Decorator, Composite |
| De comportamiento | Comunicación entre objetos | Observer, Strategy, Command |
En Go, los patrones de diseño son diferentes a los de los lenguajes OOP tradicionales.
Sin clases ni herencia, Go implementa patrones mediante interfaces, la inclusión de estructuras y la composición. Por ejemplo, donde Java podría usar una clase abstracta, Go usa una interfaz. Donde C++ podría usar herencia, Go usa la inclusión.
Aprender patrones te ayuda a reconocer problemas comunes y a comunicar soluciones eficazmente con otros desarrolladores. Cuando alguien menciona «usa aquí el patrón Strategy», todo el equipo entiende el enfoque sin explicaciones extensas.
En las próximas lecciones, implementarás varios patrones fundamentales en Go, empezando por patrones creacionales como Singleton y Factory, y después pasando a patrones de comportamiento como Observer y Strategy.
Desafío
Fácil¡Construyamos un sistema de notificaciones sencillo que demuestre cómo los patrones de diseño ayudan a organizar el código! Crearás una estructura que podría ampliarse fácilmente con patrones como Observer o Strategy en lecciones futuras, pero por ahora, concéntrate en configurar interfaces y tipos limpios que sigan buenos principios de OOP.
Organizarás tu código en tres archivos:
notifier.go: Define la interfaz principal y los tipos de tu sistema de notificaciones.Crea una interfaz
Notifiercon un único métodoSend(message string) stringque devuelva un mensaje de confirmación.Implementa dos tipos concretos de notificador:
EmailNotifiercon un campoAddress. Su métodoSenddevuelveEmail to [address]: [message]SMSNotifiercon un campoPhoneNumber. Su métodoSenddevuelveSMS to [phone]: [message]
dispatcher.go: Crea un despachador que funcione con cualquier notificador mediante la interfaz.Construye una estructura
NotificationDispatcherque contenga un slice de interfacesNotifier. Añade estos métodos:AddNotifier(n Notifier)para registrar un notificadorBroadcast(message string) []stringque envíe el mensaje a través de todos los notificadores registrados y devuelva un slice con todas las cadenas de confirmación
Crea también un constructor
NewNotificationDispatcher()que devuelva un despachador inicializado.main.go: Configura los notificadores y difunde los mensajes.Lee la cantidad de notificadores que se deben crear. Para cada notificador, lee su tipo (
emailosms) y su destino (dirección o número de teléfono). Añade cada uno a un despachador.Después, lee un mensaje para difundir. Envíalo a través de todos los notificadores e imprime cada confirmación en su propia línea.
Se proporcionarán las siguientes entradas:
- Línea 1: Número de notificadores
- Para cada notificador: tipo (
emailosms), después el destino - Línea final: Mensaje que se difundirá
Por ejemplo, dada la siguiente entrada:
3
email
alice@example.com
sms
555-1234
email
bob@example.com
Server maintenance at midnightTu salida debería ser:
Email to alice@example.com: Server maintenance at midnight
SMS to 555-1234: Server maintenance at midnight
Email to bob@example.com: Server maintenance at midnightY dada la siguiente entrada:
2
sms
555-9999
sms
555-0000
Meeting canceledTu salida debería ser:
SMS to 555-9999: Meeting canceled
SMS to 555-0000: Meeting canceledEsta estructura demuestra cómo las interfaces permiten diseños flexibles y ampliables. El despachador no conoce los tipos específicos de notificadores. Solo sabe que pueden ejecutar Send. ¡Esta es la base sobre la que se construyen patrones como Observer y Strategy!
Pruébalo tú mismo
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Lee el número de notificadores
countStr, _ := reader.ReadString('\n')
count, _ := strconv.Atoi(strings.TrimSpace(countStr))
// Crea un nuevo dispatcher
dispatcher := NewNotificationDispatcher()
// Lee cada notificador y añádelo al dispatcher
for i := 0; i < count; i++ {
notifierType, _ := reader.ReadString('\n')
notifierType = strings.TrimSpace(notifierType)
destination, _ := reader.ReadString('\n')
destination = strings.TrimSpace(destination)
// TODO: Crea el notificador apropiado según el tipo
// y añádelo al dispatcher
// Hint: Comprueba si notifierType es "email" o "sms"
}
// Lee el mensaje a transmitir
message, _ := reader.ReadString('\n')
message = strings.TrimSpace(message)
// TODO: Transmite el mensaje e imprime cada confirmación
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de POO en Go
Archivos externosEspacio de trabajo y módulos de GoPaquetes e importacionesNombres exportados vs. no exportadosIntroducción a la POO en GoStructs como clasesDefinición de métodos en structsReceptores de puntero vs. de valorInicialización de structsFunciones constructorasResumen - Calculadora simple4Interfaces
Introducción a las interfacesImplementación implícitaLa interfaz como contratoInterfaz vacía (any)Type AssertionType SwitchComposición de interfacesInterfaces Stringer y ErrorResumen: Calculadora de figuras7Encapsulamiento
Campos exportados vs. no exportadosEncapsulamiento a nivel de paqueteMétodos Getter y SetterOcultamiento de información en GoResumen - Registros de estudiantes10Genéricos (Go 1.18+)
Introducción a los GenéricosParámetros de tipoRestricciones de tipoStructs genéricosSolución alternativa para métodos genéricosResumen - Colección genérica13Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón Abstract FactoryPatrón ObserverPatrón Strategy2Inmersión profunda en Tipos y Structs
Tipos básicos y compuestosDefiniciones de tipos personalizadosEtiquetas de StructsStructs anónimosStructs anidadosValores cero y por defectoRepaso - Libreta de contactos5Composición sobre herencia
Por qué Go no tiene herenciaConceptos básicos de incrustación de structsPromoción de métodosIncrustación de múltiples structsIncrustación vs AgregaciónShadowing de métodos incrustadosResumen - Jerarquía de empleados8Manejo de Errores y OOP
La interfaz errorTipos de errores personalizadosEnvoltura de errores (fmt.Errorf)Errores centinelaerrors.Is() y errors.As()Panic, Defer y RecoverResumen - Analizador de archivos3Punteros y Memoria
Fundamentos de punteros en GoPunteros a StructsPaso por valor vs referenciaLa función new()Garbage Collection en GoResumen - Constructor de Linked List6Polimorfismo en Go
Polimorfismo mediante interfacesDuck Typing en GoReglas de satisfacción de interfacesColecciones polimórficasInyección de dependenciasResumen - Procesador de pagos9Concurrencia y POO
Fundamentos de GoroutinesChannels y comunicaciónChannels con buffer vs. sin bufferSentencia Selectsync.Mutex y sync.RWMutexsync.WaitGroupDiseño de estructuras Thread-SafeResumen - Worker PoolPractica por tu cuenta: Compilador de Go online