Introduction : SOLID et Rust — une combinaison idéale
\nLes principes SOLID sont des règles fondamentales de la conception orientée objet qui aident à créer un code flexible, maintenable et évolutif. Bien que Rust ne soit pas un langage purement orienté objet (il utilise des traits et des structures au lieu de classes), les principes SOLID lui sont pleinement applicables. Dans cet article, nous analyserons chacun des cinq principes avec des exemples de code en Rust, en montrant comment ils aident à construire des systèmes fiables.
\nTout au long de l'article, vous verrez comment Rust, avec son système de possession, ses traits et ses génériques, rend l'implémentation de SOLID naturelle et puissante. Nous commencerons par le Single Responsibility et terminerons par le Dependency Inversion, en fournissant des exemples prêts à l'emploi.
\n\n1. Single Responsibility Principle (SRP) — principe de responsabilité unique
\nEssence : Chaque module, classe ou fonction ne doit avoir qu'une seule raison de changer. En Rust, cela signifie qu'une structure doit être responsable d'une seule tâche.
\n\nMauvais exemple (violation du 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}\nIci, la structure Report est responsable de la génération, de la sauvegarde et de l'envoi — trois raisons différentes de changer. Cela viole le SRP.
Bon exemple (respect du 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}\nMaintenant, chaque structure est responsable de sa propre tâche : Report pour les données et la génération, ReportSaver pour la sauvegarde, EmailSender pour l'envoi. Cela simplifie les tests et la maintenance.
2. Open-Closed Principle (OCP) — principe d'ouverture/fermeture
\nEssence : Les entités logicielles doivent être ouvertes à l'extension, mais fermées à la modification. En Rust, cela est réalisé via les traits et les génériques.
\n\nExemple : système de filtrage
\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}\nNous pouvons ajouter de nouveaux filtres sans modifier le code existant de process. C'est une implémentation de l'OCP via le polymorphisme de traits.
3. Liskov Substitution Principle (LSP) — principe de substitution de Barbara Liskov
\nEssence :
Blogs
Book Recommendations
Lire aussi
Lire un fichier ligne par ligne en Go : meilleures pratiques 2025
Tests E2E en Rust : Guide complet pour des projets réels
Microservices en JavaScript : architecture et pratique avancée
Déploiement d'une application Kotlin sur VPS : guide pas à pas de A à Z
Ambiguïté de la taille du contenu UIScrollView : comment la corriger en 10 minutes