Try T.M Engineer Blog

多摩市で生息するエンジニアが「アウトプットする事は大事だ」と思って初めたブログ

『リファクタリング 既存のコードを安全に改善する(第2版)』の感想

著者:MartinFowlerさんの本『リファクタリング 既存のコードを安全に改善する(第2版)』を読んだので、その感想エントリーを書いていきたいと思います。

本書の第1版は「Java」で書かれていたのですが、第2版は「JavaScript」で書かれているので、フロントエンドの方でも読みやすくなっていると思います。
*とはいえ、「JavaScript」で説明できない部分(アクセス修飾子の表現等)は、「Java」で書かれているので注意してください。

本書のChapterと感想

本書のChapterは以下の通り、Chapterごとに感想を書いていきます。

Chap.1 リファクタリング-最初の例

サンプルコード(劇団員を派遣して演劇のパフォーマンスを行う会社を想定して、演じた劇に対する請求書を作成するコード)を例に、リファクタリングしていく一連の流れが書かれています。
このChapterを読むだけで、コードをリファクタリングしていく流れを体験できると思います。

私も本書を読むまで知らなかったのですが「いきなり目的に向かってリファクタリングをしても良いコード」と「いきなり目的に向かってリファクタリングをしてはいけないコード」があります。
たとえば、以下のようなサンプルコードがあり、関数名をinOldEngland(c)inNewEngland(c)に変更したいとします。

const newEnglanders = someCustomer.filter(c => inOldEngland(c));

function inOldEngland(aCustomer) {
  return ["MA", "CA", "ME", "VT", "NH", "RI"].includes(aCustomer.address.state);
}

関数名をただ変更するだけなので、直接関数名を変更したくなりますが・・・
ちょっと立ち止まって考えてみましょう!

関数名を変更すると、呼び出し側の関数名も変更する必要があります。
呼び出し側が1つしかないのであれば問題ありませんが、呼び出し側が複数ある場合、いきなり関数名を変更すると変更漏れが発生するかもしれません。
また、関数名が変わることで「引数の見直し」もしたくなるかもしれません。

このような場合、関数名変更後の関数を仮実装(inNewEngland(c)を仮実装)して移行することを考えます。

// Step.1
// 関数名を`inNewEngland(c)`に変更
const newEnglanders = someCustomer.filter(c => inNewEngland(c));

// 関数`inNewEngland`を仮実装する
function inNewEngland(aCustomer) {
  return inOldEngland(aCustomer);
}

function inOldEngland(aCustomer) {
  return ["MA", "CA", "ME", "VT", "NH", "RI"].includes(aCustomer.address.state);
}

引数も見直します。
こちらも段階的に変更します。

// Step.2
const newEnglanders = someCustomer.filter(c => inNewEngland(c));

function inNewEngland(aCustomer) {
  return inOldEngland(aCustomer.address.state);
}

// 引数を`stateCode`に変更。呼び出し側(仮実装側)の引数を変更します。
function inOldEngland(stateCode) {
  return ["MA", "CA", "ME", "VT", "NH", "RI"].includes(stateCode);
}
// Step.3
const newEnglanders = someCustomer.filter(c => inNewEngland(c.address.state));

// 引数を`stateCode`に変更。呼び出し側(実装側)の引数を変更します。
function inNewEngland(stateCode) {
  return inOldEngland(stateCode);
}

function inOldEngland(stateCode) {
  return ["MA", "CA", "ME", "VT", "NH", "RI"].includes(stateCode);
}

上記コードで、ちゃんとテストをして問題ないことを確認してから、仮実装した関数は削除して、以下コードに変更します。

// Step.4
const newEnglanders = someCustomer.filter(c => inNewEngland(c.address.state));

function inNewEngland(stateCode) {
  return ["MA", "CA", "ME", "VT", "NH", "RI"].includes(stateCode);
}

上記は遠回りなリファクタリングの方法です。
「テストをしていれば大丈夫!」と考える人もいると思いますが、「テストに責務を持たせすぎないこと!」というのも、本書では書かれています。
リファクタリングは小さな変更の積み重ねであること!」これが本書でMartinFowlerさんの言いたかった事の1つです。

Chap.2 リファクタリングの原則

リファクタリングの定義、リファクタリングを行う理由、リファクタリングはいつすべき?、問題点など・・・
リファクタリングの原則について書かれています。
やっぱり「IDEを使うのがいいよね」的な話も書かれていました。

