Vor gut einem Vierteljahrhundert, als ich C ++ lernte, wurde mir beigebracht, dass Interfaces verzeihen sollten und dass die Reihenfolge, in der Methoden aufgerufen wurden, so weit wie möglich unberücksichtigt bleiben sollte, da der Verbraucher möglicherweise keinen Zugriff auf die Quelle oder Dokumentation anstelle von hat Dies.
Wann immer ich Junior-Programmierer betreut habe und Senior-Entwickler mich belauscht haben, haben sie mit Erstaunen reagiert, was mich gefragt hat, ob dies wirklich eine Sache war oder ob sie gerade aus der Mode gekommen ist.
So klar wie Schlamm?
Betrachten Sie eine Schnittstelle mit diesen Methoden (zum Erstellen von Datendateien):
OpenFile
SetHeaderString
WriteDataLine
SetTrailerString
CloseFile
Jetzt können Sie diese natürlich der Reihe nach durchgehen, aber sagen, dass Sie sich nicht um den Dateinamen (think a.out
) gekümmert haben oder welche Header- und Trailer-Zeichenfolge enthalten waren, Sie können sie einfach aufrufen AddDataLine
.
Ein weniger extremes Beispiel könnte das Weglassen von Headern und Trailern sein.
Eine weitere Möglichkeit besteht darin, die Header- und Trailer-Zeichenfolgen festzulegen, bevor die Datei geöffnet wurde.
Handelt es sich um ein Prinzip des Interface-Designs, das erkannt wurde, oder nur um das POLA-Prinzip, bevor es einen Namen erhielt?
Hinweis: Lassen Sie sich in den Details dieser Benutzeroberfläche nicht stören. Dies ist nur ein Beispiel für diese Frage.
quelle
Antworten:
Eine Möglichkeit, sich an das Prinzip des geringsten Erstaunens zu halten, besteht darin, andere Prinzipien wie ISP und SRP oder sogar DRY zu berücksichtigen .
In dem konkreten Beispiel, das Sie angegeben haben, scheint der Vorschlag zu sein, dass es eine gewisse Abhängigkeit der Reihenfolge für die Bearbeitung der Datei gibt. Ihre API kontrolliert jedoch sowohl den Dateizugriff als auch das Datenformat, was ein bisschen nach einer Verletzung von SRP riecht.
Bearbeiten / Aktualisieren: Es wird auch vorgeschlagen, dass die API den Benutzer auffordert, DRY zu verletzen, da er bei jeder Verwendung der API dieselben Schritte wiederholen muss .
Stellen Sie sich eine alternative API vor, bei der die E / A-Vorgänge von den Datenvorgängen getrennt sind. und wo die API selbst die Bestellung "besitzt":
ContentBuilder
FileWriter
Mit der obigen Trennung muss das
ContentBuilder
Programm nichts weiter tun, als die Zeilen / Header / Trailer zu speichern (möglicherweise auch eineContentBuilder.Serialize()
Methode, die die Reihenfolge kennt). Wenn Sie anderen SOLID-Prinzipien folgen, spielt es keine Rolle mehr, ob Sie den Header oder den Trailer vor oder nach dem Hinzufügen von Zeilen setzen, da nichts in dieContentBuilder
Datei geschrieben wird, bis es an übergeben wirdFileWriter.Write
.Es hat auch den zusätzlichen Vorteil, dass es etwas flexibler ist. Beispielsweise kann es nützlich sein, den Inhalt in einen Diagnose-Logger zu schreiben oder ihn über ein Netzwerk weiterzuleiten, anstatt ihn direkt in eine Datei zu schreiben.
Beim Entwerfen einer API sollten Sie auch die Fehlerberichterstattung berücksichtigen, unabhängig davon, ob es sich um einen Status, einen Rückgabewert, eine Ausnahme, einen Rückruf oder etwas anderes handelt. Der Benutzer der API wird wahrscheinlich erwarten können, dass er Verstöße gegen seine Verträge oder sogar andere Fehler, die er nicht kontrollieren kann, wie z. B. Datei-E / A-Fehler, programmgesteuert erkennen kann.
quelle
SetHeader
oder von Bedeutung istAddLine
. Um diese Auftragsabhängigkeit zu beseitigen, handelt es sich weder um ISP noch um SRP, sondern lediglich um POLA.FileWriter
In diesem Fall kann der Wert aus dem letztenContentBuilder
Schritt derWrite
Methode erforderlich sein , um sicherzustellen, dass der gesamte Eingabeinhalt vollständig ist, sodass erInvalidContentException
nicht erforderlich ist .ContentBuilder
und zusammenzufassenFileWriter.Write
. Die Ausnahme wäre notwendig, falls irgendetwas mit dem Inhalt nicht übereinstimmt (z. B. ein fehlender Header). Eine Rückkehr könnte auch funktionieren, aber ich bin kein Fan davon, Ausnahmen in Rückkehrcodes umzuwandeln.Hier geht es nicht nur um POLA, sondern auch darum, einen ungültigen Status als mögliche Fehlerquelle zu verhindern.
Lassen Sie uns sehen, wie wir Ihrem Beispiel einige Einschränkungen auferlegen können, ohne eine konkrete Implementierung bereitzustellen:
Erster Schritt: Lassen Sie keinen Aufruf zu, bevor eine Datei geöffnet wurde.
Nun sollte klar sein, dass
CreateDataFileInterface.OpenFile
zum Abrufen einerDataFileInterface
Instanz aufgerufen werden muss , in die die eigentlichen Daten geschrieben werden können.Zweiter Schritt: Stellen Sie sicher, dass die Header und Trailer immer gesetzt sind.
Jetzt müssen Sie alle erforderlichen Parameter angeben, um einen
DataFileInterface
Dateinamen, einen Header und einen Trailer zu erhalten. Wenn der Trailer-String nicht verfügbar ist, bis alle Zeilen geschrieben sind, können Sie diesen Parameter auch in verschiebenClose()
(möglicherweise in umbenennenWriteTrailerAndClose()
), sodass die Datei zumindest nicht ohne Trailer-String fertiggestellt werden kann.Um auf den Kommentar zu antworten:
Wahr. Ich wollte mich nicht mehr auf das Beispiel konzentrieren als nötig, um meinen Standpunkt darzulegen, aber es ist eine gute Frage. In diesem Fall denke ich, ich würde es nennen
Finalize(trailer)
und argumentieren, dass es nicht zu viel tut. Das Schreiben des Trailers und das Schließen sind lediglich Implementierungsdetails. Aber wenn Sie anderer Meinung sind oder eine ähnliche Situation haben, in der es anders ist, ist hier eine mögliche Lösung:Ich würde es für dieses Beispiel eigentlich nicht tun, aber es zeigt, wie man die Technik konsequent durchführt.
Übrigens habe ich angenommen, dass die Methoden tatsächlich in dieser Reihenfolge aufgerufen werden müssen, um beispielsweise viele Zeilen nacheinander zu schreiben. Wenn dies nicht erforderlich ist, würde ich immer einen Baumeister bevorzugen, wie von Ben Cottrel vorgeschlagen .
quelle
WriteTrailerAndClose()
) einer Verletzung der SRP gleichkommt. (Dies ist etwas, mit dem ich mehrmals zu kämpfen hatte, aber Ihr Vorschlag scheint ein mögliches Beispiel zu sein.) Wie würden Sie antworten?OpenFile
Überladung bereitstellen , für die keine erforderlich ist.