Przekazywanie hashów zamiast parametrów metody

Widzę, że w Rubim (i ogólnie w językach typowanych dynamicznie) bardzo powszechną praktyką jest przekazywanie hasha, zamiast deklarowania konkretnych parametrów metody. Na przykład, zamiast deklarować metodę z parametrami i wywoływać ją w następujący sposób:

def my_method(width, height, show_border)
my_method(400, 50, false)

Możesz to zrobić w ten sposób:

def my_method(options)
my_method({"width" => 400, "height" => 50, "show_border" => false})
Chciałbym poznać Twoją opinię na ten temat. Czy jest to dobra czy zła praktyka, powinniśmy to robić czy nie? W jakiej sytuacji zastosowanie tej praktyki jest słuszne i w jakiej sytuacji może być niebezpieczne?
Author: Alan W. Smith, 2009-12-18

11 answers

Oba podejścia mają swoje zalety i wady, gdy używasz hasha opcji zastępującego standardowe argumenty, tracisz jasność w kodzie definiującym metodę, ale zyskujesz jasność za każdym razem, gdy używasz metody z powodu pseudo-nazwanych parametrów utworzonych za pomocą hasha opcji.

Moja ogólna zasada jest taka, że jeśli masz dużo argumentów dla metody (więcej niż 3 lub 4) lub dużo opcjonalnych argumentów, użyj skrótu opcji, w przeciwnym razie użyj standardowych argumentów. Jednak gdy używając skrótu opcji ważne jest, aby zawsze zawierać komentarz z definicją metody opisującą możliwe argumenty.

 25
Author: benofsky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-18 22:34:11

Ruby ma ukryte parametry hash, więc możesz również napisać

def my_method(options = {}) 

my_method(:width => 400, :height => 50, :show_border => false)

I z Rubim 1.9 i nową składnią hashową może być

my_method( width: 400, height: 50, show_border: false )

Gdy funkcja zajmuje więcej niż 3-4 parametry, o wiele łatwiej jest zobaczyć, co jest czym, bez liczenia odpowiednich pozycji.

 35
Author: MBO,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-18 10:45:46

Powiedziałbym, że jeśli jesteś albo:

  1. posiadające więcej niż 6 parametrów metody
  2. przekazywanie opcji , które mają niektóre wymagane, niektóre opcjonalne, a niektóre z wartościami domyślnymi
Prawdopodobnie chciałbyś użyć hasha. O wiele łatwiej jest zobaczyć, co znaczą argumenty bez zaglądania do dokumentacji.

Dla tych z Was, którzy mówią, że trudno jest zrozumieć, jakie opcje przyjmuje metoda, oznacza to po prostu, że kod jest słabo udokumentowany. Z YARD, możesz użyć znacznika @option, aby określić opcje:

##
# Create a box.
#
# @param [Hash] options The options hash.
# @option options [Numeric] :width The width of the box.
# @option options [Numeric] :height The height of the box.
# @option options [Boolean] :show_border (false) Whether to show a
#   border or not.
def create_box(options={})
  options[:show_border] ||= false
end

Ale w tym konkretnym przykładzie jest tak mało i prostych parametrów, więc myślę, że wybrałbym to:

##
# Create a box.
#
# @param [Numeric] width The width of the box.
# @param [Numeric] height The height of the box.
# @param [Boolean] show_border Whether to show a border or not.
def create_box(width, height, show_border=false)
end
 7
Author: henrikhodne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-20 12:24:53

Myślę, że ta metoda przekazywania parametrów jest znacznie jaśniejsza, gdy jest więcej niż kilka parametrów lub gdy istnieje wiele opcjonalnych parametrów. Zasadniczo sprawia, że wywołania metody wyraźnie samokontroli.

 3
Author: Rich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-18 10:00:27

Nie jest powszechną praktyką w Rubim używanie skrótu zamiast parametrów formalnych.

Myślę, że jest to mylone z powszechnym wzorcem przekazywania hasha jako parametru, gdy parametr może przyjmować wiele wartości, np. ustawiając atrybuty okna w zestawie narzędzi GUI.

Jeśli masz kilka argumentów do swojej metody lub funkcji, to jawnie zadeklaruj je i przekaż. Otrzymujesz świadczenie, które Tłumacz sprawdzi, że zdałeś wszystkie argumenty.

Nie nadużywaj funkcji języka, wiedz, kiedy jej używać, a kiedy nie.

 3
Author: Chris McCauley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-18 12:42:13

Zaletą użycia Hash jako parametru jest usunięcie zależności od liczby i kolejności argumentów.

