메소드 매개 변수 대신 해시 전달 [닫힘]


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}(후자는 루비 1.9.1에서만 유효)
sarahhodne

답변:


28

두 방법 모두 고유 한 장점과 단점이 있습니다. 표준 인수를 대체하는 옵션 해시를 사용하면 메서드를 정의하는 코드에서 명확성을 잃지 만 옵션 해시를 사용하여 생성 된 의사 이름 매개 변수로 인해 메서드를 사용할 때마다 명확성을 얻습니다.

내 일반적인 규칙은 메서드에 대한 인수가 많거나 (3 개 또는 4 개 이상) 선택적 인수가 많은 경우 옵션 해시를 사용하고 그렇지 않으면 표준 인수를 사용하는 것입니다. 그러나 옵션 해시를 사용할 때 가능한 인수를 설명하는 메서드 정의에 항상 주석을 포함하는 것이 중요합니다.


1
이제 옵션 해시를 사용하려면 전체 해시를 넣을 수 있습니까? 아니면 키 => 값을 개별적으로 전달해야합니까?
FilBot3 2013

1
둘 다 장점과 단점이 있다는 것은 사실이지만 일반적인 규칙에 동의하지 않습니다. 나는 옵션이 일반적으로 나쁜 관행이며 이것이 문제를 해결하는 유일한 방법 인 경우에만 사용해야한다고 생각합니다.
Dragan Nikolic

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

루비에서는 형식 매개 변수가 아닌 해시를 사용하는 것이 일반적이지 않습니다.

매개 변수가 GUI 툴킷에서 창 속성을 설정하는 것과 같이 여러 값을 취할 수있을 때 해시를 매개 변수로 전달하는 일반적인 패턴과 혼동되는같습니다 .

메서드 또는 함수에 여러 인수가있는 경우 명시 적으로 선언하고 전달합니다. 통역사가 모든 인수를 통과했는지 확인하는 이점이 있습니다.

언어 기능을 남용하지 말고 사용시기와 사용하지 않아야 할시기를 알고 있어야합니다.


1
가변 개수의 인수가 필요한 경우를 사용하십시오 def method(*args). 해시는 특정 문제를 해결하지 못합니다
MBO

1
잠재적 인 혼란을 지적 해 주셔서 감사합니다. 나는 매개 변수의 순서와 개수가 가변적 인 곳에서 원래 포스터가 제기 된 경우를 정말로 생각하고 있었다.
Chris McCauley

1
그 점에 덧붙여, 그 안티 패턴은 Magic Container
jtzero

3

Hashas 매개 변수 사용의 이점 은 인수의 수와 순서에 대한 종속성을 제거한다는 것입니다.

실제로 이것은 나중에 클라이언트 코드와의 호환성을 깨지 않고 메서드를 리팩터링 / 변경할 수있는 유연성을 갖게됨을 의미합니다 (실제로 클라이언트 코드를 변경할 수 없기 때문에 라이브러리를 빌드 할 때 매우 좋습니다).

(Sandy Metz의 "Ruby의 실용적인 객체 지향 디자인"Ruby의 소프트웨어 디자인에 관심이 있다면 훌륭한 책입니다.)


귀하의 진술은 사실이지만 라이브러리 호환성을 유지하는 가장 좋은 방법은 없다고 생각합니다. 옵션으로 새 매개 변수를 추가하기 만하면 동일한 결과를 얻을 수 있습니다. 호환성을 깨지 않으며 여전히 명확성과 자체 문서화의 이점이 있습니다.
Dragan Nikolic 2016

@DraganNikolic 저도 sany metz에 참여하고 있습니다. 수동 매개 변수 목록을 정렬하지 않아도되는 이점은 엄청납니다. 책에 나와있는 자체 문서화 코드의 다른 방법이 있습니다
Mirv-Matt

많은 사람들이 (저를 포함해서!) 자주 잊는 또 다른 점은 매개 변수의 순서도 종속성이라는 것입니다! 이것은 단지 선택적인 매개 변수를 사용한다는 것은 어떤 이유로 든 매개 변수의 순서를 변경할 수 없다는 것을 의미합니다 (그러나 일반적으로 어쨌든 많은 인수 인수를 원하지 않습니다!)
Aldo 'xoen'Giambelluca

2

좋은 습관입니다. 메서드 시그니처와 인수 순서에 대해 생각할 필요가 없습니다. 또 다른 장점은 입력하고 싶지 않은 인수를 쉽게 생략 할 수 있다는 것입니다. 광범위하게 전달되는 이러한 유형의 인수를 사용하므로 ExtJS 프레임 워크를 살펴볼 수 있습니다.


1

트레이드 오프입니다. 명확성을 잃고 (어떤 매개 변수를 전달할지 어떻게 알 수 있습니까) 확인하고 (올바른 수의 인수를 전달 했습니까?) 유연성을 얻습니다 (메소드는 수신하지 않는 기본 매개 변수를 사용할 수 있습니다. 더 많은 매개 변수 및 기존 코드 중단)

이 질문은 더 큰 강 / 약 유형 토론의 일부로 볼 수 있습니다. 여기 에서 Steve yegge의 블로그를 참조 하십시오. 매우 유연한 인수 전달을 지원하려는 경우 C 및 C ++에서이 스타일을 사용했습니다. 일부 쿼리 매개 변수가있는 표준 HTTP GET이 바로이 스타일입니다.

해시 방식을 사용하는 경우 테스트가 정말 좋은지 확인해야한다고 말하고 싶습니다. 잘못된 철자 매개 변수 이름 문제는 런타임에만 나타납니다.


1

동적 언어를 사용하는 사람은 아무도 신경 쓰지 않지만 해시를 함수에 전달하기 시작할 때 프로그램이 타격을 입을 성능 저하에 대해 생각하십시오.

코드가 소스 코드 리터럴 인 모든 멤버와 함께 해시를 사용하는 경우 인터프리터는 정적 const 해시 개체를 만들고 포인터로만 참조 할 수 있을 만큼 똑똑 할 있습니다.

그러나 이러한 멤버 중 하나라도 변수이면 호출 될 때마다 해시를 재구성해야합니다.

몇 가지 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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.