Principios SOLID en Go: teoría y práctica de la A a la Z
\n\nSOLID es el acrónimo de cinco principios fundamentales de la programación orientada a objetos que ayudan a crear código flexible, mantenible y escalable. Aunque Go no es un lenguaje OOP clásico (no tiene clases ni herencia en el sentido tradicional), estos principios son aplicables y extremadamente útiles en el desarrollo con Go. En este artículo, analizaremos cada principio con ejemplos concretos, mostraremos cómo aplicarlos en Go y explicaremos por qué es importante.
\n\n1. Single Responsibility Principle (SRP) — Principio de responsabilidad única
\n\nEsencia: Cada módulo, clase o función debe tener solo una razón para cambiar. En otras palabras, una entidad debe ser responsable de una sola parte de la funcionalidad.
\n\nEn Go, esto a menudo se viola cuando una estructura (struct) realiza demasiadas tareas: trabaja con la base de datos, envía correos electrónicos, genera informes.
\n\nEjemplo incorrecto (violación de SRP):
\ntype User struct {\n Name string\n Email string\n}\n\nfunc (u *User) Save() error {\n // lógica de guardado en BD\n return nil\n}\n\nfunc (u *User) SendWelcomeEmail() error {\n // lógica de envío de email\n return nil\n}\n\nfunc (u *User) GenerateReport() string {\n // lógica de generación de informe\n return "report"\n}\n\n\nEjemplo correcto (cumplimiento de SRP):
\ntype User struct {\n Name string\n Email string\n}\n\ntype UserRepository struct{}\n\nfunc (r *UserRepository) Save(u *User) error {\n // solo guardado\n return nil\n}\n\ntype EmailService struct{}\n\nfunc (s *EmailService) SendWelcome(u *User) error {\n // solo envío de email\n return nil\n}\n\ntype ReportGenerator struct{}\n\nfunc (g *ReportGenerator) Generate(u *User) string {\n // solo generación de informe\n return "report"\n}\n\n\nConclusión: Cada estructura es responsable de su tarea. Cambiar la lógica de guardado no afectará el envío de correos.
\n\n2. Open-Closed Principle (OCP) — Principio de abierto/cerrado
\n\nEsencia: Las entidades de software (clases, módulos, funciones) deben estar abiertas para la extensión, pero cerradas para la modificación. Es decir, debe poder agregar nueva funcionalidad sin modificar el código existente.
\n\nEn Go, esto se implementa excelentemente a través de interfaces.
\n\nEjemplo incorrecto (violación de OCP):
\ntype PaymentProcessor struct{}\n\nfunc (p *PaymentProcessor) Process(paymentType string, amount float64) {\n if paymentType == "credit_card" {\n // lógica para tarjeta de crédito\n } else if paymentType == "paypal" {\n // lógica para PayPal\n }\n // al agregar un nuevo tipo, habrá que modificar este método\n}\n\n\nEjemplo correcto (cumplimiento de OCP):
\ntype PaymentMethod interface {\n Pay(amount float64) error\n}\n\ntype CreditCard struct{}\n\nfunc (c *CreditCard) Pay(amount float64) error {\n // lógica para tarjeta de crédito\n return nil\n}\n\ntype PayPal struct{}\n\nfunc (p *PayPal) Pay(amount float64) error {\n // lógica para PayPal\n return nil\n}\n\nfunc ProcessPayment(method PaymentMethod, amount float64) error {\n return method.Pay(amount)\n}\n\n\nConclusión: Ahora, para agregar un nuevo método de pago (por ejemplo, ApplePay), basta con crear una nueva estructura con el método Pay(), sin tocar el código existente.
3. Liskov Substitution Principle (LSP) — Principio de sustitución de Barbara Liskov
\n\nEsencia: Los objetos en un programa deben ser reemplazables por instancias de sus subtipos sin alterar la corrección del programa. En Go, esto significa que si una función acepta una interfaz, cualquier implementación de esa interfaz debe funcionar correctamente sin violar las expectativas.
\n\nEjemplo incorrecto (violación de LSP):
\ntype Bird interface {\n Fly() error\n}\n\nt