サカナ未遂

プログラミング、筋トレ、子育て

リファクタリング:Rubyエディション 6章

引き続き読んでいく

3章〜5章は座学的な感じなのでざっと読んだ。 6章から、サンプルコードの改良するための手法の説明になる。

6章 メソッドの構成方法

メソッドの抽出(Extract Method)

コードの断片をメソッドにして、その目的を説明する名前をつける

メソッドの粒度が細かく出来ていれば、他のメソッドからそのメソッドを使える可能性が高くなる。 メソッドを抽出することによってコードがわかりやすくなるなら、抽出されるコードよりも名前のほうが長くてもメソッドを抽出すべき。

ソースコードのコメントはメソッドを見分けるのに使える。 コメント自体が抽出するメソッドの名前の候補になる。

メソッド抽出する対象のコードに、「ローカル変数なし」、「ローカル変数使用」、「ローカル変数への再代入」でそれぞれのケースを考慮する。 - 「ローカル変数なし」簡単 - 「ローカル変数使用」引数を渡す - 「ローカル変数への再代入」変数が抽出したコードより後で使われている場合は戻り値として返す。

メソッドのインライン化(Inline Method)

メソッドの中身が、メソッド名と同じくらいわかりやすい場合は、いちいちメソッド化しなくてよい。

一時変数のインライン化(Inline Temp)

一度だけ代入されてる一時変数は取り除いて式に直す。

一時変数から問い合わせメソッドへ(Replace Temp with Query)

式をメソッドにする。一時変数のすべての参照箇所を式に置き換える。

一時変数は、使われているメソッドのコンテキストの中でしか参照できない、一時変数にアクセスするにはメソッドを長くするしかないので、メソッドの長大化を助長してしまう。

一時変数からチェインへ(Replace Temp with Chain)

チェイニングをサポートするようにメソッドを書き換えて、一時変数を不要にする。 1つのオブジェクトの表現力を高める。

説明用変数の導入(Introduce Explaining Variable)

処理の目的を説明するような名前を持つ一時変数に式、または式の一部の結果を代入する。

確かに処理がわかりやすくなるがReplace Temp with Queryとぶつかりそう??? と思ったらちゃんと書いてた。 「説明変数の導入なら、メソッドの抽出を選ぶ」

一時変数の分割(Split Temporary Variable)

一時変数を使い回さない。代入ごとに別々の一時変数を用意する。 使い回すと変数は複数の仕事をかかえている兆候になる。

引数への代入の除去(Remove Assignments to Parameters)

引数に代入を行っている場合は一時変数にいれる。

メソッドからメソッドオブジェクトへ(Replace Method with Method Object)

ローカル変数の使い方によっては「メソッドの抽出」が適用できない場合、メソッドを独自のオブジェクトに変更し、すべてのローカル変数がそのオブジェクトのインスタンス変数になるようにする。

アルゴリズム変更(Substitute Algorithm)

メソッド本体を新しいアルゴリズムで書き換える

# 変更前
def use_programing_language(langs)
  language_list = []
  langs.each do |lnag|
    if(lang == "Ruby")
      language_list <<lang 
    end
    if(lang == "Java")
      language_list <<lang 
    end
    if(lang == "Javascript")
      language_list <<lang 
    end
  end
  return language_list
end

# 変更後
def use_programing_language(langs)
  langs.select {|lang| %w(Ruby Java Javascript).include? lang }
end

ループからコレクションクロージャメソッドへ(Replace Loop with Collection Closure Method)

ループではなくコレクションクロージャメソッドを使う。

# 変更前
zoo = []
animals.each{ |a| zoo << a.name }


# 変更後
zoo = animals.map(&:name)

サンドウィッチメソッドの抽出(Extract Surrounding Method)

同じようなコードの2つのメソッドで中頃に違いがある場合、重複部分を抽出してブロック付きのメソッドにする。

重複がメソッドの先頭や末尾なら重複を取り除くのはそれほど難しいことではない。 真ん中にある場合はそうもいかないが、Rubyのブロックを使えばうまい具合に重複部分を抽出してメソッドにまとめることができる。

クラスアノテーションの導入(Introduce Class Annotation)

クラス定義からクラスメソッドを呼び出して振る舞いを宣言する。

initializeを隠蔽する?あまり理解できてない。

名前付き引数の導入(Introudce Named Parameter)

引数リストをハッシュに変換して、ハッシュキーを引数の名前として使う。

# 変更前
Class Person
  attr_reader :first_name, :last_name
  
  def initialize(first_name, last_name)
    @first_name = first_name
    @last_name = last_name
  end
end

Person.new("yamada", "taro")


# 変更後
Class Person
  attr_reader :first_name, :last_name
  
  def initialize(params)
    @first_name = params[:first_name]
    @last_name = params[:last_name]
  end
end

Person.new(first_name: "yamada", last_name: "taro")

呼び出し元は綺麗になるが、クラス定義の方はメソッドの必須引数をしるためにメソッド定義を読む必要がある。 ここで「クラスアノテーションの導入」を使う。

そしたら

Class Person
  attr_reader :first_name, :last_name

  hash_initializer :first_name, :last_name

(どのみちメソッド定義読まなあかんのか?)

所感

サンプルコードを用いて、各テーマに沿ってリファクタリングを学べる。 なぜそれが必要なのかという理由と手順が書いてあり、ありがたい。

とにかく一時変数はさけて、小さいメソッドにしていくことが綺麗なコードにできる。 ただ何がなんでも一時変数がだめではなく、引数に代入をさせないようにするときなどは有用。 6章後半はちょっと理解が難しかったが、ここに書いてあると覚えておけば折を見て見返すことができる。