Princípios SOLID em Rust: teoria e prática do A ao Z

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: SOLID e Rust — uma combinação ideal

\n

Os princípios SOLID são regras fundamentais de design orientado a objetos que ajudam a criar código flexível, sustentável e escalável. Embora Rust não seja uma linguagem puramente orientada a objetos (ela usa traits e structs em vez de classes), os princípios SOLID são totalmente aplicáveis a ela. Neste artigo, analisaremos cada um dos cinco princípios com exemplos de código em Rust, mostrando como eles ajudam a construir sistemas robustos.

\n

Ao longo do artigo, você verá como Rust, com seu sistema de ownership, traits e genéricos, torna a implementação do SOLID natural e poderosa. Começaremos com o Single Responsibility e terminaremos com o Dependency Inversion, fornecendo exemplos prontos para uso.

\n\n

1. Single Responsibility Principle (SRP) — princípio da responsabilidade única

\n

Essência: Cada módulo, classe ou função deve ter apenas um motivo para mudar. Em Rust, isso significa que uma struct deve ser responsável por uma única tarefa.

\n\n

Exemplo ruim (violação do SRP)

\n
struct Report {\n    title: String,\n    content: String,\n}\n\nimpl Report {\n    fn generate(&self) -> String {\n        format!("# {}\\n\\n{}", self.title, self.content)\n    }\n\n    fn save_to_file(&self, path: &str) -> std::io::Result<()> {\n        std::fs::write(path, self.generate())?;\n        Ok(())\n    }\n\n    fn send_email(&self, recipient: &str) {\n        println!("Sending email to {} with report", recipient);\n    }\n}
\n

Aqui, a struct Report é responsável por gerar, salvar e enviar — três razões diferentes para mudar. Isso viola o SRP.

\n\n

Exemplo bom (cumprimento do SRP)

\n
struct Report {\n    title: String,\n    content: String,\n}\n\nimpl Report {\n    fn generate(&self) -> String {\n        format!("# {}\\n\\n{}", self.title, self.content)\n    }\n}\n\nstruct ReportSaver;\n\nimpl ReportSaver {\n    fn save(&self, report: &Report, path: &str) -> std::io::Result<()> {\n        std::fs::write(path, report.generate())?;\n        Ok(())\n    }\n}\n\nstruct EmailSender;\n\nimpl EmailSender {\n    fn send(&self, recipient: &str, message: &str) {\n        println!("Sending email to {}: {}", recipient, message);\n    }\n}
\n

Agora cada struct é responsável por sua tarefa: Report pelos dados e geração, ReportSaver por salvar, EmailSender por enviar. Isso simplifica os testes e a manutenção.

\n\n

2. Open-Closed Principle (OCP) — princípio aberto/fechado

\n

Essência: Entidades de software devem estar abertas para extensão, mas fechadas para modificação. Em Rust, isso é alcançado através de traits e genéricos.

\n\n

Exemplo: sistema de filtragem

\n
trait Filter<T> {\n    fn apply(&self, items: &[T]) -> Vec<T>;\n}\n\nstruct EvenFilter;\n\nimpl Filter<i32> for EvenFilter {\n    fn apply(&self, items: &[i32]) -> Vec<i32> {\n        items.iter().filter(|&x| x % 2 == 0).copied().collect()\n    }\n}\n\nstruct GreaterThanFilter {\n    threshold: i32,\n}\n\nimpl Filter<i32> for GreaterThanFilter {\n    fn apply(&self, items: &[i32]) -> Vec<i32> {\n        items.iter().filter(|&&x| x > self.threshold).copied().collect()\n    }\n}\n\nfn process<T: Clone, F: Filter<T>>(items: &[T], filter: F) -> Vec<T> {\n    filter.apply(items)\n}\n\nfn main() {\n    let numbers = vec![1, 2, 3, 4, 5, 6];\n    let even = process(&numbers, EvenFilter);\n    let greater = process(&numbers, GreaterThanFilter { threshold: 3 });\n    println!("Even: {:?}, Greater: {:?}", even, greater);\n}
\n

Podemos adicionar novos filtros sem modificar o código existente de process. Esta é a implementação do OCP através do polimorfismo de traits.

\n\n

3. Liskov Substitution Principle (LSP) — princípio da substituição de Barbara Liskov

\n

Essência:

Blogs

Book Recommendations