Introducción: ¿Por qué es importante SOLID en Go?
SOLID son cinco principios fundamentales de la programación orientada a objetos que ayudan a crear código flexible, mantenible y extensible. Aunque Go no es un lenguaje OOP clásico (no tiene clases ni herencia), estos principios se aplican perfectamente gracias a las interfaces, la composición y el tipado estricto. En este artículo analizaremos cada principio SOLID con ejemplos en Go para que puedas escribir código de mayor calidad y escalable.
1. Single Responsibility Principle (SRP) — Principio de responsabilidad única
SRP establece: "Un módulo debe tener una sola razón para cambiar". En Go, esto significa que una función, estructura o paquete debe encargarse de una tarea claramente definida.
Ejemplo incorrecto (violación de SRP):
type Report struct { Title string Body string}
func (r *Report) Generate() string { return fmt.Sprintf("<h1>%s</h1><p>%s</p>", r.Title, r.Body)}
func (r *Report) SaveToFile(filename string) error { return os.WriteFile(filename, []byte(r.Generate()), 0644)}
func (r *Report) SendEmail(addr string) error { // lógica de envío de email return nil}Aquí la estructura Report se encarga de la generación, el guardado y el envío — tres razones diferentes para cambiar.
Ejemplo correcto (cumplimiento de SRP):
type Report struct { Title string Body string}
func (r *Report) Generate() string { return fmt.Sprintf("<h1>%s</h1><p>%s</p>", r.Title, r.Body)}
type ReportSaver struct{}
func (rs *ReportSaver) SaveToFile(report *Report, filename string) error { return os.WriteFile(filename, []byte(report.Generate()), 0644)}
type EmailSender struct{}
func (es *EmailSender) Send(report *Report, addr string) error { // lógica de envío de email return nil}Ahora cada entidad tiene una única responsabilidad: generación, guardado y envío.
2. Open/Closed Principle (OCP) — Principio de abierto/cerrado
OCP afirma: "Las entidades de software deben estar abiertas para la extensión, pero cerradas para la modificación". En Go esto se logra mediante interfaces y composición.
Ejemplo incorrecto (violación de OCP):
type PaymentProcessor struct{}
func (p *PaymentProcessor) Process(paymentType string, amount float64) { if paymentType == "credit_card" { // procesamiento de tarjeta de crédito } else if paymentType == "paypal" { // procesamiento de PayPal } else if paymentType == "crypto" { // procesamiento de criptomoneda }}Al agregar un nuevo tipo de pago, habría que modificar el método Process.
Ejemplo correcto (cumplimiento de OCP):
type PaymentMethod interface { Pay(amount float64) error}
type CreditCard struct{}func (c *CreditCard) Pay(amount float64) error { // lógica de pago con tarjeta return nil}
type PayPal struct{}func (p *PayPal) Pay(amount float64) error { // lógica de pago con PayPal return nil}
type PaymentProcessor struct{}
func (pp *PaymentProcessor) Process(method PaymentMethod, amount float64) error { return method.Pay(amount)}Ahora para agregar un nuevo método de pago basta con implementar la interfaz PaymentMethod, sin tocar el código existente.
3. Liskov Substitution Principle (LSP) — Principio de sustitución de Barbara Liskov
LSP exige: "Los objetos de un programa deben ser reemplazables por instancias de sus subtipos sin alterar la corrección del programa". En Go esto se refiere a la implementación de interfaces.
Ejemplo incorrecto (violación de LSP):
type Bird interface { Fly() error}
type Penguin struct{}func (p *Penguin) Fly() error { return errors.New("los pingüinos no vuelan")}
func LetBirdFly(b Bird) {