Chap.3 コードの不吉な臭い

いわゆるリファクタリングすべき場所を解説している。
このあたりは「不吉な臭い」等でググると書いている記事が多くでてくる。
とはいえ、第1版と第2版ではいくつか追加・削除されたものもあるので注意。

devtab.jp

Chap.4 テストの構築

リファクタリングにはテストが欠かせない・・・というお話。
最近は、開発者にとってテストは関心事の1つになってきているとも書いてありましたね。テストやりましょう・・・・m( )m

Chap.5 カタログの紹介

ここから以下のChapterすべてリファクタリングテクニックのお話。 「うーん、書いてみないとわからんなぁ・・・」という箇所は、雑に写経しながら理解しました。

Chap.6 リファクタリングはじめの一歩

関数と変数に着目したリファクタリングのテクニック集。
- 関数は一画面に収まらなければ、ロジックを詰め込みすぎだと考える - 2回以上使われるコードはそれ自体を関数にすべき! - パラメーターが多すぎるならオブジェクトごと渡す!

などなど、いわゆるリファクタリングの王道パターンに対するテクニック集が記載されていました。

Chap.7 カプセル化

クラスに着目したリファクタリングのテクニック集。
私はあまり意識していなかったのですが、関数からコレクションを返す時は、コピーか読み取り専用にするのが良いとされていますね。
昨今では、このあたりもパフォーマンス的に問題になることは少ないとも書かれていました。
前のChapterでも書かれていましたが「変数・関数に名前を付ける -> 名前以上のことをする処理の場合はクラス化する」これを守っていればよさそうです。
委譲の隠蔽仲介人の除去は使い方が難しいテクニック(= o = ;;)

Chap.8 特性の移動

クラスや関数のロジックの移動に着目したテクニック集。
オブジェクト指向の大原則「データ構造処理を分離しよう」にしたがってリファクタリング(移動)しましょう!という感じですね。
ループの分離なんかで書かれている「1回のループで処理したいという理由だけで、2つの異なる処理を同時に行っているループをよくみる。」というコメントは、イタタタ・・・・

Chap.9 データの再編成

変数、フィールド名やそれ自体の置き換えや変更に着目したテクニック集。
フレッド・ブルックスの名言「フローチャートを見せてくれても、テーブルを隠されたら煙に巻かれたままだろう。テーブルを見せてくれれば通常フローチャートは要らない。それだけですぐわかる」 それだけ、名前って大切なんですね。ところで、フレッド・ブルックスって誰?(= = ;;)注)すごい人です
「変更可能なデータは、ソフトウェアにおける問題の発生源となる。」とのことですね。
話は変わりますが、TypeScriptreadonly修飾子がありますが、これが使えるだけで結構変わりそうです。

Chap.10 条件記述の単純化

条件分岐に着目したテクニック集。
ポリモーフィズムによる条件記述の置き換えは何を言っているのかさっぱりだったので、写経多め・・・
条件分岐にアサーションを入れてコメント代わりに使う方法があった。
Fowlerさんはセルフテストと呼んでいるそうですが、こんな使い方をするのもおもしろいですよね。

Chap.11 APIリファクタリング

関数、関数の引数などの呼び出し側に着目したテクニック集。
個人的にでるかな〜?と思っていた、setterの削除はここで登場。
「関数に渡す引数が、その関数に相応しいかを考えよう」をFowlerさんは責務という言葉を使って「債務が関数側に相応しいかを考えよう」と言っていました。
ファクトリ関数によるコンストラクターの置き換えで、コンストラクターの制限について書いていましたが、正直そんな事まで考えたこともありませんでした。m( )m

Chap.12 継承の取り扱い

最後は継承に着目したテクニック集。
委譲継承を同列で考えたことなんてありませんでした。m( )m
委譲によるサブクラスの置き換え委譲によるスーパークラスの置き換えはぜったい難しい。。。

さいごに

というわけで、雑な写経をしつつ、本を読み進めてみました。
大切なのはChap.3 コードの不吉な臭いを覚えること。そして、それに着目して怪しいコードはリファクタリングしていくこと。
リファクタリングはいつすべきなのか?」という問に対しても、Fowlerさんは「常に」と回答しています。
テクニックだけではなく、リファクタリングは身近なものでなくてはならないことを、この本で教わりました。
最初にも書きましたが、第2版は「JavaScript」で書かれているのですごく読みやすかったです。興味のある方は是非読んでみることをオススメします!!