Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Ruby:「プリマドンナメソッド」の臭いの警告を私が受け入れるまで(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

Ruby:「プリマドンナメソッド」の臭いの警告を私が受け入れるまで(翻訳)

まあ聞いてください。もともとこの記事のタイトルは「プリマドンナメソッドの臭いの警告に対抗するには」だったのですが、記事を書き上げる前に、David A. Blackの力強いメッセージを目にしたことで考えを改めました。以下はそこに至るまでに私のたどった旅路です。

これまでの仕事で、私は忠実に「コードの臭い/品質」の標準に厳密に従ってきました。ありとあらゆるプルリクをrubocopreekfastererでパスさせることこそ私にとって無上の幸せです。ときにはワインボトルの封を切ってRuby Criticをかけ、設計上のありとあらゆるまずい決定をあぶり出すことさえありました。

しかし、「正しい」コードの臭いを私が認めなければ一体どうなるでしょう?

「プリマドンナメソッド(Prima Donna method)」というコードの臭いは、少なくともここ10年の間Rubyのコーディング慣習にしっかり組み込まれており、Rubyコード品質チェッカーの主要なコンポーネントとして一般に認識されています。

「プリマドンナメソッド」とは、要は末尾に!の付くメソッドでありながら、それに対応する「!なしメソッド」がないものを指します(Rails開発者なら#save#save!を思い浮かべてください)。

訳注: Prima Donnaは「彼氏募集中=パートナーがいない」の連想かなと思ったりしましたが、社内で「思わせぶりだからなんじゃ?」という意見をもらって、その方がありそうに思えました。ご存じの方はTwitterまでどうぞ🙇。

David A. Blackの名著『The Well Grounded Rubyist』は、プリマドンナメソッドが「コードの臭い」である理由を雄弁に述べています。

!で終わるメソッド名の!とは「このメソッドは危険である」ことを示す。より正確には「このメソッドは、(!が末尾にない)同等の別メソッドの危険なバージョンである」ことを示す。「危険」という言葉は相対的なものであり、!なしの同様のメソッドが!ありのメソッドに対応して存在していなければ、!という記号には何の意味もない。

従って、たとえばgsub!というメソッドはgsubメソッドの危険なバージョンであり、exit!exitの危険なバージョンであり、flatten!flattenの危険なバージョンである、という具合になる。

参考までに、この原則に違反する例は次のようになります。

class Foo

  def bar!
    puts 'Bar!'
  end

end

違反しない同等のコードは以下のようになります。

class Foo

  def bar
    puts 'Bar!'
  end

  def bar!
    puts 'Bar!'
  end

end

このコードの臭いは、メソッドの内容については一切関知していないことにもご注目ください。これは!付きのメソッド名が送信されていることに警鐘を鳴らしているに過ぎません。開発者はプログラミング経験を改善する共通言語を必要としていますし、メソッドがどのように命名されるべきかについて一定の期待を持つものです。

しかし、ワインを満載した我がRuby Criticのとあるセッションで、コードベースのいくつかのメソッドから違反が発見されました。それらの違反は、以下のいずれかの解決方法を必要としていました。

  1. 使いもしない(!なしの)相方メソッドを作成する
  2. yaml設定を変えて、この臭いを検出するlinterを無視する
  3. メソッド名に!を付けないようにする

しかしここが問題でした。そのときの私には「どの解決法にしたところでコードは少しも改善されないじゃないか」と思えました。臭いを無視することは、それ自体が私にとって臭いですし、意味のないメソッドを追加することも同様です。メソッド名を変更すると、私にとってのメソッド名のニュアンスが失われてしまう可能性がありました。

!に関する現状の方言チックな警告は、私にとってますます「新種の」危険な振る舞いに思えてきました。私には、以下のメソッドを分割する価値があるとは思えませんでした。

class Purchase < ApplicationRecord
  def void!
    # #update_columnを使うべきかどうかという話はまた別の記事で...
    update_column(:void, true)
  end
end

私はこのメソッド名のままで十分明快だと思いましたし、!記号が付いていれば今後「データベースを変更する操作である」ことが臨時の開発者にも伝わるだろうと思いました。そういったわけで、私の新発見と意見をコミュニティに伝えてみたのです。しかし、Black氏からの以下の的確なメッセージを目にしたとき、はっとさせられました。

破壊的なメソッド名だという理由で!を付けてはいけません。メソッド名に!を付けていいのは、その変更が本当に「危険」であり、かつそれと同等な「危険でない」!なしメソッドも存在する場合に限られます。破壊的なメソッド群が気まぐれに!で終わったり終わってなかったりと一貫していなければ、!の意味がずれ、ぼやけてしまいます。そしてしまいには、!の意味が完璧に失われてしまうでしょう。

破壊的メソッドを書いたときに、メソッド名だけではその危険性をうまく伝えられないことに気づくと、つい!を付けて意味を明確にしたい誘惑にかられるかもしれません。しかしそのやり方はダメです。破壊的メソッド名に!を付けないと破壊的であることがうまく伝わらないとしたら、それはそのメソッド名が元からダメなのであり、!を貼り付けてごまかしてはなりません。

こうした批判や「コードの臭い」の警告を「が間違ってる...だと?俺もプロなんだけどな!」と自分勝手に解釈するのは簡単です。しかしながら、ときには気持ちの高ぶりをぐっとこらえ、自分のコードスニペットが臭いを放つのを目にしたときの激情を冷静に再検討する価値はあります。

当初私の記事で鳴らそうと思っていた「警鐘」とは違うものになりました。「どんなメソッドでも破壊的になる可能性がある」のです。!は、既存のメソッドの破壊的なバージョンであることだけを伝えるためのものです。

関連記事

RailsのObject#tryがダメな理由と効果的な代替手段(翻訳)

Railsアンチパターン: Decoratorの肥大化(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。