Hierbei handelt es sich um eine Entwurfsentscheidung, die anscheinend ziemlich häufig zu treffen ist: Wie kann der Kontext durch eine Methode geleitet werden, die ihn nicht benötigt? Gibt es eine richtige Antwort oder kommt es auf den Kontext an?
Beispielcode, der eine Lösung erfordert
// needs the dependency
function baz(session) {
session('baz');
}
// doesn't care about the dependency
function bar() {
baz();
}
// needs the dependency
function foo(session) {
session('foo')
bar();
}
// creates the dependency
function start() {
let session = new Session();
foo(session);
}
Mögliche Lösungen
- threadlocal
- global
- Kontextobjekt
- die Abhängigkeit weitergeben
- curry baz und gib es in bar mit der abhängigkeit als erstes arg
- Abhängigkeitsspritze
Beispiele dafür, wo es herkommt
HTTP-Anforderungsverarbeitung
Kontextobjekte in Form von Anforderungsattributen werden häufig verwendet: siehe expressjs, Java Servlets oder .net's owin.
Protokollierung
Für die Java-Protokollierung werden häufig Globals / Singletons verwendet. Siehe die typischen Log4J- / Commons-Logging- / Java-Logging-Muster.
Transaktionen
Thread-Locals werden häufig verwendet, um eine Transaktion oder Sitzung mit einer Kette von Methodenaufrufen zu verknüpfen, damit sie nicht als Parameter an alle Methoden übergeben werden müssen, die sie nicht benötigen.
design-patterns
Jamie McCrindle
quelle
quelle
Antworten:
Die einzig faire Antwort ist, dass es von den Redewendungen Ihres Programmierparadigmas abhängt. Wenn Sie OO verwenden, ist es mit ziemlicher Sicherheit falsch, eine Abhängigkeit von Methode zu Methode zu Methode zu übergeben. Es ist ein Code-Geruch in OO. Tatsächlich ist dies eines der Probleme, die OO löst - ein Objekt repariert einen Kontext. In OO besteht ein korrekter Ansatz (es gibt immer andere Möglichkeiten) darin, die Abhängigkeit über einen Konstruktor oder eine Eigenschaft zu liefern. Ein Kommentator erwähnt "Dependency Injection" und das ist absolut legitim, aber nicht unbedingt notwendig. Geben Sie einfach die Abhängigkeit an, damit sie als Mitglied für
foo
und verfügbar istbaz
.Sie erwähnen das Currying, also gehe ich davon aus, dass funktionale Programmierung nicht ausgeschlossen ist. In diesem Fall ist die Schließung ein philosophisches Äquivalent des Objektkontexts. Jeder Ansatz, der die Abhängigkeit erneut behebt, sodass sie für abhängige Personen verfügbar ist, funktioniert einwandfrei. Currying ist ein solcher Ansatz (und es lässt Sie klug klingen). Denken Sie daran, dass es andere Möglichkeiten gibt, eine Abhängigkeit zu schließen. Einige von ihnen sind elegant und einige schrecklich.
Vergessen Sie nicht die aspektorientierte Programmierung . Es scheint in den letzten Jahren in Ungnade gefallen zu sein, aber sein primäres Ziel ist es, genau das Problem zu lösen , das Sie beschreiben. Das klassische Aspektbeispiel ist die Protokollierung. In AOP wird die Abhängigkeit automatisch hinzugefügt, nachdem anderer Code geschrieben wurde. AOP-Leute nennen das " Weben ". Gemeinsame Aspekte werden an den entsprechenden Stellen in den Code eingearbeitet. Dies erleichtert das Nachdenken über Ihren Code und ist verdammt cool, fügt aber auch eine neue Testlast hinzu. Sie müssen einen Weg finden, um festzustellen, ob Ihre endgültigen Artefakte einwandfrei sind. AOP hat auch dafür Antworten, also lassen Sie sich nicht einschüchtern.
quelle
Ist
bar
abhängig vonbaz
, was wiederum erfordertdependency
, dannbar
erfordert diesdependency
auch, um richtig zu verwendenbaz
. Daher wäre es die richtige Vorgehensweise, die Abhängigkeit entweder als Parameter an weiterzuleitenbar
oder sie zu currybaz
und weiterzuleitenbar
.Der erste Ansatz ist einfacher zu implementieren und zu lesen, schafft jedoch eine Kopplung zwischen
bar
undbaz
. Der zweite Ansatz entfernt diese Kopplung, kann jedoch zu weniger klarem Code führen. Welcher Ansatz am besten ist, hängt daher wahrscheinlich von der Komplexität und dem Verhalten beider Funktionen ab. Wenn beispielsweise Nebenwirkungen auftretenbaz
oderdependency
auftreten, ist die Testfreundlichkeit wahrscheinlich ein wichtiger Faktor für die Auswahl der Lösung.Ich würde vorschlagen, dass alle anderen von Ihnen vorgeschlagenen Optionen "hacky" sind und wahrscheinlich sowohl zu Problemen beim Testen als auch bei der Suche nach schwer auffindbaren Fehlern führen.
quelle
dependency
ist die Abhängigkeitsinjektion sicherlich der eigentliche Vorgang des Umweges über Parameter?Philosophisch gesprochen
Ich stimme der Besorgnis von David Arno zu .
Ich lese das OP als auf der Suche nach Implementierungslösungen. Die Antwort ist jedoch , das Design zu ändern . "Muster"? OO-Design dreht sich, könnte man sagen, alles um den Kontext. Es ist ein riesiges, leeres Blatt Papier mit unzähligen Möglichkeiten.
Der Umgang mit vorhandenem Code ist ein anderer Kontext.
Ich arbeite gerade an genau demselben Problem. Nun, ich korrigiere die Hunderte von Codezeilen beim Kopieren und Einfügen, damit ein Wert eingefügt werden kann.
Modularisieren Sie den Code
Ich habe 600 Zeilen doppelten Codes weggeworfen und dann überarbeitet, sodass anstelle von "A ruft B ruft C ruft D auf ..." ich "Ruf A, Rückgabe, Ruf B, Rückgabe, Ruf C auf ..." habe. Jetzt müssen wir den Wert nur noch in eine dieser Methoden einfügen, sagen wir Methode E.
Fügen Sie dem Konstruktor einen Standardparameter hinzu. Bestehende Anrufer ändern sich nicht - "optional" ist hier das maßgebliche Wort. Wird ein Argument nicht übergeben, wird der Standardwert verwendet. Dann nur 1 Zeile ändern, um die Variable in die überarbeitete, modulare Struktur zu übergeben; und eine kleine Änderung in Methode E, um es zu verwenden.
Verschlüsse
Ein Programmier-Thread - "Warum sollte ein Programm einen Abschluss verwenden?"
Im Wesentlichen injizieren Sie Werte in eine Methode, die eine mit den Werten angepasste Methode zurückgibt. Diese angepasste Methode wird anschließend ausgeführt.
Mit dieser Technik können Sie eine vorhandene Methode ändern, ohne ihre Signatur zu ändern.
quelle