Передача хешів замість параметрів методу [закрито]


78

Я бачу, що в Ruby (і динамічно набраних мовах загалом) дуже поширеною практикою є передача хешу, замість того, щоб оголошувати конкретні параметри методу. Наприклад, замість того, щоб оголошувати метод з параметрами і викликати його так:

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

Ви можете зробити це таким чином:

def my_method(options)
my_method({"width" => 400, "height" => 50, "show_border" => false})

Я хотів би знати вашу думку щодо цього. Це хороша чи погана практика, робити ми це чи ні? У якій ситуації застосовується ця практика, і яка ситуація може бути небезпечною?


11
Невелика примітка: {width => 400, height => 50, show_border => false}неприпустимий синтаксис рубіну. Думаю, ви маєте на увазі {:width => 400, :height => 50, :show_border => false}або {width: 400, height: 50, show_border: false}(останнє діє лише в ruby ​​1.9.1)
сарахходне

Відповіді:


28

Обидва підходи мають свої переваги та недоліки, коли ви використовуєте хеш опцій, замінюючи стандартні аргументи, ви втрачаєте чіткість у коді, що визначає метод, але отримуєте чіткість кожного разу, коли ви використовуєте метод, через псевдоіменовані параметри, створені за допомогою хешу параметрів.

Моє загальне правило полягає в тому, що якщо у вас є чимало аргументів для методу (більше 3 або 4), або багато необов’язкових аргументів, тоді використовуйте хеш опцій, інакше використовуйте стандартні аргументи. Однак при використанні хешу параметрів важливо завжди включати коментар із визначенням методу, що описує можливі аргументи.


1
Тепер, якби я мав хеш опцій, чи міг би я просто вкласти весь хеш, чи мені потрібно передавати значення keys => окремо?
FilBot3

1
Це правда, що і ті, і інші мають переваги та недоліки, проте я не погоджуюсь із загальним правилом. Я вважаю, що варіанти є загалом поганою практикою, і їх слід використовувати, лише якщо це єдиний спосіб вирішити проблему.
Драган Ніколич

42

Ruby має неявні хеш-параметри, тому ви також можете писати

def my_method(options = {}) 

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

а з Ruby 1.9 та новим хеш-синтаксисом це може бути

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

Коли функція приймає більше 3-4 параметрів, набагато легше зрозуміти, що є, без підрахунку відповідних позицій.


Наразі з Ruby 2.7 неявні параметри хешу застаріли і будуть видалені в Ruby 3.0 - означає, нам знову потрібно {…} явно передати хеш ( див. ). Наведений тут синтаксис Ruby 1.9 все ще можливий для виклику методу з параметрами ключових слів .
tanius

8

Я б сказав, що якщо ви:

  1. Маючи більше 6 параметрів методу
  2. Передача параметрів, які мають деякі обов’язкові, деякі необов’язкові, а деякі зі значеннями за замовчуванням

Ви, швидше за все, захочете використовувати хеш. Набагато легше зрозуміти, що означають аргументи, не шукаючи документації.

Для тих, хто каже, що важко зрозуміти, які варіанти використовує метод, це просто означає, що код погано документований. За допомогою YARD ви можете використовувати @optionтег, щоб вказати параметри:

##
# 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

Але в цьому конкретному прикладі є настільки мало і простих параметрів, тому я думаю, що я піду з цим:

##
# 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

3

Я думаю, що цей метод передачі параметрів є набагато зрозумілішим, коли існує більше декількох параметрів або коли є кілька необов’язкових параметрів. По суті, це робить виклики методів явно самодокументуючими.


3

У Ruby не прийнято використовувати хеш, а не формальні параметри.

Я думаю, це плутають із загальноприйнятою схемою передачі хешу як параметра, коли параметр може приймати ряд значень, наприклад, встановлення атрибутів вікна у графічному інтерфейсі.

Якщо у вас є кілька аргументів для вашого методу або функції, тоді явно оголосіть їх і передайте. Ви отримуєте перевагу того, що перекладач перевірить, що ви передали всі аргументи.

Не зловживайте мовною функцією, знайте, коли її використовувати, а коли не використовувати.


1
Якщо вам потрібна змінна кількість аргументів, просто використовуйте def method(*args), хеші не вирішують цю конкретну проблему
MBO

