スタメンでエンジニアをしている田中です。
皆さんはJay Fieldsの著「リファクタリング:Rubyエディション」という本をご存じでしょうか? リファクタリングにおける有名な本として、Martin Fowlerの著「リファクタリング」という本はご存じの方が多いかと思います。 こちらは当時広く知られていたJava(初版), JavaScript(第二版)でサンプルコードが書かれていました。
「リファクタリング:Rubyエディション」は原著である「リファクタリング」をRubyに対応させたもので、Rubyの特徴を活かしたリファクタリングの方法が載っており、一度は読んでみたいと思っていました。しかしながら、こちらの本は現在絶版のため入手することが難しいです。
今回、縁があってこちらの本をお借りする機会がありましたので、その内容についてご紹介します。
リファクタリングをする上で大切なこと
いくつか良いなと思った文章があったので引用の形でご紹介します。
リファクタリングについて
リファクタリングとは、コードの外からふるまいを変えずに、内部構造を改良するようにして、ソフトウェアシステムを変えていくプロセスである。
コンピュータが理解できるコードなら誰でも書ける。優れたプログラマが書くのは、人間が理解できるコードだ。
あなたがリファクタリングをしているのは、真実や美を追求するためではない。世界をわかりやすくして、ばたばたと揺れ動いているプログラムの支配権を再び握るために努力しているのである。
テストについて
リファクタリングを始める前に、しっかりとしたテストが必要だ。
リファクタリングではテストが命綱になる。リファクタリングが成功するかどうかはよいテストが用意できているかどうかによって左右される。
パフォーマンスとの兼ね合い
リファクタリングをしているときはわかりやすくすることに集中すべきで、パフォーマンスをあげることはその後で別の仕事として行うのが正しい方法である。
大切なことは
- ふるまいを変えない
- ふるまいを保証するためにテストを書く
- パフォーマンスはまた別のタスクとして考える
私自身はリファクタリングとパフォーマンスの役割の違いについては特に意識しないといけないと思いました。リファクタリング時にパフォーマンスも併せて考えてしまっていましたが、そもそもの目的が異なるので書くべきコードは違うものです。まずはリファクタリングによって分かりやすくした上で必要に応じてパフォーマンスチューニングといった流れを意識していきたいです。
具体的な手法
数多くの方法が紹介されていますが、ここでは3つに絞って紹介します。
サンドイッチメソッドの抽出
ほぼ同じ内容のコードのメソッドが2つあり、それらの違いがメソッドのちょうど中頃のみの場合に、重複部分を抽出してブロック付きのメソッドにする。
サンプル
変更前
def charge(amount, credit_card_number) begin connection = CreditCardServer.connect(...) connection.send(amount, credit_card_number) rescue IOError => e Logger.log "Could not submit order #{@order_number} to the server: #{e}" return nil ensure connection.close end end
変更後
def charge(amount, credit_card_number) connect do |connection| connection.send(amount, credit_card_number) end end def connect begin connection = CreditCardServer.connect(...) yield connection rescue IOError => e Logger.log "Could not submit order #{@order_number} to the server: #{e}" return nil ensure connection.close end end
前処理と後処理が必要なメソッドで具体的な処理の中身を変えたい場合に活用できそうだと感じました。ブロックを利用した書き方がRubyっぽく、またこの書き方が私自身あまり使いこなせていないと思ったのでご紹介しました。
条件分岐の組み換え
Rubyではnilガードという書き方があるので、三項演算子で書かずにRubyらしい書き方をしようということでした。この他にもRubyで提供されている記法に則ることでよりシンプルに書けそうだと思いました。
サンプル
変更前
paraneters = params ? params : []
変更後
parameters = params || []
他の言語を経験していると、ついRubyらしさを忘れた書き方をしてしまいがちなので、気をつけないといけないと思いました。
条件分岐のネストからガード節へ
正常な実行経路が分かりにくい条件分岐を持つメソッドがある場合に、すべての特殊条件をガード節で処理する。
サンプル
変更前
def pay_amount if @dead result = dead_amount else if @separated result = separated_amount else if @retired result = retired_amount else result = normal_amount end end end return result; end
変更後
def pay_amount return dead_amount if @dead return separated_amount if @separated return retired_amount if @retired normal_amount end
早期returnと呼ばれている方法ですが、全てのケースにて適用して良いというわけではありません。 たとえば、条件分岐があるときに両方とも正常なふるまいである場合はガード節へ移動してはいけません。 あくまで、異常系(特殊条件)は早期returnしようという考えのもとで上記のようなリファクタリングを行う必要があるのだと思いました。
おわりに
リファクタリングとRubyにおける具体的な方法について、「リファクタリング:Rubyエディション」をもとにご紹介しました。Rubyらしさを活かした書き方が多くあり参考になりました。今回いくつかの方法をご紹介しましたが、その他にも様々な方法が載っています。しかし、今の私にすべての方法が必要かと言われるとそうとは言い切れません。扱うソフトウェアの規模の大きさや状態によって、何が必要かは変わってくるものだと思います。必要になる機会は今後必ずあると思うので、そのときに再度手にとって読み返したいと思います。
ただ、冒頭にもお伝えしたとおり、こちらの本は現在絶版のため入手することがとても難しいです。復刊ドットコムというサイトにてリクエストをすると、リクエスト数によっては復刊されることもあるそうなので、ご興味ある方はリクエストしていただけると幸いです。(こちらからどうぞ)
本記事を執筆中に復刊されました!!!元々の販売価格よりも高めではありますが、これまでなかなか手に入らなかったことを考えると新品でこの価格はかなりお買い得に感じます!!(私は注文しました!!) ご興味ある方はこちらからどうぞ!!!
最後になりますが、株式会社スタメンでは一緒に働くエンジニアを募集しています。ご興味のある方はぜひエンジニア採用サイトをご覧ください。
Photo by João Silas on Unsplash