Właśnie czytałem artykuł na blogu i zauważyłem, że autor użył tap
we fragmencie czegoś takiego:
user = User.new.tap do |u|
u.username = "foobar"
u.save!
end
Moje pytanie brzmi: jaka dokładnie jest korzyść lub korzyść z używania tap
? Nie mogłem po prostu zrobić:
user = User.new
user.username = "foobar"
user.save!
Lub jeszcze lepiej:
user = User.create! username: "foobar"
User.new.tap &:foobar
user = User.create!(username: 'foobar')
w tym przypadku byłoby najwyraźniej i najkrócej :) - ostatni przykład z pytania.user
”. Również argument, że „Czytelnik nie musiałby czytać zawartości bloku, aby wiedzieć, że instancjauser
została utworzona”. nie ma żadnej wagi, ponieważ w pierwszym bloku kodu czytelnik musi również przeczytać tylko pierwszą linię, „aby wiedzieć, że instancjauser
została utworzona”.Innym przypadkiem użycia kranu jest wykonanie manipulacji na obiekcie przed jego zwróceniem.
Więc zamiast tego:
możemy zapisać dodatkową linię:
W niektórych sytuacjach ta technika może zaoszczędzić więcej niż jedną linię i sprawić, że kod będzie bardziej zwarty.
źródło
some_object.tap(&:serialize)
Korzystanie z kranu, tak jak zrobił to blogger, jest po prostu wygodną metodą. W twoim przykładzie mogło to być przesadą, ale w przypadkach, gdy chcesz zrobić kilka rzeczy z użytkownikiem, dotknięcie może zapewne zapewnić bardziej przejrzysty interfejs. Może więc lepiej będzie na poniższym przykładzie:
Użycie powyższego ułatwia szybkie sprawdzenie, że wszystkie te metody są zgrupowane razem, ponieważ wszystkie odnoszą się do tego samego obiektu (użytkownika w tym przykładzie). Alternatywą byłoby:
Ponownie, jest to dyskusyjne - ale można stwierdzić, że druga wersja wygląda trochę bardziej niechlujnie i wymaga trochę bardziej ludzkiego analizowania, aby zobaczyć, że wszystkie metody są wywoływane na tym samym obiekcie.
źródło
user = User.new, user.do_something, user.do_another_thing
... czy mógłbyś wyjaśnić, dlaczego można to zrobić?tap
Z mojego doświadczenia wynika, że używanie nigdy nie przyniosło żadnych korzyści. Tworzenie i praca zeuser
zmienną lokalną jest moim zdaniem znacznie czystsza i czytelna.u = user = User.new
a następnie użyłeś gou
do wywołań konfiguracji, byłoby to bardziej zgodne z pierwszym przykładem.Może to być przydatne przy debugowaniu serii
ActiveRecord
połączonych zakresów.To sprawia, że bardzo łatwo jest debugować w dowolnym punkcie łańcucha bez konieczności przechowywania czegokolwiek w zmiennej lokalnej ani konieczności znacznej zmiany oryginalnego kodu.
I wreszcie, użyj go jako szybkiego i dyskretnego sposobu debugowania bez zakłócania normalnego wykonywania kodu :
źródło
Wizualizuj swój przykład w funkcji
Takie podejście wiąże się z dużym ryzykiem związanym z konserwacją, w zasadzie z niejawną wartością zwrotu .
W tym kodzie zależy od
save!
zwrócenia zapisanego użytkownika. Ale jeśli użyjesz innej kaczki (lub twoja obecna ewoluuje), możesz otrzymać inne rzeczy, takie jak raport o stanie ukończenia. W związku z tym zmiany w kaczce mogą spowodować uszkodzenie kodu, co by się nie zdarzyło, gdyby wartość zwracana była podawana zwykłymuser
dotknięciem lub użyj.Widziałem takie wypadki dość często, szczególnie w przypadku funkcji, w których wartość zwracana zwykle nie jest używana, z wyjątkiem jednego ciemnego, błędnego rogu.
Niejawna wartość zwracana jest zwykle jedną z tych rzeczy, w których nowicjusze mają tendencję do przerywania rzeczy, dodając nowy kod po ostatniej linii, nie zauważając efektu. Nie widzą, co naprawdę oznacza powyższy kod:
źródło
user
?User.new.tap{ |u| u.username = name; u.save! }
Jeśli chcesz zwrócić użytkownika po ustawieniu nazwy użytkownika, musisz to zrobić
Z
tap
was może uratować tę niezręczną powrótźródło
Object#tap
dla mnie najczęstszy przypadek użycia .user = User.new.tap {|u| u.username = 'foobar' }
Skutkuje to mniejszym bałaganem w kodzie, ponieważ zakres zmiennej jest ograniczony tylko do części, w której jest naprawdę potrzebna. Ponadto wcięcie w bloku sprawia, że kod jest bardziej czytelny dzięki przechowywaniu razem odpowiedniego kodu.
Opis
tap
mówi :Jeśli przeszukamy kod źródłowy railsów pod kątem
tap
użycia , możemy znaleźć kilka interesujących zastosowań. Poniżej znajduje się kilka pozycji (lista nie jest wyczerpująca), które dadzą nam kilka pomysłów, jak z nich korzystać:Dołącz element do tablicy na podstawie określonych warunków
Inicjowanie tablicy i zwracanie jej
Jako cukier syntaktyczny, aby uczynić kod bardziej czytelnym - w poniższym przykładzie można powiedzieć, że użycie zmiennych
hash
iserver
sprawia, że intencja kodu jest jaśniejsza.Inicjuj / wywołuj metody na nowo utworzonych obiektach.
Poniżej przykład z pliku testowego
Działanie na wyniku
yield
wywołania bez konieczności używania zmiennej tymczasowej.źródło
Odmiana odpowiedzi @ sawa:
Jak już wspomniano, używanie
tap
pomaga zorientować się w przeznaczeniu kodu (niekoniecznie czyniąc go bardziej zwartym).Następujące dwie funkcje są równie długie, ale w pierwszej musisz przeczytać do końca, aby dowiedzieć się, dlaczego na początku zainicjowałem pusty hash.
Z drugiej strony, od samego początku wiesz, że inicjowany skrót będzie wyjściem bloku (aw tym przypadku wartością zwracaną przez funkcję).
źródło
tap
daje bardziej przekonujący argument. Zgadzam się z innymi, że kiedy widziszuser = User.new
, zamiar jest już jasny. Anonimowa struktura danych może być jednak wykorzystana do wszystkiego, atap
metoda przynajmniej jasno pokazuje, że to struktura danych jest głównym elementem metody.def tapping1; {one: 1, two: 2}; end
programami.tap
To pomocnik w tworzeniu łańcuchów połączeń. Przekazuje swój obiekt do danego bloku i po jego zakończeniu zwraca obiekt:
Zaletą jest to, że tap zawsze zwraca obiekt, do którego jest wywoływany, nawet jeśli blok zwraca inny wynik. W ten sposób można wstawić blok kranu w środek istniejącego potoku metody bez przerywania przepływu.
źródło
Powiedziałbym, że nie ma żadnej korzyści z używania
tap
. Jedyna potencjalna korzyść, jak wskazuje @sawa jest, cytuję: „Czytelnik nie musiałby czytać zawartości bloku, aby wiedzieć, że utworzono instancję użytkownika”. Jednak w tym momencie można wysunąć argument, że jeśli robisz nieprostą logikę tworzenia rekordów, twój zamiar byłby lepiej przekazany poprzez wyodrębnienie tej logiki do własnej metody.Uważam, że
tap
jest to niepotrzebne obciążenie dla czytelności kodu i można by to zrobić bez lub zastąpić lepszą techniką, taką jak Extract Method .Chociaż
tap
jest to wygodna metoda, to także osobiste preferencje. Daćtap
szansę. Następnie napisz kod bez użycia tap, zobacz, czy podoba ci się jeden sposób.źródło
Mogłoby istnieć wiele zastosowań i miejsc, z których moglibyśmy skorzystać
tap
. Jak dotąd znalazłem tylko następujące 2 zastosowaniatap
.1) Głównym celem tej metody jest skorzystanie z łańcucha metod w celu wykonania operacji na wynikach pośrednich w łańcuchu. to znaczy
2) Czy kiedykolwiek zdarzyło Ci się wywołać metodę na jakimś obiekcie, a wartość zwracana nie była tym, czego chciałeś? Może chciałeś dodać dowolną wartość do zestawu parametrów przechowywanych w hashu. Aktualizujesz go za pomocą Hash. [] , Ale otrzymujesz back bar zamiast hash params, więc musisz go jawnie zwrócić. to znaczy
Aby przezwyciężyć tę sytuację,
tap
w grę wchodzi metoda. Po prostu wywołaj to na obiekcie, a następnie przekaż stuknij blok z kodem, który chciałeś uruchomić. Obiekt zostanie przekazany do bloku, a następnie zostanie zwrócony. to znaczyIstnieją dziesiątki innych przypadków użycia, spróbuj znaleźć je sam :)
Źródło:
1) Dock Object API API
2) pięć metod-ruby-powinieneś-używać
źródło
Masz rację: użycie
tap
w twoim przykładzie jest trochę bezcelowe i prawdopodobnie mniej czyste niż twoje alternatywy.Jak zauważa Rebitzele,
tap
jest to tylko wygodna metoda, często używana do tworzenia krótszych odniesień do bieżącego obiektu.Jednym z dobrych przypadków użycia
tap
jest debugowanie: możesz zmodyfikować obiekt, wydrukować bieżący stan, a następnie kontynuować modyfikowanie obiektu w tym samym bloku. Zobacz na przykład: http://moonbase.rydia.net/mental/blog/programming/eavesdropping-on-expressions .Czasami lubię używać
tap
metod wewnętrznych, aby warunkowo powrócić wcześniej, podczas gdy w przeciwnym razie zwracam bieżący obiekt.źródło
Istnieje narzędzie o nazwie flog, które mierzy, jak trudno jest odczytać metodę. „Im wyższy wynik, tym większy problem z kodem”.
i zgodnie z wynikiem bata metoda
tap
jest najtrudniejsza do odczytania (i zgadzam się z tym)źródło
Możesz uczynić swoje kody bardziej modułowymi za pomocą dotknięcia i uzyskać lepsze zarządzanie zmiennymi lokalnymi. Na przykład w poniższym kodzie nie musisz przypisywać zmiennej lokalnej do nowo utworzonego obiektu w zakresie metody. Zwróć uwagę, że zmienna bloku u jest objęta zakresem w obrębie bloku. W rzeczywistości jest to jedna z piękności kodu ruby.
źródło
W railsach możemy
tap
jawnie dodać parametry do białej listy:źródło
Podam inny przykład, którego użyłem. Mam metodę user_params, która zwraca parametry potrzebne do zapisania dla użytkownika (to jest projekt Rails)
Możesz zobaczyć, że nie zwracam niczego, ale ruby zwraca wynik ostatniej linii.
Po jakimś czasie musiałem warunkowo dodać nowy atrybut. Więc zmieniłem to na coś takiego:
Tutaj możemy użyć tap, aby usunąć lokalną zmienną i usunąć powrót:
źródło
W świecie, w którym wzorce programowania funkcjonalnego stają się najlepszą praktyką ( https://maryrosecook.com/blog/post/a-practical-introduction-to-functional-programming ), można zobaczyć
tap
, jakomap
na pojedynczej wartości, rzeczywiście , aby zmodyfikować dane w łańcuchu transformacji.Nie trzeba
item
tutaj wielokrotnie deklarować .źródło
Jaka jest różnica?
Różnica w zakresie czytelności kodu jest czysto stylistyczna.
Kod Przejdź przez:
Kluczowe punkty:
u
zmienna jest teraz używana jako parametr bloku?user
zmienna powinna teraz wskazywać na użytkownika (z nazwą użytkownika: „foobar” i który również jest zapisany).Dokumentacja API
Oto łatwa do odczytania wersja kodu źródłowego:
Aby uzyskać więcej informacji, zobacz te linki:
https://apidock.com/ruby/Object/tap
http://ruby-doc.org/core-2.2.3/Object.html#method-i-tap
źródło