SOLID-Prinzipien in Rust: Theorie und Praxis

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einleitung: Warum SOLID für Rust-Entwickler wichtig ist



SOLID sind fünf fundamentale Prinzipien des objektorientierten Designs, die helfen, flexiblen, wartbaren und skalierbaren Code zu erstellen. Obwohl Rust keine klassische OOP-Sprache ist, lassen sich diese Prinzipien dank des Systems der Traits, Ownership und Generics hervorragend anwenden. In diesem Artikel werden wir jedes SOLID-Prinzip anhand praktischer Beispiele in Rust durchgehen.



Sie erfahren, wie man:

  • "Gottgleiche" Strukturen durch Dekomposition vermeidet
  • Erweiterbare Systeme durch Traits entwirft
  • Polymorphismus ohne Vererbung effektiv nutzt
  • Schnittstellen trennt und Abhängigkeiten injiziert


1. Single Responsibility Principle (SRP) — Prinzip der einzigen Verantwortung



SRP besagt: "Eine Struktur oder ein Modul sollte nur einen einzigen Grund für eine Änderung haben". In Rust bedeutet dies, dass jeder Typ für eine klar definierte Aufgabe verantwortlich sein sollte.



Verletzung des SRP: "Gottgleiche" Struktur



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) { // Logik zum Senden einer E-Mail println!("Sende Bericht {} an {}", self.generate(), recipient); }}


Dieser Code verletzt SRP: Die Struktur Report ist verantwortlich für die Datengenerierung, das Speichern in eine Datei und das Senden einer E-Mail. Eine Änderung der Speicher- oder Sende-Logik würde eine Änderung des Berichts selbst erfordern.



Korrekte Implementierung mit 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!("Sende Bericht {} an {}", report.generate(), recipient); }}


Jetzt hat jede Struktur eine einzige Verantwortung: Report — Generierung, FileSaver — Speicherung, EmailSender — Versand. Dies vereinfacht das Testen und Ändern des Codes.



2. Open/Closed Principle (OCP) — Prinzip der Offenheit/Geschlossenheit



OCP besagt: "Klassen sollten für Erweiterungen offen, aber für Modifikationen geschlossen sein". In Rust wird dies durch Traits und Generics erreicht.



Beispiel: Zahlungsabwicklungssystem



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

struct CreditCardProcessor;

impl PaymentProcessor for CreditCardProcessor { fn process(&self, amount: f64) -> String { format!("Verarbeite {} über Kreditkarte", amount) }}

struct PayPalProcessor;

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

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

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


Das Hinzufügen einer neuen Zahlungsmethode (z. B. CryptoProcessor) erfordert keine Änderung des vorhandenen Codes — es reicht aus, das PaymentProcessor-Trait zu implementieren.



Blogs

Buchempfehlungen