Передача хэшей вместо параметров метода


Я вижу, что в 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 72

11 ответов:

оба подхода имеют свои преимущества и недостатки, Когда вы используете хэш опций, заменяющий стандартные аргументы, вы теряете ясность в коде, определяющем метод, но получаете ясность всякий раз, когда вы используете метод из-за псевдо-именованных параметров, созданных с помощью хэша опций.

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

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 параметров, гораздо легче увидеть, что есть что, не считая соответствующих позиций.

Я бы сказал, что если вы:

  1. имея более 6 параметров метода
  2. передает опции которые имеют некоторые обязательные, некоторые необязательные и некоторые со значениями по умолчанию

вы, скорее всего, хотите использовать хэш. Гораздо проще понять, что означают аргументы, не заглядывая в документацию.

для тех из вас, кто говорит, что трудно понять, какие варианты принимает метод, это просто означает, что код плохо документирован. С двор можно использовать @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

Я думаю, что этот метод передачи параметров намного яснее, когда есть больше, чем несколько параметров или когда есть ряд дополнительных параметров. Он по существу делает вызовы методов явно самодокументированными.

в Ruby не принято использовать хэш, а не формальные параметры.

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

Если у вас есть несколько аргументов для вашего метода или функции, то явно объявить их и передать их. Вы получаете выгоду, которую получит переводчик проверьте, что вы передали все аргументы.

Не злоупотребляйте функцией языка, знайте, когда ее использовать, а когда нет.

преимущества использования Hash в качестве параметра является то, что вы удалите зависимость от количества и порядка аргументов.

на практике это означает, что позже у вас будет гибкость для рефакторинга/изменения вашего метода без нарушения совместимости с клиентским кодом (и это очень хорошо при создании библиотек, потому что вы не можете фактически изменить клиентский код).

(Сэнди Метц "практичный объектно-ориентированный дизайн в Ruby" великая книга если вы заинтересованы в разработке программного обеспечения на Ruby)

Это хорошая практика. Вам не нужно думать о сигнатуре метода и порядок аргументов. Другим преимуществом является то, что вы можете легко опустить аргументы, которые вы не хотите вводить. Вы можете взглянуть на структуру ExtJS, поскольку она широко использует этот тип аргумента.

это компромисс. Вы теряете некоторую ясность (как узнать, какие параметры передать) и проверку (я передал правильное количество аргументов?) и получить гибкость (метод может по умолчанию params он не получает, мы можем развернуть новую версию, которая принимает больше params и не ломать существующий код)

вы можете увидеть этот вопрос как часть более крупного обсуждения сильного / слабого типа. Смотрите Стив yegge это здесь Блог. Я использовал этот стиль в C и C++ в тех случаях, когда я хотел поддержка довольно гибкой передачи аргументов. Возможно, стандартный HTTP GET, с некоторыми параметрами запроса-это именно этот стиль.

Если вы идете на хэш-оценку, я бы сказал, что вам нужно убедиться, что ваше тестирование действительно хорошо, проблемы с неправильно написанными именами параметров будут отображаться только во время выполнения.

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

переводчик может возможно будьте достаточно умны, чтобы создать статический объект хэша const и ссылаться на него только указателем, если код использует хэш со всеми членами, которые являются литералами исходного кода.

но если любой из этих членов являются переменными, то хэш должен быть восстановлен каждый раз это называется.

Я сделал некоторые оптимизации Perl, и такие вещи могут стать заметными во внутренних циклах кода.

параметры функции работают гораздо лучше.

В общем, мы всегда должны использовать стандартные аргументы, если это не представляется возможным. Использование опций, когда вам не нужно их использовать, является плохой практикой. Стандартные аргументы ясны и документированы (если они правильно названы).

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

вот пример, который иллюстрирует это:

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", и это их ответственность, чтобы проверить и обработать их.

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

пример

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