Introdução: SOLID e Rust — uma combinação ideal
\nOs 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.
\nAo 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\n1. Single Responsibility Principle (SRP) — princípio da responsabilidade única
\nEssê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\nExemplo ruim (violação do SRP)
\nstruct 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}\nAqui, a struct Report é responsável por gerar, salvar e enviar — três razões diferentes para mudar. Isso viola o SRP.
Exemplo bom (cumprimento do SRP)
\nstruct 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}\nAgora 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.
2. Open-Closed Principle (OCP) — princípio aberto/fechado
\nEssê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\nExemplo: sistema de filtragem
\ntrait 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}\nPodemos adicionar novos filtros sem modificar o código existente de process. Esta é a implementação do OCP através do polimorfismo de traits.
3. Liskov Substitution Principle (LSP) — princípio da substituição de Barbara Liskov
\nEssência:
Blogs
Book Recommendations
Leia também
C# para aprendizado de máquina: possibilidades, limitações e exemplos de código
PHP em 2025: o que há de novo na prática — visão geral das novidades
Algoritmos e estruturas de dados em Go: guia para iniciantes
Princípios SOLID em C++: da teoria à prática para código limpo
Código limpo em JavaScript: princípios e exemplos para Node.js