Princípios SOLID em Rust: teoria e prática

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: Por que SOLID é importante para desenvolvedores Rust



SOLID são cinco princípios fundamentais de design orientado a objetos que ajudam a criar código flexível, sustentável e escalável. Apesar de Rust não ser uma linguagem OOP clássica, esses princípios são perfeitamente aplicáveis graças ao sistema de traits, ownership e generics. Neste artigo, vamos analisar cada princípio SOLID com exemplos práticos em Rust.



Você aprenderá como:

  • Evitar estruturas "divinas" através da decomposição
  • Projetar sistemas extensíveis usando traits
  • Usar polimorfismo eficientemente sem herança
  • Separar interfaces e injetar dependências


1. Single Responsibility Principle (SRP) — Princípio da Responsabilidade Única



O SRP afirma: "Uma estrutura ou módulo deve ter apenas um motivo para mudar". Em Rust, isso significa que cada tipo deve ser responsável por uma tarefa claramente definida.



Violação do SRP: Estrutura "Divina"



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 envio de email println!(\"Enviando relatório {} para {}\", self.generate(), recipient); }}


Este código viola o SRP: a estrutura Report é responsável pela geração de dados, salvamento em arquivo e envio de email. Alterar a lógica de salvamento ou envio exigiria modificar o próprio relatório.



Implementação correta com 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 relatório {} para {}\", report.generate(), recipient); }}


Agora cada estrutura tem uma única responsabilidade: Report — geração, FileSaver — salvamento, EmailSender — envio. Isso simplifica os testes e a modificação do código.



2. Open/Closed Principle (OCP) — Princípio Aberto/Fechado



O OCP afirma: "As classes devem estar abertas para extensão, mas fechadas para modificação". Em Rust, isso é alcançado através de traits e generics.



Exemplo: Sistema de processamento de pagamentos



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

struct CreditCardProcessor;

impl PaymentProcessor for CreditCardProcessor { fn process(&self, amount: f64) -> String { format!(\"Processando {} via cartão de crédito\", amount) }}

struct PayPalProcessor;

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

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

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


Adicionar um novo método de pagamento (por exemplo, CryptoProcessor) não requer alteração do código existente — basta implementar o trait PaymentProcessor.



Blogs

Book Recommendations