Methodenverkettung ist die Praxis von Objektmethoden, die das Objekt selbst zurückgeben, damit das Ergebnis für eine andere Methode aufgerufen wird. So was:
participant.addSchedule(events[1]).addSchedule(events[2]).setStatus('attending').save()
Dies scheint eine gute Praxis zu sein, da es lesbaren Code oder eine "fließende Schnittstelle" erzeugt. Für mich scheint es jedoch die Objektaufrufnotation zu brechen, die durch die Objektorientierung selbst impliziert wird - der resultierende Code stellt keine Aktionen dar, die auf das Ergebnis einer vorherigen Methode zurückzuführen sind. So wird im Allgemeinen erwartet, dass objektorientierter Code funktioniert:
participant.getSchedule('monday').saveTo('monnday.file')
Dieser Unterschied schafft es, zwei verschiedene Bedeutungen für die Punktnotation "Aufrufen des resultierenden Objekts" zu erzeugen: Im Zusammenhang mit der Verkettung würde das obige Beispiel als Speichern des Teilnehmerobjekts lauten , obwohl das Beispiel tatsächlich den Zeitplan speichern soll Objekt von getSchedule empfangen.
Ich verstehe, dass der Unterschied hier darin besteht, ob von der aufgerufenen Methode erwartet werden sollte, dass sie etwas zurückgibt oder nicht (in diesem Fall würde sie das aufgerufene Objekt selbst zur Verkettung zurückgeben). Diese beiden Fälle unterscheiden sich jedoch nicht von der Notation selbst, sondern nur von der Semantik der aufgerufenen Methoden. Wenn die Methodenverkettung nicht verwendet wird, kann ich immer wissen, dass ein Methodenaufruf mit etwas arbeitet, das mit dem Ergebnis des vorherigen Aufrufs zusammenhängt. Bei der Verkettung bricht diese Annahme, und ich muss die gesamte Kette semantisch verarbeiten, um zu verstehen, was das eigentliche Objekt ist genannt ist wirklich. Beispielsweise:
participant.attend(event).setNotifications('silent').getSocialStream('twitter').postStatus('Joining '+event.name).follow(event.getSocialId('twitter'))
Dort beziehen sich die letzten beiden Methodenaufrufe auf das Ergebnis von getSocialStream, während sich die vorherigen auf den Teilnehmer beziehen. Vielleicht ist es eine schlechte Praxis, tatsächlich Ketten zu schreiben, in denen sich der Kontext ändert (oder?), Aber selbst dann müssen Sie ständig überprüfen, ob Punktketten, die ähnlich aussehen, tatsächlich im selben Kontext bleiben oder nur am Ergebnis arbeiten .
Mir scheint, dass die oberflächliche Verkettung von Methoden zwar lesbaren Code erzeugt, die Überladung der Bedeutung der Punktnotation jedoch nur zu mehr Verwirrung führt. Da ich mich nicht als Programmierguru betrachte, gehe ich davon aus, dass der Fehler bei mir liegt. Also: Was vermisse ich? Verstehe ich die Verkettung von Methoden irgendwie falsch? Gibt es Fälle, in denen die Verkettung von Methoden besonders gut oder besonders schlecht ist?
Nebenbemerkung: Ich verstehe, dass diese Frage als eine als Frage maskierte Meinungsäußerung gelesen werden kann. Es ist jedoch nicht so - ich möchte wirklich verstehen, warum Verkettung als gute Praxis angesehen wird und wo ich falsch denke, dass sie die inhärente objektorientierte Notation bricht.
quelle
.
gäbe, die alle mehtod-Rückgabewerte ignoriert und immer verkettete Methoden mit demselben Objekt aufruft.Antworten:
Ich stimme zu, dass dies subjektiv ist. Zum größten Teil vermeide ich die Verkettung von Methoden, aber kürzlich habe ich auch einen Fall gefunden, in dem es genau das Richtige war - ich hatte eine Methode, die ungefähr 10 Parameter akzeptierte und mehr benötigte, aber für die meiste Zeit musste man nur eine angeben wenige. Mit Overrides wurde dies sehr schnell sehr umständlich. Stattdessen habe ich mich für den Verkettungsansatz entschieden:
Der Ansatz der Methodenverkettung war optional, erleichterte jedoch das Schreiben von Code (insbesondere mit IntelliSense). Wohlgemerkt, dass dies ein Einzelfall ist und in meinem Code keine allgemeine Praxis ist.
Der Punkt ist - in 99% der Fälle können Sie wahrscheinlich genauso gut oder sogar besser ohne Methodenverkettung abschneiden. Aber es gibt die 1%, bei denen dies der beste Ansatz ist.
quelle
P = MyObject.GetParamsObj().SomeParameter(asdasd).SomeOtherParameter(asdasd); Obj = MyObject.Start(); MyObject.Execute(P);
. Sie haben den Vorteil, dass Sie dieses Parameterobjekt in anderen Aufrufen wiederverwenden können, was ein Plus ist!PizzaBuilder.AddSauce().AddDough().AddTopping()
mehr Referenzen habenlist.add(someItem)
, lese ich das als "dieser Code fügtsomeItem
demlist
Objekt hinzu". Wenn ich also lesePizzaBuilder.AddSauce()
, lese ich dies natürlich als "dieser Code fügt demPizzaBuilder
Objekt Sauce hinzu ". Mit anderen Worten, sehe ich die Orchestrierung (die man die Zugabe tun) als das Verfahren , in dem der Codelist.add(someItem)
oderPizzaBuilder.addSauce()
eingebettet ist. Aber ich denke nur, dass das PizzaBuilder-Beispiel ein wenig künstlich ist. Ihr BeispielMyObject.SpecifySomeParameter(asdasd)
funktioniert gut für mich.Nur meine 2 Cent;
Die Verkettung von Methoden macht das Debuggen schwierig: - Sie können den Haltepunkt nicht an einem präzisen Punkt platzieren, sodass Sie das Programm genau dort anhalten können, wo Sie es möchten. - Wenn eine dieser Methoden eine Ausnahme auslöst und Sie eine Zeilennummer erhalten, haben Sie keine Ahnung Welche Methode in der "Kette" hat das Problem verursacht?
Ich denke, es ist im Allgemeinen eine gute Praxis, immer sehr kurze und prägnante Zeilen zu schreiben. Jede Zeile sollte nur einen Methodenaufruf ausführen. Ziehen Sie mehr Zeilen längeren Zeilen vor.
BEARBEITEN: Kommentar erwähnt, dass Methodenverkettung und Zeilenumbruch getrennt sind. Das ist wahr. Abhängig vom Debugger kann es jedoch möglich sein, einen Haltepunkt in die Mitte einer Anweisung zu setzen oder nicht. Selbst wenn Sie dies können, bietet die Verwendung separater Zeilen mit Zwischenvariablen viel mehr Flexibilität und eine ganze Reihe von Werten, die Sie im Überwachungsfenster untersuchen können, um den Debugging-Prozess zu unterstützen.
quelle
Persönlich bevorzuge ich Verkettungsmethoden, die nur auf das ursprüngliche Objekt wirken, z. B. das Festlegen mehrerer Eigenschaften oder das Aufrufen von Methoden vom Typ Dienstprogramm.
Ich verwende es nicht, wenn eine oder mehrere der verketteten Methoden in meinem Beispiel ein anderes Objekt als foo zurückgeben würden. Während Sie syntaktisch alles verketten können, solange Sie die richtige API für dieses Objekt in der Kette verwenden, macht das Ändern von Objekten IMHO die Lesbarkeit weniger gut und kann sehr verwirrend sein, wenn die APIs für die verschiedenen Objekte Ähnlichkeiten aufweisen. Wenn Sie am Ende einig wirklich gemeinsamen Methodenaufruf tun (
.toString()
,.print()
, was auch immer) , der Gegenstand handeln Sie letztlich auf? Jemand, der den Code beiläufig liest, erkennt möglicherweise nicht, dass es sich um ein implizit zurückgegebenes Objekt in der Kette handelt und nicht um die ursprüngliche Referenz.Das Verketten verschiedener Objekte kann auch zu unerwarteten Nullfehlern führen. In meinen Beispielen sind unter der Annahme, dass foo gültig ist, alle Methodenaufrufe "sicher" (z. B. gültig für foo). Im Beispiel des OP:
... gibt es keine Garantie (als externer Entwickler, der sich den Code ansieht), dass getSchedule tatsächlich ein gültiges Nicht-Null-Zeitplanobjekt zurückgibt. Das Debuggen dieses Codestils ist häufig sehr viel schwieriger, da viele IDEs den Methodenaufruf zum Zeitpunkt des Debuggens nicht als ein Objekt auswerten, das Sie überprüfen können. IMO, wann immer Sie ein Objekt benötigen, um es zu Debugging-Zwecken zu untersuchen, bevorzuge ich es in einer expliziten Variablen.
quelle
Participant
keine gültige hatSchedule
danngetSchedule
Methode entwickelt , um eine RückkehrMaybe(of Schedule)
Art undsaveTo
Verfahren entwickelt , um einen akzeptierenMaybe
Typen.Martin Fowler hat hier eine gute Diskussion:
quelle
Meiner Meinung nach ist die Verkettung von Methoden ein Novum. Sicher, es sieht cool aus, aber ich sehe keine wirklichen Vorteile darin.
Wie ist:
besser als:
Die Ausnahme kann sein, wenn addObject () ein neues Objekt zurückgibt. In diesem Fall ist der nicht verkettete Code möglicherweise etwas umständlicher wie:
quelle
Dies ist gefährlich, da Sie möglicherweise von mehr Objekten als erwartet abhängig sind. In diesem Fall gibt Ihr Aufruf eine Instanz einer anderen Klasse zurück:
Ich werde ein Beispiel geben:
foodStore ist ein Objekt, das aus vielen Lebensmittelgeschäften besteht, die Sie besitzen. foodstore.getLocalStore () gibt ein Objekt zurück, das Informationen zum Speicher enthält, der dem Parameter am nächsten liegt. getPriceforProduct (irgendetwas) ist eine Methode dieses Objekts.
Wenn Sie also foodStore.getLocalStore (Parameter) aufrufen .getPriceforProduct (irgendetwas)
Sie sind nicht nur auf FoodStore angewiesen, sondern auch auf LocalStore.
Sollte sich getPriceforProduct (irgendetwas) jemals ändern, müssen Sie nicht nur FoodStore ändern, sondern auch die Klasse, die die verkettete Methode aufgerufen hat.
Sie sollten immer eine lose Kopplung zwischen den Klassen anstreben.
Davon abgesehen verkette ich sie persönlich gerne, wenn ich Ruby programmiere.
quelle
Viele verwenden die Methodenverkettung als Annehmlichkeit, anstatt Bedenken hinsichtlich der Lesbarkeit zu berücksichtigen. Die Verkettung von Methoden ist akzeptabel, wenn dieselbe Aktion für dasselbe Objekt ausgeführt wird - jedoch nur, wenn die Lesbarkeit tatsächlich verbessert wird und nicht nur, um weniger Code zu schreiben.
Leider verwenden viele Methodenverkettungen gemäß den in der Frage angegebenen Beispielen. Während sie können noch lesbar gemacht werden, werden sie hohe Kopplung zwischen mehreren Klassen leider verursacht, so ist es nicht wünschenswert.
quelle
Das scheint irgendwie subjektiv zu sein.
Methodenverkettung ist nichts, was von Natur aus schlecht oder gut ist.
Lesbarkeit ist das Wichtigste.
(Bedenken Sie auch, dass eine große Anzahl verketteter Methoden die Dinge sehr zerbrechlich macht, wenn sich etwas ändert.)
quelle
Vorteile der Verkettung
dh wo ich sie gerne benutze
Ein Vorteil der Verkettung, den ich nicht erwähnt habe, war die Möglichkeit, ihn während der Variableninitiierung oder beim Übergeben eines neuen Objekts an eine Methode zu verwenden. Ich bin mir nicht sicher, ob dies eine schlechte Praxis ist oder nicht.
Ich weiß, dass dies ein erfundenes Beispiel ist, aber sagen Sie, dass Sie die folgenden Klassen haben
Angenommen, Sie haben keinen Zugriff auf die Basisklasse, oder Sie sagen, die Standardwerte sind dynamisch, basieren auf der Zeit usw. Ja, Sie könnten dann instanziieren und dann die Werte ändern, aber das kann umständlich werden, insbesondere wenn Sie nur übergeben die Werte zu einer Methode:
Aber ist das nicht viel einfacher zu lesen:
Oder was ist mit einem Klassenmitglied?
vs.
Auf diese Weise habe ich die Verkettung verwendet. Normalerweise dienen meine Methoden nur der Konfiguration. Sie sind also nur 2 Zeilen lang. Legen Sie dann einen Wert fest
Return Me
. Für uns hat es große Zeilen aufgeräumt, die sehr schwer zu lesen und zu verstehen sind. Code in einer Zeile, die wie ein Satz liest. etwas wieVs So etwas wie
Nachteil der Verkettung,
dh wo ich es nicht gerne benutze
Ich verwende keine Verkettung, wenn viele Parameter an die Routinen übergeben werden müssen, hauptsächlich, weil die Zeilen sehr lang werden. Wie im OP erwähnt, kann es verwirrend werden, wenn Sie Routinen an andere Klassen aufrufen, um sie an eine der Routinen zu übergeben die Verkettungsmethoden.
Es besteht auch die Sorge, dass eine Routine ungültige Daten zurückgibt. Bisher habe ich die Verkettung nur verwendet, wenn ich dieselbe aufgerufene Instanz zurückgebe. Wie bereits erwähnt, erschweren Sie beim Debattieren zwischen Klassen das Debuggen (welches hat null zurückgegeben?) Und können die Abhängigkeitskopplung zwischen Klassen erhöhen.
Fazit
Wie alles im Leben und beim Programmieren ist das Verketten weder gut noch schlecht, wenn Sie das Schlechte vermeiden können, kann das Verketten ein großer Vorteil sein.
Ich versuche diese Regeln zu befolgen.
quelle
Durch die Verkettung von Methoden können erweiterte DSLs direkt in Java entworfen werden. Im Wesentlichen können Sie mindestens diese Arten von DSL-Regeln modellieren:
Diese Regeln können über diese Schnittstellen implementiert werden
Mit diesen einfachen Regeln können Sie komplexe DSLs wie SQL direkt in Java implementieren, wie dies von jOOQ , einer von mir erstellten Bibliothek, durchgeführt wird. Ein ziemlich komplexes SQL-Beispiel aus meinem Blog finden Sie hier:
Ein weiteres schönes Beispiel ist jRTF , ein kleines DSL, mit dem RTF-Dokumente direkt in Java bearbeitet werden können. Ein Beispiel:
quelle
Methodenverkettung mag in den meisten Fällen einfach eine Neuheit sein, aber ich denke, sie hat ihren Platz. Ein Beispiel könnte in CodeIgniters Active Record-Verwendung zu finden sein :
Das sieht viel sauberer aus (und macht meiner Meinung nach mehr Sinn) als:
Es ist wirklich subjektiv; Jeder hat seine eigene Meinung.
quelle
Ich denke, der primäre Irrtum besteht darin, dass dies im Allgemeinen ein objektorientierter Ansatz ist, obwohl es sich eher um einen funktionalen Programmieransatz als um irgendetwas anderes handelt.
Die Hauptgründe, warum ich es verwende, sind sowohl die Lesbarkeit als auch die Verhinderung, dass mein Code von Variablen überschwemmt wird.
Ich verstehe nicht wirklich, wovon andere sprechen, wenn sie sagen, dass dies die Lesbarkeit beeinträchtigt. Es ist eine der prägnantesten und kohärentesten Formen der Programmierung, die ich verwendet habe.
Auch das:
convertTextToVoice.LoadText ("source.txt"). ConvertToVoice ("destination.wav");
So würde ich es normalerweise benutzen. Ich benutze es normalerweise nicht, um x Anzahl von Parametern zu verketten. Wenn ich x Anzahl von Parametern in einen Methodenaufruf eingeben möchte, würde ich die Parameter- Syntax verwenden:
public void foo (params object [] items)
Und wandeln Sie die Objekte basierend auf dem Typ um oder verwenden Sie je nach Anwendungsfall einfach ein Datentyp-Array oder eine Sammlung.
quelle
Ich stimme zu, ich habe daher die Art und Weise geändert, wie eine fließende Schnittstelle in meiner Bibliothek implementiert wurde.
Vor:
Nach dem:
In der "Vorher" -Implementierung haben die Funktionen das Objekt geändert und endeten in
return this
. Ich habe die Implementierung geändert, um ein neues Objekt des gleichen Typs zurückzugeben .Meine Begründung für diese Änderung :
Der Rückgabewert hatte nichts mit der Funktion zu tun, er war nur dazu da, den Verkettungsteil zu unterstützen. Es hätte laut OOP eine void-Funktion sein sollen.
Die Methodenverkettung in Systembibliotheken implementiert sie auch so (wie linq oder string):
Das ursprüngliche Objekt bleibt erhalten, sodass der API-Benutzer entscheiden kann, was damit geschehen soll. Es ermöglicht:
Eine Kopierimplementierung kann auch zum Erstellen von Objekten verwendet werden:
Wo die
setBackground(color)
Funktion die Instanz ändert und nichts zurückgibt (wie es soll) .Das Verhalten der Funktionen ist vorhersehbarer (siehe Punkt 1 und 2).
Die Verwendung eines kurzen Variablennamens kann auch die Code-Unordnung verringern, ohne dem Modell eine API aufzuzwingen.
Fazit:
Meiner Meinung nach ist eine fließende Schnittstelle, die eine
return this
Implementierung verwendet, einfach falsch.quelle
return this
verwalten oder auf andere Weise. Ich bin der Meinung, dass es auf "nicht natürliche" Weise eine fließende API erhält. (Grund 6 hinzugefügt: um eine nicht fließende Alternative zu zeigen, die nicht über den Overhead / die zusätzliche Funktionalität verfügt)Der völlig übersehene Punkt hier ist, dass die Methodenverkettung DRY ermöglicht . Es ist ein effektiver Ersatz für das "mit" (das in einigen Sprachen schlecht implementiert ist).
Dies ist aus dem gleichen Grund wichtig, aus dem DRY immer wichtig ist. Wenn sich herausstellt, dass A ein Fehler ist und diese Vorgänge für B ausgeführt werden müssen, müssen Sie nur an einer Stelle aktualisieren, nicht an 3.
Pragmatisch gesehen ist der Vorteil in diesem Fall gering. Trotzdem, ein bisschen weniger tippen, ein bisschen robuster (trocken), nehme ich es.
quelle
Ich hasse es im Allgemeinen, Methoden zu verketten, weil ich denke, dass dies die Lesbarkeit verschlechtert. Kompaktheit wird oft mit Lesbarkeit verwechselt, aber es handelt sich nicht um dieselben Begriffe. Wenn Sie alles in einer einzigen Anweisung ausführen, ist dies kompakt, aber meistens weniger lesbar (schwerer zu befolgen) als in mehreren Anweisungen. Wie Sie bemerkt haben, kann die Verkettung von Methoden Verwirrung stiften, es sei denn, Sie können nicht garantieren, dass der Rückgabewert der verwendeten Methoden gleich ist.
1.)
vs.
2.)
vs.
3.)
vs.
Wie Sie sehen, gewinnen Sie so gut wie nichts, weil Sie Ihrer einzelnen Anweisung Zeilenumbrüche hinzufügen müssen, um sie besser lesbar zu machen, und Einrückungen hinzufügen müssen, um zu verdeutlichen, dass Sie über verschiedene Objekte sprechen. Wenn ich eine identitätsbasierte Sprache verwenden möchte, würde ich stattdessen Python lernen, ganz zu schweigen davon, dass die meisten IDEs die Einrückung durch automatische Formatierung des Codes entfernen.
Ich denke, der einzige Ort, an dem diese Art der Verkettung nützlich sein kann, ist das Weiterleiten von Streams in CLI oder das Zusammenfügen mehrerer Abfragen in SQL. Beide haben einen Preis für mehrere Aussagen. Wenn Sie jedoch komplexe Probleme lösen möchten, müssen Sie selbst diejenigen bezahlen, die den Preis zahlen und den Code in mehreren Anweisungen mithilfe von Variablen schreiben oder Bash-Skripte und gespeicherte Prozeduren oder Ansichten schreiben.
Zu den DRY-Interpretationen: "Vermeiden Sie die Wiederholung von Wissen (nicht die Wiederholung von Text)." und "Tippe weniger, wiederhole nicht einmal Texte.", der erste, was das Prinzip wirklich bedeutet, aber der zweite ist ein weit verbreitetes Missverständnis, weil viele Menschen überkomplizierten Bullshit nicht verstehen können wie "Jedes Wissen muss einen einzigen, eindeutigen haben, maßgebliche Darstellung innerhalb eines Systems ". Die zweite ist die Kompaktheit um jeden Preis, die in diesem Szenario bricht, weil sie die Lesbarkeit verschlechtert. Die erste Interpretation wird durch DDD unterbrochen, wenn Sie Code zwischen begrenzten Kontexten kopieren, da in diesem Szenario eine lose Kopplung wichtiger ist.
quelle
Die gute:
Das Schlechte:
Allgemeines:
Ein guter Ansatz besteht darin, die Verkettung im Allgemeinen erst dann zu verwenden, wenn Situationen auftreten oder bestimmte Module dafür besonders geeignet sind.
Verkettung kann in einigen Fällen die Lesbarkeit erheblich beeinträchtigen, insbesondere beim Wiegen in Punkt 1 und 2.
Bei der Akasation kann es missbraucht werden, z. B. anstelle eines anderen Ansatzes (z. B. Übergeben eines Arrays) oder durch Mischen von Methoden auf bizarre Weise (parent.setSomething (). GetChild (). SetSomething (). GetParent (). SetSomething ()).
quelle
Stellungnahme
Der größte Nachteil der Verkettung besteht darin, dass es für den Leser schwierig sein kann zu verstehen, wie sich jede Methode auf das ursprüngliche Objekt auswirkt, wenn dies der Fall ist, und welchen Typ jede Methode zurückgibt.
Einige Fragen:
In den meisten Sprachen kann das Debuggen beim Verketten tatsächlich schwieriger sein. Selbst wenn sich jeder Schritt in der Kette in einer eigenen Linie befindet (was den Zweck der Verkettung zunichte macht), kann es schwierig sein, den nach jedem Schritt zurückgegebenen Wert zu überprüfen, insbesondere bei nicht mutierenden Methoden.
Die Kompilierungszeiten können je nach Sprache und Compiler langsamer sein, da die Auflösung von Ausdrücken sehr viel komplexer sein kann.
Ich glaube, dass Verketten wie bei allem eine gute Lösung ist, die in manchen Szenarien nützlich sein kann. Es sollte mit Vorsicht angewendet werden, um die Auswirkungen zu verstehen und die Anzahl der Kettenelemente auf wenige zu beschränken.
quelle
In typisierten Sprachen (die fehlen
auto
oder gleichwertig sind) muss der Implementierer die Typen der Zwischenergebnisse nicht deklarieren.Bei längeren Ketten, bei denen es sich möglicherweise um mehrere Zwischentypen handelt, müssen Sie jeden von ihnen deklarieren.
Ich glaube, dass dieser Ansatz wirklich in Java entwickelt wurde, wo a) alle Funktionsaufrufe Aufrufe von Mitgliedsfunktionen sind und b) explizite Typen erforderlich sind. Natürlich gibt es hier einen Kompromiss, der die Aussagekraft verliert, aber in manchen Situationen finden es einige Leute lohnenswert.
quelle