【Ruby】整数を16進数化するメソッドの作成でリファクタリングに触れる


やりたいこと

自分のコードのリファクタリングがしたい⋯
でもなにから手をつけていいかわからない⋯

そんな気持ちを抱えるなか、現在勉強中の「プロを目指す人のためのRuby入門」にて、メソッドを作成する過程でのリファクタリング手順について触れられていたのでその流れについて自分用のメモ、アウトプットです。

整数を16進数化するために使用するメソッドの作り方

作成イメージ

3つの整数を受け取り、16進数に変換するto_hexメソッドを作成します。

to_hex(0, 0, 0)
# => '#000000'

16進数化メソッドで使用するメソッド

リファクタリングの前に、このメソッドを作成するために使用するメソッドについて学習 ✎

メソッド① to_sメソッド:整数を16進数に変換する

整数を16進数にするためにはto_sメソッドが使える。

0.to_s(16)
# =>'0'
255.to_s(16) 
# => 'ff'

to_sメソッドは、文字列以外のオブジェクトを文字列に変換するメソッドと認識していたけれど、引数を指定することで、指定された文字列表現に変換することもできると初めて知りました。

今回は引数として「16」と指定することで、16進数の文字列に変換しています。

メソッド② rjustメソッド:整数→16進数メソッドの戻り値を2桁の文字列にする

to_s(16)メソッドでは0.to_s(16)としたときの戻り値が1桁なので、期待している'00'といったような2桁の値に変換するためにrjustメソッドを使います。

rjust(width, padding = ' ') 
# => String

# width:返り値の文字列の桁数
# padding:長さが width になるまで self の左側に詰める文字

長さ width の文字列に self を右詰めした文字列を返します。 self の長さが width より長い時には元の文字列の複製を返します。また、第 2 引数 padding を指定したときは空白文字の代わりに padding を詰めます。

# 例
'0'.rjust(5)
# => '     0'   #デフォルトで半角スペースで文字列を埋める。

rjustの"r"は右の"r"か!!!

to_hexメソッドを作る

STEP① to_hexメソッドを作成する

16進数化に使うメソッド①と②を使用して16進数化メソッドを定義するとこんなかんじ。

def to_hex(r, g, b)
  '#' +
  r.to_s(16).rjust(2, '0') +
  g.to_s(16).rjust(2, '0') + 
  b.to_s(16).rjust(2, '0')
end

これまでのわたしはここで終わっていた⋯!

STEP② to_hexメソッドのリファクタリング(繰り返しをなくす)

STEP①で作成したコードは、
to_s(16).rjust(2, '0')で続く3列がRubyのDRYの原則に反しています。
DRY原則に従い重複を取り除くと、こう。

def to_hex(r, g, b)
  hex = '#'
  [r, g, b].each do |n|
    hex += n.to_s(16).rjust(2, '0')
  end
  hex
end

繰り返しがなくなった!
こんな風にeach文を使ってリファクタリングするだなんて、自分の引き出しにはなかったのでとても勉強になります。
こうすることの利点は、コードに変更を加える際などに、何箇所も修正する必要がなくなること!

STEP③ さらにリファクタリング

本の中では、STEP②から更にリファクタリングしています。

def to_hex(r, g, b)
  [r, g, b].inject('#') do |hex, n|
    hex + n.to_s(16).rjust(2, '0')
  end
end

injectメソッドを使ってたたみ込み演算することで、もっとコンパクトになりました。

injectメソッドはすでに本の中で紹介されていますのが、使い方が少しむずかしい。。。
ただ今回のように、「配列の中身を足し合わせたい!!」という状況はこれまでにも何度かあり、そのときにこのメソッドが使えていれば、もっとコードを短くかけていたと思います。これからは積極的に使っていこう!

・ injectメソッドメモ

たたみ込み演算を行うメソッド

numbers = [1, 2, 3, 4]   
sum = numbers.inject(0) {|result, n| result + n}  

・ブロックの第1引数resultへは最初のみ0が入り、2回目以降は前回のブロックの戻り値が入る。
・ブロックの第2引数nは配列の各要素(1, 2, 3, 4)が入る。
・繰り返し処理が最後まで終わるとブロックの戻り値がinjectメソッドの戻り値となる。

終わり

PFを制作するうちにどんどん長くなるコードに罪悪感を感じ、「リファクタリングせねば⋯!」という心の声は聞こえていました。

しかし、具体的にどこから手をつけていいんだろう⋯と、とても腰が重かったのです。
本書では、メソッドを作成する過程でリファクタリングの方法、進め方を解説しており大変勉強になりました。

あくまで、1メソッドのリファクタリングですので、
こういった一つ一つの技を学んで実践で使いながらものにし、息をするようにリファクタリングができるというところを目指したいと思った次第であります。

参考

・ 書籍:「プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで」
Ruby 3.0.0リファレンスマニュアル instance method Integer#inspect
Ruby でマルチバイト文字に対して ljust しても綺麗に揃わない場合