Wzory do przekazywania kontekstu przez łańcuch metod

19

Jest to decyzja projektowa, która wydaje się często pojawiać: jak przekazać kontekst za pomocą metody, która jej nie potrzebuje, do metody, która to robi. Czy istnieje poprawna odpowiedź, czy zależy to od kontekstu.

Przykładowy kod, który wymaga rozwiązania

// 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);
}

Możliwe rozwiązania

  • Threadlocal
  • światowy
  • obiekt kontekstowy
  • przekazać zależność
  • curry baz i przekaż go do paska z ustawioną zależnością jako pierwszy argument
  • zastrzyk zależności

Przykłady gdzie pojawia się

Przetwarzanie żądań HTTP

Często używane są obiekty kontekstowe w postaci atrybutów żądania: patrz expressjs, Java Servlet lub owin .net.

Logowanie

Do logowania w Javie ludzie często używają globals / singletonów. Zobacz typowe wzorce logowania log4j / commons / java.

Transakcje

Lokalne wątki są często używane do utrzymywania transakcji lub sesji związanych z łańcuchem wywołań metod, aby uniknąć konieczności przekazywania ich jako parametrów do wszystkich metod, które ich nie potrzebują.

Jamie McCrindle
źródło
Proszę użyć bardziej sensownego przykładu.
Tulains Córdova
Dodałem kilka przykładów tego, co się dzieje.
Jamie McCrindle
3
Miałem na myśli bardziej znaczący przykładowy kod.
Tulains Córdova

Odpowiedzi:

11

Jedyną uczciwą odpowiedzią jest to, że zależy to od idiomów twojego paradygmatu programistycznego. Jeśli używasz OO, prawie na pewno niepoprawne jest przekazywanie zależności między metodami. To zapach kodu w OO. W rzeczywistości jest to jeden z problemów, które rozwiązuje OO - obiekt naprawia kontekst. Tak więc w OO jednym poprawnym (zawsze istnieją inne sposoby) podejściem jest dostarczenie zależności za pośrednictwem podmiotu konstruktorskiego lub własności. Komentator wspomina o „wstrzykiwaniu zależności” i jest to całkowicie uzasadnione, ale nie jest to absolutnie konieczne. Wystarczy podać zależność, aby była dostępna jako członek dla fooi baz.

Wspominasz o curry, więc założę, że programowanie funkcjonalne nie jest wykluczone. W takim przypadku filozoficznym odpowiednikiem kontekstu obiektowego jest zamknięcie. Każde podejście, które po raz kolejny naprawia zależność, dzięki czemu jest dostępne dla osób na utrzymaniu, działa dobrze. Curry to jedno z takich podejść (i sprawia, że ​​brzmisz mądrze). Pamiętaj tylko, że istnieją inne sposoby zamknięcia zależności. Niektóre z nich są eleganckie, a niektóre okropne.

Nie zapomnij o programowaniu aspektowym . Wygląda na to, że w ostatnich latach popadł w niełaskę, ale jego głównym celem jest rozwiązanie dokładnie opisanego problemu. W rzeczywistości klasycznym przykładem aspektu jest logowanie. W AOP zależność jest dodawana automatycznie po napisaniu innego kodu. Ludzie AOP nazywają to „ tkaniem ”. Wspólne aspekty są wplecione w kod w odpowiednich miejscach. To sprawia, że ​​twój kod jest łatwiejszy do myślenia i jest naprawdę fajny, ale także dodaje nowe obciążenie testowe. Będziesz potrzebował sposobu, aby ustalić, że twoje ostatnie artefakty są zdrowe. AOP ma na to również odpowiedzi, więc nie czuj się zastraszony.

Scant Roger
źródło
Twierdzenie, że przekazywanie parametrów w ramach metod OO jest zapachem kodu, jest bardzo kontrowersyjnym stwierdzeniem. Byłbym całkowicie przeciwny: zachęcanie do mieszania stanu i funkcjonalności w klasie jest jednym z największych błędów popełnianych przez paradygmat OO, a unikanie go poprzez wstrzykiwanie zależności bezpośrednio do metod, a nie przez konstruktora, jest oznaką dobrze zaprojektowanego kawałka kodu, niezależnie od tego, czy jest to OO czy nie.
David Arno
3
@DavidArno Sugeruję użycie innego paradygmatu niż stwierdzenie, że stan obiektu jest „jednym z największych błędów popełnianych przez paradygmat OO”, a następnie obejście paradygmatu. Nie mam nic przeciwko prawie żadnemu podejściu, ale ogólnie nie lubię kodu, w którym autor walczy ze swoim narzędziem. Państwo prywatne jest cechą wyróżniającą OO. Jeśli unikniesz tej funkcji, tracisz część mocy OO.
Scant Roger
1
@DavidArno Klasa, która jest w całości stanowa i nie ma żadnej funkcji, nie ma mechanizmu wymuszania niezmiennych relacji w tym stanie. Taka klasa wcale nie jest OO.
Kevin Krumwiede
@KevinKrumwiede, do pewnego stopnia zastosowałeś reducto ad absudium do mojego komentarza, ale twoja uwaga jest nadal słuszna. Niezmienność stanu jest ważną częścią „przejścia od OO”. Tak więc unikanie mieszania funkcjonalności i stanu musi pozwolić na wystarczającą funkcjonalność obiektu stanu, niezbędną do osiągnięcia niezmienności (enkapsulowane pola ustawiane przez konstruktora i dostępne za pośrednictwem getterów).
David Arno
@ScantRoger, zgadzam się, że można przyjąć inny paradygmat, a mianowicie paradygmat funkcjonalny. Co ciekawe, większość współczesnych języków „OO” ma rosnącą listę funkcji funkcjonalnych, więc można trzymać się tych języków i przyjąć paradygmat funkcji, bez „walki z narzędziem”.
David Arno,
10

