概要
Ruby のメソッドの引数でデフォルト式を与える場合は、キーワード引数を使いましょう。
キーワード引数を使うことで、デフォルト式によって達成したい目的である、関心の分離が達成され、コードの見通しがよくなります。
def foo(bar = 'baz'); end # 悪い例
def foo(bar: 'baz'); end # 良い例
実際にキチンとした例を見ていきましょう。
悪い例
def hello(from = 'Me', to = 'World')
puts "#{from}: Hello, #{to}!"
end
hello('Me', 'My World') #=> Me: Hello, My World!
hello('Tom') #=> Tom: Hello, World!
良い例
def hello(from: 'Me', to: 'World')
puts "#{from}: Hello, #{to}!"
end
hello(to: 'My World') #=> Me: Hello, My World!
hello(from: 'Tom') #=> Tom: Hello, World!
キーワード引数によるメリットを分解すると、以下の2つに分類できます。
- キーワード引数の場合は、必要な引数だけ指定できる
- キーワード引数の場合は、実引数の意味が分かりやすい
必要な引数だけ指定できる
概要で例としてあげた hello
関数の呼び出し方に注目してみましょう。
自分が作った My World
に対して hello と呼びかけたいことをコードで表現してみましょう
# 悪い例
hello('Me', 'My World')
# 良い例
hello(to: 'My World')
通常の引数を利用した場合は、我々の関心事は、 My World
に対して hello したいということであるのに、 Me
も指定する必要が出てきています。一体、Me
って何のことなのでしょうか?
これでは、関心の分離が出来ていませんね。しかも、メソッド定義を変更した場合にリファクタ
これは、Rubyの仕様上、引数は個数と位置によって識別されている為、逃れられないことです。
一方、キーワード引数を利用した場合は、 My World
のみを指定すればよく、関心の分離が達成されています。
実引数の意味が分かりやすい
概要で例としてあげた hello
関数の呼び出し方に注目してみましょう。
Tom
に Hello, World!
と喋ってもらいたいことをコードで表現してみましょう。
# 悪い例
hello('Tom')
# 良い例
hello(from: 'Tom')
通常の引数の場合は、呼び出し側のコードを見るだけでは、実行される処理が分かりづらいですが、キーワード引数を利用することによって、呼び出し側のコードを見るだけで、何が実行されるかが明確です。
通常の引数では、Tom
に対して hello しているのかな? って勘違いしちゃいますよね。
しかし、通常の引数の場合、メソッド定義としては、 from
, to
という順序で引数を定義するのはごく自然なのです。
どうしてこのような事故が起きてしまうのでしょうか。
まず、一般的に、デフォルト式をもたせるメソッド名は、省略された状態で完全性を担保する必要があります。完全性が担保されるということは、メソッド名にもその完全性は現れます。つまり、 hello_from
というような、目的語を取る複合動詞ではなく、hello
というような、目的語を取らない複合動詞のメソッド名が選択されます。そして、選択されるべきです。この状態では、動詞たるメソッド名と、目的語たる引数の関係性は、メソッドというI/Fからは失われています。
更に、デフォルト式をもたせるメソッドは、デフォルト式を指定されない状態で呼ばれることが殆どです。それは、開発者がコードを書く時に、デフォルト式はコードの関心の外であるためです。しかし、このような状況が継続した場合、開発者の頭の中からはメソッドの定義は消えています。そう、関心の外になっているからです。当然、メソッド定義を読めば良いということですが、これは開発時(特にコードリーディング時)のオーバーヘッドになるので、コードがキチンと処理を表現する状態が望ましいです。
逆に、キーワード引数を利用した場合には、メソッドと引数の関係が from
というキーワードによって明確になるので、メソッド定義に関心を向ける必要がなくなります。また、その関係性が呼び出し側のコードに明示されるため、開発者もメソッド定義まで立ち戻る必要はなくなるのです。
まとめ
通常の引数を使うか、キーワード引数を使うか。
些細な違いが、コードの品質や開発効率に大きな違いを与えてしまうので、デフォルト式としてはキーワード引数を使うことをオススメします。