W praktyce oznacza to, że później będziesz mieć możliwość refaktorowania / zmiany metody bez naruszania zgodności z kodem klienta (i jest to bardzo dobre podczas budowania bibliotek, ponieważ nie możesz zmienić kodu klienta).

[[1]}(Sandy Metz 's " Practical Object-Oriented Design in Ruby " to świetna książka, jeśli jesteś zainteresowany Projektowanie oprogramowania w Ruby)
 3
Author: Aldo 'xoen' Giambelluca,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-22 15:54:36

To dobra praktyka. Nie musisz myśleć o podpisie metody i kolejności argumentów. Kolejną zaletą jest to, że możesz łatwo pominąć argumenty, których nie chcesz wprowadzać. Możesz przyjrzeć się frameworkowi ExtJS, ponieważ używa on tego typu argumentów przekazywanych szeroko.

 1
Author: Li0liQ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-18 09:58:39

To kompromis. Tracisz trochę jasności (skąd mam wiedzieć, jakie params przekazać) i sprawdzanie (czy zdałem odpowiednią liczbę argumentów?) i zyskać elastyczność (metoda może domyślne params nie odbiera, możemy wdrożyć nową wersję, która zajmuje więcej params i złamać żaden istniejący kod)

Możesz zobaczyć to pytanie jako część większej dyskusji typu Strong/Weak. Zobacz Steve yegge ' S blog tutaj. Używałem tego stylu w C i c++ w przypadkach, w których chciałem wspierać całkiem elastyczne przekazywanie argumentów. Prawdopodobnie standardowy HTTP GET, z niektórymi parametrami zapytań jest dokładnie takim stylem.

Jeśli pójdziesz do oceny hash powiedziałbym, że musisz upewnić się, że twoje testy są naprawdę dobre, problemy z nieprawidłowo napisanymi nazwami parametrów pojawią się tylko w czasie wykonywania.

 1
Author: djna,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-18 10:17:43

Jestem pewien, że nikogo używającego języków dynamicznych nie obchodzi, ale pomyśl o karze wydajności Twojego programu, kiedy zaczniesz przekazywać hasze do funkcji.

Interpreter może być na tyle inteligentny, aby utworzyć statyczny obiekt skrótu const i odwoływać się do niego tylko za pomocą wskaźnika, jeśli kod używa skrótu ze wszystkimi członkami, które są literałami kodu źródłowego.

Ale jeśli któryś z tych elementów jest zmienną, to hash musi być rekonstruowany za każdym razem, gdy jest dzwoniłem.

Zrobiłem kilka optymalizacji Perla i tego typu rzeczy mogą stać się zauważalne w wewnętrznych pętlach kodu.

Parametry funkcji działają znacznie lepiej.

 1
Author: Zan Lynx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-27 22:40:08

Ogólnie powinniśmy zawsze używać standardowych argumentów, chyba że nie jest to możliwe. Korzystanie z opcji, gdy nie musisz ich używać, jest złą praktyką. Standardowe argumenty są jasne i udokumentowane (jeśli są poprawnie nazwane).

Jednym (i być może jedynym) powodem użycia opcji jest to, że funkcja otrzymuje argumenty, które nie są przetwarzane, ale po prostu przechodzą do innej funkcji.

Oto przykład, który ilustruje, że:

def myfactory(otype, *args)
  if otype == "obj1"
    myobj1(*args)
  elsif otype == "obj2"
    myobj2(*args)
  else
    puts("unknown object")
  end
end

def myobj1(arg11)
  puts("this is myobj1 #{arg11}")
end

def myobj2(arg21, arg22)
  puts("this is myobj2 #{arg21} #{arg22}")
end

W tym przypadku "myfactory" nie jest nawet świadomy argumenty wymagane przez 'myobj1' lub 'myobj2'. 'myfactory' po prostu przekazuje argumenty do 'myobj1' i 'myobj2', a ich obowiązkiem jest ich sprawdzenie i przetworzenie.

 0
Author: Dragan Nikolic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-05 16:01:22

Hasze są szczególnie przydatne do przekazywania wielu opcjonalnych argumentów. Używam hash, na przykład do inicjalizacji klasy, której argumenty są opcjonalne.

Przykład

class Example

def initialize(args = {})

  @code

  code = args[:code] # No error but you have no control of the variable initialization. the default value is supplied by Hash

  @code = args.fetch(:code) # returns IndexError exception if the argument wasn't passed. And the program stops

  # Handling the execption

  begin

     @code = args.fetch(:code)

  rescue 

 @code = 0

  end

end
 0
Author: Leonardo Ostan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-11 15:19:10