Ich habe eine Methode, die eine Datendatei erstellt, nachdem ich mit einer digitalen Karte gesprochen habe:
CreateDataFile(IFileAccess boardFileAccess, IMeasurer boardMeasurer)
Hier boardFileAccess
und boardMeasurer
sind die gleiche Instanz eines Board
Objekts, das sowohl IFileAccess
als auch implementiert IMeasurer
. IMeasurer
wird in diesem Fall für eine einzelne Methode verwendet, bei der ein Pin auf der Platine aktiviert wird, um eine einfache Messung durchzuführen. Die Daten aus dieser Messung werden dann lokal auf der Karte mit gespeichert IFileAccess
. Board
befindet sich in einem separaten Projekt.
Ich bin zu dem Schluss gekommen, dass CreateDataFile
eine Sache darin besteht, eine schnelle Messung durchzuführen und die Daten dann zu speichern. Beides in derselben Methode zu tun, ist für jemanden, der diesen Code verwendet, intuitiver, als eine Messung durchzuführen und in eine Datei zu schreiben als separate Methodenaufrufe.
Mir erscheint es umständlich, dasselbe Objekt zweimal an eine Methode zu übergeben. Ich habe überlegt, eine lokale Schnittstelle zu erstellen IDataFileCreator
, die erweitert wird, IFileAccess
und IMeasurer
dann eine Implementierung zu haben, die eine Board
Instanz enthält, die nur die erforderlichen Board
Methoden aufruft. Wenn man bedenkt, dass immer dasselbe Board-Objekt zum Messen und Schreiben von Dateien verwendet wird, ist es dann eine schlechte Praxis, dasselbe Objekt zweimal an eine Methode zu übergeben? Wenn ja, ist die Verwendung einer lokalen Schnittstelle und Implementierung eine geeignete Lösung?
quelle
Antworten:
Nein, das ist vollkommen in Ordnung. Dies bedeutet lediglich, dass die API in Bezug auf Ihre aktuelle Anwendung überarbeitet wurde .
Dies beweist jedoch nicht, dass es niemals einen Anwendungsfall geben wird, in dem die Datenquelle und der Vermesser unterschiedlich sind. Der Sinn einer API besteht darin, dem Anwendungsprogrammierer Möglichkeiten anzubieten, von denen nicht alle genutzt werden. Sie sollten nicht künstlich einschränken, was API-Benutzer tun können, es sei denn, dies erschwert die API, sodass die Verständlichkeit im Netz abnimmt.
quelle
Vereinbaren Sie mit @ KilianFoth Antwort , dass dies völlig in Ordnung ist.
Wenn Sie möchten, können Sie dennoch eine Methode erstellen, die ein einzelnes Objekt verwendet, das beide Schnittstellen implementiert:
Es gibt keinen allgemeinen Grund, warum Argumente unterschiedliche Objekte sein müssen, und wenn für eine Methode andere Argumente erforderlich wären, wäre dies eine besondere Anforderung, die der Vertrag klarstellen sollte.
quelle
Ich denke, das ist eigentlich dein Problem. Die Methode macht nicht eine Sache. Es werden zwei unterschiedliche Vorgänge ausgeführt, bei denen E / A-Vorgänge für verschiedene Geräte ausgeführt werden. Beide Vorgänge werden auf andere Objekte heruntergeladen:
Dies sind zwei verschiedene E / A-Operationen. Insbesondere verändert der erste Befehl das Dateisystem in keiner Weise.
In der Tat sollten wir beachten, dass es einen impliziten mittleren Schritt gibt:
Ihre API sollte diese in irgendeiner Form separat bereitstellen. Woher wissen Sie, dass ein Anrufer keine Messung durchführen möchte, ohne sie irgendwo zu speichern? Woher wissen Sie, dass sie keine Messung von einer anderen Quelle erhalten möchten? Woher wissen Sie, dass sie es nicht an einem anderen Ort als dem Gerät aufbewahren möchten? Es gibt gute Gründe, die Operationen zu entkoppeln. Bei einem bloßen Minimum, sollte jedes einzelne Stück sein verfügbar zu jedem Anrufer. Ich sollte nicht gezwungen sein, die Messung in eine Datei zu schreiben, wenn mein Anwendungsfall dies nicht erfordert.
Beispielsweise können Sie die Vorgänge wie folgt trennen.
IMeasurer
hat eine Möglichkeit, die Messung abzurufen:Ihr Messtyp könnte einfach etwas Einfaches sein, wie ein
string
oderdecimal
. Ich bestehe nicht darauf, dass Sie eine Schnittstelle oder Klasse dafür benötigen, aber das Beispiel hier wird dadurch allgemeiner.IFileAccess
hat eine Methode zum Speichern von Dateien:Dann brauchen Sie eine Möglichkeit, eine Messung zu serialisieren. Integrieren Sie dies in die Klasse oder Schnittstelle, die eine Messung darstellt, oder verwenden Sie eine Dienstprogrammmethode:
Es ist nicht klar, ob Sie diese Serialisierungsoperation noch getrennt haben.
Diese Art der Trennung verbessert Ihre API. Auf diese Weise kann der Anrufer entscheiden, was er wann benötigt, anstatt Ihre vorgefassten Vorstellungen über die Ausführung der E / A zu erzwingen. Anrufer sollten die Kontrolle haben, um eine gültige Operation auszuführen , unabhängig davon, ob Sie dies für nützlich halten oder nicht.
Sobald Sie für jede Operation eine eigene Implementierung haben, ist Ihre
CreateDataFile
Methode nur noch eine Abkürzung fürBemerkenswert ist, dass Ihre Methode nur einen geringen Mehrwert bietet, wenn Sie dies alles erledigt haben. Die obige Codezeile ist für Ihre Anrufer nicht schwer direkt zu verwenden, und Ihre Methode dient höchstens der Bequemlichkeit. Es sollte und ist etwas optionales . Und das ist die richtige Art und Weise, wie sich die API verhält.
Sobald alle relevanten Teile herausgerechnet wurden und wir festgestellt haben, dass die Methode nur eine Annehmlichkeit ist, müssen wir Ihre Frage umformulieren:
Was wäre der häufigste Anwendungsfall für Ihre Anrufer?
Wenn es darum geht, den typischen Anwendungsfall des Messens und Beschreibens derselben Tafel ein wenig komfortabler zu gestalten, ist es durchaus sinnvoll, sie direkt in der
Board
Klasse verfügbar zu machen :Wenn dies den Komfort nicht verbessert, würde ich mich überhaupt nicht um die Methode kümmern.
Dies ist eine bequeme Methode, die eine weitere Frage aufwirft.
Sollte die
IFileAccess
Schnittstelle über den Messtyp Bescheid wissen und wissen, wie er serialisiert werden kann? In diesem Fall können Sie eine Methode hinzufügen, umIFileAccess
:Jetzt machen die Anrufer einfach Folgendes:
Das ist genauso kurz und wahrscheinlich klarer als Ihre Bequemlichkeitsmethode, wie in der Frage gedacht.
quelle
Der konsumierende Kunde sollte sich nicht mit einem Artikelpaar befassen müssen, wenn ein einzelner Artikel ausreicht. In Ihrem Fall tun sie es fast nicht, bis der Aufruf von
CreateDataFile
.Die mögliche Lösung, die Sie vorschlagen, besteht darin, eine kombinierte abgeleitete Schnittstelle zu erstellen. Dieser Ansatz erfordert jedoch ein einzelnes Objekt, das beide Schnittstellen implementiert, was ziemlich einschränkend ist, wohl eine undichte Abstraktion, da es grundsätzlich an eine bestimmte Implementierung angepasst ist. Überlegen Sie, wie kompliziert es wäre, wenn jemand die beiden Schnittstellen in separaten Objekten implementieren möchte: Er müsste alle Methoden in einer der Schnittstellen als Proxy speichern, um zum anderen Objekt weiterzuleiten. (FWIW, eine andere Option besteht darin, nur die Schnittstellen zusammenzuführen, anstatt dass ein Objekt zwei Schnittstellen über eine abgeleitete Schnittstelle implementieren muss.)
Ein anderer Ansatz, der die Implementierung weniger einschränkt / diktiert, besteht darin, dass er
IFileAccess
mit einerIMeasurer
In-Komposition gepaart wird , sodass einer von ihnen an den anderen gebunden ist und diesen referenziert. (Dies erhöht etwas die Abstraktion von einem von ihnen, da es nun auch die Paarung darstellt.) DannCreateDataFile
könnte beispielsweise nur einer der Verweise genommenIFileAccess
werden und der andere nach Bedarf erhalten. Ihre aktuelle Implementierung als Objekt, das beide Schnittstellen implementiert, dient lediglichreturn this;
als Kompositionsreferenz, hier als Getter fürIMeasurer
inIFileAccess
.Wenn sich das Pairing zu einem bestimmten Zeitpunkt in der Entwicklung als falsch herausstellt, das heißt, dass manchmal ein anderer Measurer mit demselben Dateizugriff verwendet wird, können Sie dasselbe Pairing durchführen, jedoch auf einer höheren Ebene, was bedeutet, dass die zusätzliche Schnittstelle eingeführt wurde keine abgeleitete Schnittstelle sein, sondern eine Schnittstelle mit zwei Gettern, die einen Dateizugriff und einen Vermesser über die Komposition und nicht über die Ableitung koppeln. Der konsumierende Kunde hat dann einen Gegenstand, mit dem er sich befassen muss, solange die Paarung besteht, und bei Bedarf müssen einzelne Objekte bearbeitet werden (um neue Paarungen zu erstellen).
In einem anderen Punkt möchte ich fragen, wem es gehört
CreateDataFile
und wer dieser Dritte ist. Wir haben bereits einen konsumierenden ClientCreateDataFile
, der das besitzende Objekt / die besitzende Klasse vonCreateDataFile
und dasIFileAccess
und aufruftIMeasurer
. Manchmal, wenn wir einen größeren Blick auf den Kontext werfen, können alternative, manchmal bessere Organisationen auftreten. Hier ist es schwierig, da der Kontext unvollständig ist, also nur Denkanstöße.quelle
Einige haben darauf hingewiesen, dass
CreateDataFile
dies zu viel bedeutet. Ich könnte vorschlagen, dass stattdessenBoard
zu viel getan wird, da der Zugriff auf eine Datei vom Rest des Boards ein separates Anliegen zu sein scheint.Wenn wir jedoch davon ausgehen, dass dies kein Fehler ist, besteht das größere Problem darin, dass die Schnittstelle in diesem Fall vom Client definiert werden sollte
CreateDataFile
.Das Prinzip der Schnittstellentrennung besagt, dass der Client nicht mehr von einer Schnittstelle abhängen muss, als er benötigt. Ausgehend von dieser anderen Antwort kann dies wie folgt umschrieben werden: "Eine Schnittstelle wird durch die Bedürfnisse des Kunden definiert."
Jetzt ist es möglich, diese clientspezifische Schnittstelle mit
IFileAccess
und zu erstellen,IMeasurer
wie andere Antworten vermuten lassen. Letztendlich sollte dieser Client jedoch eine maßgeschneiderte Schnittstelle haben.quelle