Jeśli barzależy od tego baz, co z kolei wymaga dependency, to także barwymaga dependencyprawidłowego użycia baz. Dlatego poprawnym podejściem byłoby albo przekazanie zależności jako parametru bar, albo curry bazi przekazanie tego do bar.

Pierwsze podejście jest prostsze do wdrożenia i odczytu, ale tworzy sprzężenie między bari baz. Drugie podejście usuwa to połączenie, ale może skutkować mniej czytelnym kodem. To, które podejście jest najlepsze, będzie prawdopodobnie zależeć od złożoności i zachowania obu funkcji. Na przykład, jeśli bazlub dependencymają skutki uboczne, łatwość testowania będzie prawdopodobnie dużym czynnikiem decydującym o wyborze rozwiązania.

Sugerowałbym, że wszystkie inne opcje, które proponujesz, są z natury „hackerskie” i mogą prowadzić do problemów zarówno z testowaniem, jak i trudnymi do wyśledzenia błędów.

David Arno
źródło
1
Prawie całkowicie się zgadzam. Wstrzykiwanie zależności może być kolejnym podejściem nie „hacky”.
Jonathan van de Veen
1
@JonathanvandeVeen, z pewnością sam fakt przekazywania dependencyprzez parametry to wstrzykiwanie zależności?
David Arno
2
@DavidArno Framework wstrzykiwania zależności nie pozbywa się tego rodzaju zależności, po prostu je przenosi. Magia polega na tym, że przenoszą ich poza zajęcia, do miejsca, w którym testowanie jest problemem kogoś innego.
Kevin Krumwiede
@JonathanvandeVeen Zgadzam się, zastrzyk zależności jest prawidłowym rozwiązaniem. W rzeczywistości to ten, który najczęściej wybrałem.
Jamie McCrindle
1

Mówiąc filozoficznie

Zgadzam się z troską Davida Arno .

Czytam OP jako szukam rozwiązań wdrożeniowych. Odpowiedzią jest jednak zmiana projektu . „Wzory”? Można powiedzieć, że projektowanie OO opiera się na kontekście. To rozległa, pusta kartka papieru w ciąży z możliwościami.

Radzenie sobie z istniejącym kodem to inny, dobrze, kontekst.



Pracuję teraz nad tym samym problemem. Cóż, naprawiam setki wierszy kodu kopiuj-wklej, co zostało zrobione tylko po to, aby można było wstrzyknąć wartość.

Zmodularyzuj kod

Wyrzuciłem 600 wierszy zduplikowanego kodu, a następnie refaktoryzowałem, więc zamiast „A wzywa B wzywa C wzywa D ...” Mam „Zadzwoń A, zwróć, Zadzwoń B, zwróć, Zadzwoń C ...”. Teraz musimy tylko wprowadzić wartość do jednej z tych metod, powiedzmy metoda E.

Dodaj domyślny parametr do konstruktora. Obecni dzwoniący nie zmieniają się - tutaj słowo opcjonalne jest „opcjonalne”. Jeśli argument nie zostanie przekazany, zostanie użyta wartość domyślna. Następnie zmienia się tylko 1 linia, aby przekazać zmienną do przebudowanej, modułowej struktury; i niewielka zmiana w metodzie E, aby z niej skorzystać.


Domknięcia

Wątek dla programistów - „Dlaczego program miałby używać zamknięcia?”

Zasadniczo wstrzykujesz wartości do metody, która zwraca metodę dostosowaną do tych wartości. Ta dostosowana metoda jest następnie wykonywana.

Ta technika pozwoli ci zmodyfikować istniejącą metodę bez zmiany jej podpisu.

radarbob
źródło
To podejście wygląda dziwnie znajomo ...
Roger o problemie ze sprzężeniem czasowym (twój link), @Snowman. Ważne jest, aby wymagana kolejność wykonania była zamknięta.
radarbob,