Refactoring, Second Edition
情報
Martin Fowler(と一部Kent Beck)による名著の第2版
例に使用されている言語はJavaScriptになり、それが話題になった
著者がCanonical versionだと言っているWeb Editionを読んだ
Preface
18年経ってほぼ全てのページを書き変えたが本質は変わっていない
What's in This Book?
JavaScript Examples
JavaScriptを選んだのはそれ自身を推しているからではなく、広く利用されているから
どの言語にも応用できる書き方をしている
Acknowledgments
tommy.icon 初版はKent Beckが書いてたほうが良い本になってたかもと書いてあって謙虚だと思った
Chapter 1 — Refactoring: A First Example
原則から始めず例から始める
The Starting Point
機能を追加する対象のコードが適切に構造化されていない場合、まずは機能を追加しやすくリファクターする
変更こそがリファクタリングに駆り立てる
The First Step in Refactoring
リファクターの前にしっかりしたテストがあること
Decomposing the statement Function
小さなステップでリファクターしていき、テストを繰り返す
著者はリファクターに成功するたびコミットし、プッシュする前にsquashする
tommy.icon このテクニック、リファクタリングに限らず使ったほうがいいな
ローカル変数を削除することで抽出を容易にできる
一時変数は自身のルーチンでしか使われないため長く複雑なルーチンを作りがち
forループを繰り返す場合など、パフォーマンスに悪影響しそうに思えるが、良くリファクターされたコードはパフォーマンスを上げるのも容易になる
まずはパフォーマンスは無視してリファクターし、そのあとチューニングを行うと良い
Chapter 2 — Principles in Refactoring
Defining Refactoring
「リファクタリング」という用語の定義
名詞: 目に見える振る舞いを変えずにソフトウェアの内部構造を理解、修正しやすくする変更
動詞: 目に見える振る舞いを変えずに一連のリファクタリングを適用してソフトウェアを再構築する
リファクタリングは小さなステップの連続で行う
The Two Hats
機能追加とリファクタリングは交互に行う
Why Should We Refactor?
コードを少なく重複を無くすことでソフトウェアの設計をきれいにする
将来の他の人や自分のためにリファクタリングにより理解しやすいコードにしておく
バグを見つけやすくする
I’m not a great programmer; I’m just a good programmer with great habits. (Kent Beck)
リファクタリングにより、理解しやすいコードになり機能追加を容易にし、バグを生みにくくデバッグしやすくなる。そうすることでソフトウェアの「スタミナ」を増すことができ、結果的にプログラミングの速度を速くすることができる
When Should We Refactor?
Don Robertsの「The Rule of Three」ガイドライン
最初にやる時はただやればよい。二度目に同様のことをやるときは重複に気づきつつもやればよい。三度目に同様のことをやるときはリファクタリングする。
新機能を追加する時に既存のコードの構造を改善するリファクタリングを行う
コードの理解はずっと続かないので頭の中からコード自身に理解を移す
他に優先度の高いタスクがある時でも、少しでも良くするとよい
リファクタリングは小さなステップで行うものなので少しずつの改善も可能
リファクタリングのみを計画的に行うというケースは少ない
リファクタリングを別コミットにするという主義には著者は懐疑的
依存するライブラリを書き換える時なども、まずは利用する側が抽象化されたインターフェイスを利用するようにすることで、移行もしやすくなる
リファクタリングによりレビューの効果も大きくなる
リファクタリングのレビューは元コードの作者にレビューしてもらうとよい
技術に明るくないマネージャーに対してはリファクタリングの実施については「言わない」
リファクタリングをすべきでない時
APIとして使っているものが散らかったコードの時
使い方だけ理解し、そのまま利用すればよい
作り直したほうがいい時
Problems with Refactoring
過剰なリファクタリングよりは過少なリファクタリングの方が多い
リファクタリングはコードを綺麗にしたり、良いエンジニアリング実践というわけではなく、機能追加やデバッグを速くする経済的なものである
インターフェイスの変更など、コードの所有について問題になることがあるが、著者は所有を個人にするのではなく、チームにすることを推奨する
リファクターしやすいようフィーチャーブランチを使うことに関しては、短い間隔でメインラインと統合する(メインブランチを自分のブランチ取り込み自分のブランチをメインラインに取り込む)ことを著者は好む
よく設計されていないコードにテストを追加するための参考文献としてレガシーコード改善ガイドをガイドにする
tommy.icon 読まなきゃ
大きいシステムの場合、より多くの恩恵を受けるためよく訪れる範囲をリファクターする
Refactoring, Architecture, and Yagni
アーキテクチャはリファクタリングにより事前に全て行わずともイテレートすることができる
Refactoring and the Wider Software Development Process
リファクタリングの第一の基礎はセルフテストコードである
セルフテストコード、CI、リファクタリングの三本柱でYagni設計のアプローチが実現できる
Refactoring and Performance
パフォーマンスは推測するものではなく計測するもの
パフォーマンスのホットスポットになっている部分に集中する
Chapter 3 — Bad Smells in Code
この章はKent Beckとの共著
tommy.icon 面白いジョークは自分のでそうじゃないのはKent Beckのだと書いてあって笑った
Mysterious Name
いい名前付けが思い浮かばない時は設計の欠陥のサイン
Long Function
コメントを書くということをする前に、良い名前の関数に分けることで自身を説明できないか考える
Comments
コメントが全て悪いのではなく、悪いコードの臭いを隠すためのデオドラントとして使われていることが多いことが問題
コメントを書く前にリファクタリングで必要ないように出来ないか考える
Chapter 4 — Building Tests
A First Test
コードに一時的に欠陥を入れるなどして、1回はそれぞれのテストを失敗させることで以降の安心感を得る
Modifying the Fixture
setup-exercice-verify
setup: 初期標準フィクスチャーを得る
exercise: フィクスチャーをテストで使用する
verify: フィクスチャーがやるべきことをやっていることを確認する
Chapter 6 — A First Set of Refactorings
Extract Function
Kent Beckはテキストや画像をハイライトするため、reverseというメソッドを呼び出すだけのhightlightというメソッドを作っていた
コードの意図が解りやすくなるのであれば厭わない
良い名前付けが重要
2つ以上の値を返さなければいけなくなった場合
それぞれの値にそれぞれの関数を与える
一時変数をReplace Temp with Query、Split Variableで分ける
Inline Function
複雑な場合、1行ずつカット&ペーストするなど小さなステップで行う
Extract Variable
デバッグの時にもフックしやすくなる
式に名前を与える
Change Function Declaration
関数はソフトウェアのジョイント部分である
名前が最重要
パブリックAPIの名前変更などの際は、いったん新しく名前変更した関数を抽出し、元の関数からそれを呼び出すようにして、移行が済んでから元の関数を削除する
Encapsulate Variable
データは関数に比べ操作しづらい
関数は元の関数を転送する関数として保ったまま名前変更したり移動したりすることができる
データではそれができない
ミュータブルなデータはカプセル化する
Combine Functions into Class
Combine Functions into Transformのどちらを利用した方がいいかはプログラムのコンテクストによる
Combine Functions into Transform
コードの中で元データが更新されるならCombine Functions into Classを利用した方が良い
イミュータブルデータ構造の言語では発生しないが、値の変更により一貫性の無いデータになってしまうことがある
Split Phase
違うステージで違うデータと関数の組み合わせを使っている場合、別々のモジュールにすることで違いを明示的にできる
Chapter 7 — Encapsulation
Encapsulate Record
ミュータブルなデータを保存する時はオブジェクトの方が適している場合が多い
フィールド名を徐々に変更できる
Encapsulate Collection
クラスの仲介を挟まずゲッターで取得したコレクションを変更できることを読み込み専用プロキシーやコピーを渡すことで防ぐ
Replace Primitive with Object
著者は簡単なプリント以上のものが必要になった時、そのデータのためのクラスを作ることが多い
Replace Temp with Query
クラスの中で使うことで効力をより発揮する
抽出するメソッドに同じコンテクストを提供できるため
Extract Class
クラスが大きくなりすぎた時、理解を助けるため分割することを考える
Inline Class
2つのクラスの機能の割り当てを変更したい時にまず1つのクラスにまとめたあとに分割するということもできる
Delete the source class and hold a short, simple funeral service.
tommy.icon ところどころこういうジョークを挟んでるのが息抜きになる
Hide Delegate
デリゲートを参照するサーバーを参照するクライアントが多数ある場合にデリゲートに変更があった時、影響をサーバーのみに限定できる
Remove Middle Man
Hide Delegateではデリゲートに新機能が追加される度にサーバーに委譲メソッドを追加する必要がある
そういった場面で煩わしさを解消するために適用
Chapter 8 — Moving Features
Move Function
より適したコンテクストに属させるために関数を移動する
Move Field
プログラムのベースになるのはデータ構造
データ構造を最初から正しく設計することは難しいため変更が必要ならリファクタリングする
Move Statements into Function
複数箇所に重複コードを見つけたら呼び出し先の1箇所にまとめられないか考える
Move Statements to Callers
異なる振る舞いが要求された場合、関数から呼び出し先に移動する
Slide Statements
Extract Functionの前準備として使うことが多い
副作用のないコードにすることでコードを自由に並び替えることができる
Split Loop
2つの異なることを一気にやろうとしているループに対して適用する
Extracrt Functionに続くことが多い
Remove Dead Code
不要なコードをコメントアウトする習慣があったが、バージョン管理システムが広く使われている現在では不必要
Chapter 9 — Organizing Data
Split Variable
1つより多くの責任を持つ変数は複数の変数で置き換えるべき
Rename Field
Fred Brooksの格言にあるようにコードの理解にはデータ構造が鍵となる
Replace Derived Variable with Query
簡単に計算できるデータについては変数を削除して計算にした方がデータの意味を明確にする場合が多い
Change Reference to Value
インナーオブジェクトをバリューオブジェクトに置き換えることで扱いやすくする
Change Value to Reference
複数のレコードが同じデータ構造にリンクしている場合があり、その共有するデータ構造の更新が必要な場合に参照にする
Replace Magic Literal
定数が比較に使われる場合は関数にすることも考える
Chapter 10 — Simplifying Conditional Logic
Decompose Conditional
条件チェックだけでなくそのアクションが複雑であると意図を隠してしまう
Consolidate Conditional Expression
複数の異なる条件チェックを関数抽出しつつまとめることができる
Replace Nested Conditional with Guard Clauses
if elseであればif、elseそれぞれに続くものに同じ比重を与えるが、Guard Clauseでは通常でないことが発生した時に何かやったあと関数を出るという意図を強調する
Replace Conditional with Polymorphism
いくつかの種類に分けられる時、ベースケースとそのバリエーションに分けられる時などに使う
Introduce Special Case
nullも含めた特殊なケースについて同じ処理を行う時に使う
Introduce Assertion
アサーションはエラーを発見するのにも役立つが、コードを読む人とのコミュニケーションに役立つ
Replace Control Flag with Break
return文が関数内で複数存在べきでないと主張する人もいるが、関数でやることが終わった時はreturnする方が良い
Chapter 11 — Refactoring APIs
Separate Query from Modifier
ほとんどの場合、値を返すものと観測可能な副作用が伴うものは分離した方が良い(Command Query Separation)
Parameterize Function
異なるリテラル値で似通ったロジックの関数があった場合、引数化して単一関数にする
Remove Flag Argument
フラグ引数を持つ関数は何が利用可能なのかや、真偽値の場合それが何を意味するのか考えなければならない
Preserve Whole Object
レコードの一部ではなく全体を取ることで将来他のデータを利用できるようにし、引数の数も減らすことができる
Replace Parameter with Query
関数の呼び出し側にとって楽が出来るようにする
Replace Query with Parameter
関数のボディ内で参照透過性が得られないスコープの要素にアクセスしている場合、引数化して関数の呼び出し側に責任を移す
Remove Setting Method
フィールドがコンストラクターでのみセットされるようにし、イミュータブルであることを明白にする
Replace Constructor with Factory Function
コンストラクターはサブクラスなどを生成できなかったり、名前が固定されているといった制限がある
Replace Function with Command
コマンドではアンドゥ、多彩なライフサイクル、カスタマイズといったことを実現できる
著者は95%のケースではコマンドではなく関数を選択する
Replace Command with Function
コマンドが十分に複雑でない場合に関数化する
Return Modified Value
データの変更を追うのは大変なのでデータの更新が明確に示されているようにする
Replace Error Code with Exception
プログラムの終了に置き換えても良いケースで例外を利用する
Replace Exception with Precheck
予期しないエラーでない時には例外を投げるのではなく条件をチェックすべき
Chapter 12 — Dealing with Inheritance
Pull Up Method
重複を排除する
Pull Up Field
データの重複を排除し、それを利用する振る舞いもスーパークラスに移動できる
Pull Up Constructor Body
まず関数の抽出を行うと良い
Push Down Method
メソッドがサブクラスのうち1つか少数にしか関連しない場合に行う
Push Down Field
フィールドがサブクラスのうち1つか少数にしか関連しない場合に行う
Replace Type Code with Subclasses
サブクラスを利用することでポリモーフィズムや異なる振る舞いをさせることができる
間接的にサブクラスを利用する場合もある
Remove Subclass
システムが成長するにつれサブクラスの存在が必要なくなる場合がある
Extract Superclass
プログラムの成長過程で共通要素を見つけ継承について気づく時がある
Collapse Hierarchy
クラスとその親クラスに差異がない場合に統合する
Replace Subclass with Delegate
継承の問題
1つ軸でのバリエーションしか作れない
クラス間の関連を強く持たせてしまう
"Favor object composition over class inheritance" という言葉があるが、継承は委譲に変更できるため、ほとんどの場合でまずは継承を活用する
Replace Superclass with Delegate
スーパークラスの関数がサブクラスで意味を成さない場合は継承すべきでないサイン
全体の感想
Martin FowlerというとMicroservicesの発案者だったりするので小難しい文章を書く人なのかなと思っていたけど、平易な文章で解りやすく書いてあって意外だった
原則から始めず実践例から始めていて頭に入りやすかった
テスト駆動開発 (本)も同じ構成だった
最初から完璧なコードはないし、完璧にたどり着くことはないけど、リファクタリングを重ねてイテレートしていくことでより良いコードを目指すことが大事と思った
使えるところでは出来るだけイミュータブルなデータを使うことを推奨していて意識しようと思った
パフォーマンスは後から考えるというのはKent Beckの"Make it work, make it right, make it fast. "に通ずると思った
#本
#技術書
#プログラミング
#JavaScript