Zum Inhalt springen

Zustandsmuster

Im laufenden Beispiel dieses Abschnitts konnte ein Blogbeitrag einen von drei Zuständen haben: Draft, PendingReview und Published. Im Beispiel wurde jeder Zustand als struct dargestellt, und Zustandsübergänge wurden als Trait-Methoden dargestellt, die Trait-Objekte zurückgeben.

Eine alternative Implementierung könnte die Zustände als enum darstellen, wie folgt:

enum BlogState {
Draft,
PendingReview,
Published
}
impl Post {
fn request_review(&mut self) {
use BlogState::*;
self.state = match self.state {
Draft => PendingReview,
PendingReview => PendingReview,
Published => Published
}
}
}

Welche der folgenden Gründe sprechen dafür, die struct/trait-Darstellung der enum-Darstellung vorzuziehen?

Der struct/trait-Ansatz ist insofern erweiterbar, als ein API-Client potenziell einen neuen Zustand (wie Retracted) erstellen könnte, ohne die Kern-API-Funktionalität zu ändern. Beim Hinzufügen dieses Zustands müssen die Methoden für andere Zustände nicht geändert werden. Wohingegen bei enums ein Client keinen neuen Zweig zum enum hinzufügen kann. Darüber hinaus müssen alle match-Ausdrücke aktualisiert werden, wenn ein Zustand hinzugefügt wird.

Ein match ist wahrscheinlich nicht langsamer als dynamische Versendung. Ein match ist eine einfache Verzweigung basierend auf dem Tag eines enum, während dynamische Versendung eine Indirektionsebene über die virtuelle Tabelle eines Trait-Objekts mit nicht-inlined Funktionsaufrufen erfordert.

Ein API-Client kann im struct/trait-Ansatz keine neue Methode für bestehende Zustände hinzufügen; er kann nur neue Zustände hinzufügen. Die Methoden sind durch die Trait-Definition des API-Autors festgelegt. Beachten Sie, dass Sie eine neue Methode hinzufügen könnten, die nur auf bestehenden Methoden über Erweiterungs-Traits aufbaut, wie zum Beispiel:

trait StateExt {
fn request_review_twice(self: Box<Self>) -> Box<dyn State>;
}
impl<S: State> StateExt for S {
fn request_review_twice(self: Box<Self>) -> Box<dyn State> {
self.request_review().request_review()
}
}

Diese Erweiterungen können jedoch nicht auf die privaten Daten der Zustände zugreifen.