Principios SOLID en Rust: teoría y práctica

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: Por qué SOLID es importante para los desarrolladores de Rust



SOLID son cinco principios fundamentales de diseño orientado a objetos que ayudan a crear código flexible, mantenible y escalable. A pesar de que Rust no es un lenguaje OOP clásico, estos principios son perfectamente aplicables gracias al sistema de traits, ownership y genéricos. En este artículo analizaremos cada principio SOLID con ejemplos prácticos en Rust.



Aprenderás cómo:

  • Evitar estructuras "todopoderosas" mediante la descomposición
  • Diseñar sistemas extensibles a través de traits
  • Usar polimorfismo eficientemente sin herencia
  • Separar interfaces e inyectar dependencias


1. Single Responsibility Principle (SRP) — Principio de Responsabilidad Única



SRP establece: "Una estructura o módulo debe tener solo una razón para cambiar". En Rust, esto significa que cada tipo debe encargarse de una tarea claramente definida.



Violación de SRP: Estructura "todopoderosa"



struct Report {    data: Vec<String>,}

impl Report { fn generate(&self) -> String { self.data.join(", ") }

fn save_to_file(&self, path: &str) -> std::io::Result<()> { let content = self.generate(); std::fs::write(path, content) }

fn send_email(&self, recipient: &str) { // Lógica de envío de email println!("Enviando reporte {} a {}", self.generate(), recipient); }}


Este código viola SRP: la estructura Report es responsable de generar datos, guardarlos en un archivo y enviar emails. Cambiar la lógica de guardado o envío requeriría modificar el propio reporte.



Implementación correcta con SRP



struct Report {    data: Vec<String>,}

impl Report { fn new(data: Vec<String>) -> Self { Self { data } }

fn generate(&self) -> String { self.data.join(", ") }}

struct FileSaver;

impl FileSaver { fn save(report: &Report, path: &str) -> std::io::Result<()> { std::fs::write(path, report.generate()) }}

struct EmailSender;

impl EmailSender { fn send(report: &Report, recipient: &str) { println!("Enviando reporte {} a {}", report.generate(), recipient); }}


Ahora cada estructura tiene una única responsabilidad: Report — generación, FileSaver — guardado, EmailSender — envío. Esto simplifica las pruebas y los cambios en el código.



2. Open/Closed Principle (OCP) — Principio de Abierto/Cerrado



OCP afirma: "Las clases deben estar abiertas para extensión, pero cerradas para modificación". En Rust, esto se logra mediante traits y genéricos.



Ejemplo: Sistema de procesamiento de pagos



trait PaymentProcessor {    fn process(&self, amount: f64) -> String;}

struct CreditCardProcessor;

impl PaymentProcessor for CreditCardProcessor { fn process(&self, amount: f64) -> String { format!("Procesando {} mediante tarjeta de crédito", amount) }}

struct PayPalProcessor;

impl PaymentProcessor for PayPalProcessor { fn process(&self, amount: f64) -> String { format!("Procesando {} mediante PayPal", amount) }}

struct PaymentHandler<T: PaymentProcessor> { processor: T,}

impl<T: PaymentProcessor> PaymentHandler<T> { fn execute(&self, amount: f64) -> String { self.processor.process(amount) }}


Añadir un nuevo método de pago (por ejemplo, CryptoProcessor) no requiere modificar el código existente — basta con implementar el trait PaymentProcessor.



Blogs

Book Recommendations