「リファクタリング:Rubyエディション」を読んで

スタメンでエンジニアをしている田中です。

皆さんは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