1
Дякуємо, що вказали на потенційну плутанину. Я справді думав про випадок, коли підняли оригінальний плакат, де порядок і кількість параметрів є змінними.
Chris McCauley

1
Щоб додати до цього, цей анти-шаблон також відомий як Чарівний контейнер
jtzero

3

Перевага використання Hashпараметра as полягає в тому, що ви видаляєте залежність від кількості та порядку аргументів.

На практиці це означає, що згодом ви матимете можливість переробляти / змінювати метод, не порушуючи сумісність з клієнтським кодом (і це дуже добре при створенні бібліотек, оскільки насправді ви не можете змінити код клієнта).

( "Практичний об’єктно-орієнтований дизайн у Ruby" Сенді Меца - чудова книга, якщо вас цікавить дизайн програмного забезпечення в Ruby)


Хоча ваше твердження відповідає дійсності, я не думаю, що це найкращий спосіб підтримувати сумісність бібліотеки. Ви можете досягти того самого, просто додавши нові параметри як необов’язкові. Вони не порушують сумісність і все ще мають перевагу ясності та самодокументування.
Драган Ніколич

@DraganNikolic Я на борту з Sany Metz речі теж - переваги не маючи організувати список ручних параметрів величезна і є інші способи самостійного документування коду , викладені в книзі
РГЧ - Метт

Інша річ, яку багато хто (включаючи мене!) Часто забувають, це те, що порядок параметрів - це теж залежність! Це означає, що просто використання необов’язкових параметрів означає, що ви не можете змінити порядок параметрів з будь-якої причини (але зазвичай ви все одно не хочете багато аргументів аргументів!)
Aldo 'xoen' Giambelluca

2

Це хороша практика. Вам не потрібно думати про підпис методу та порядок аргументів. Ще одна перевага полягає в тому, що ви можете легко опустити аргументи, які не хочете вводити. Ви можете поглянути на фреймворк ExtJS, оскільки він широко використовує цей тип аргументів.


1

Це компроміс. Ви втрачаєте деяку ясність (звідки я знаю, які параметри передавати) та перевірку (чи я передав потрібну кількість аргументів?) Та отримуєте гнучкість (метод може за замовчуванням отримувати параметри, які він не отримує, ми можемо розгорнути нову версію, яка приймає більше параметрів і не порушувати жодного існуючого коду)

Ви можете побачити це питання як частину обговорення сильного / слабкого типу. Дивіться тут блог Стіва Єгге . Я використовував цей стиль на C та C ++ у випадках, коли хотів підтримати досить гнучку передачу аргументів. Можливо, стандартний HTTP GET, з деякими параметрами запиту - саме цей стиль.

Якщо ви вибрали хеш-апрарах, я б сказав, що вам потрібно переконатися, що ваше тестування справді добре, проблеми з неправильно написаними назвами параметрів з’являться лише під час запуску.


1

Я впевнений, що нікого, хто використовує динамічні мови, не хвилює, але подумайте про покарання за продуктивність, за яку буде вражена ваша програма, коли ви почнете передавати хеші функціям.

Перекладач може , можливо , бути досить розумний , щоб створити статичну константний хеш - об'єкт і тільки посилатися на нього покажчиком, якщо код використовує хеш з усіма членами , які є кодовими літералами джерела.

Але якщо будь-який з цих членів є змінними, тоді хеш повинен відбудовуватися кожного разу, коли він викликається.

Я зробив деякі оптимізації Perl, і подібні речі можуть стати помітними у внутрішніх циклах коду.

Параметри функції працюють набагато краще.


0

Загалом, ми завжди повинні використовувати стандартні аргументи, якщо це неможливо. Використання опцій, коли вам не потрібно їх використовувати, є поганою практикою. Стандартні аргументи чіткі та самодокументовані (якщо вони правильно названі).

Однією (і, можливо, єдиною) причиною використання параметрів є те, що функція отримує аргументи, які не обробляються, а просто передаються іншій функції.

Ось приклад, який ілюструє:

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

У цьому випадку "myfactory" навіть не знає аргументів, необхідних для "myobj1" або "myobj2". "myfactory" просто передає аргументи в "myobj1" і "myobj2", і їх відповідальність - перевіряти та обробляти.


0

Хеші особливо корисні для передачі кількох необов’язкових аргументів. Я використовую хеш, наприклад, для ініціалізації класу, аргументи якого необов’язкові.

ПРИКЛАД

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.