本記事は Qiita の「テスト駆動開発 Advent Calendar 2020」の12月21日の記事です。
リファクタリングをしたけどコードが壊れていてまともに動かなくなってしまった ことはありませんか?
コードがひどいのはわかっているがリファクタリングの方法がわからない、
あるいはリファクタリングのメリットに対してコストが高すぎると思っていませんか?
これらの多くは、リファクタリングの基本的なテクニックを確認して気を付けることですぐに改善できる部分がたくさんあります。
この記事では、リファクタリングをこれから始める人と始めたばかりの人向けに学習コストが低く効果が高いテクニックをまとめました。
ここで紹介する内容はサッカーでいうところのドリブル、パス、トラップのようなもので、その先で高度なテクニックや戦術を使うようになっても変わらずに必要です。
リファクタリングを学んでいるとコードスメルやデザインパターンが出てきますが、そこに進む前にここで紹介する内容をしっかり身に付けておくのがオススメです。
リファクタリングの定義
具体的なテクニックに入る前にリファクタリングの定義を確認しておきましょう
コードリファクタリングとは、既存のコンピュータコードを外部の挙動を変えずに再構築することです。リファクタリングは、ソフトウェアの機能を維持しつつ、設計、構造、実装を改善することを目的としています。リファクタリングの潜在的な利点としては、コードの可読性の向上や複雑さの軽減などが挙げられます。これらは、ソースコードの保守性を向上させ、よりシンプルで、よりクリーンで、より表現力豊かな内部アーキテクチャやオブジェクトモデルを作成して、拡張性を向上させることができます。
Code Refactoring from Wikipedia
Contents
基本のテクニック
基本の原則
基本のテクニック
Rename
良い名前は、理解しやすさと変更のしやすさを向上させます。また良い名前をつけているとコード内のコメントの多くは不要になりコードの見通しがよくなります。
曖昧な短い名前より多少冗長であっても誤解のない名前の方がいいです。
シンプルな言葉にすると見やすいですが、多義語は避けたほうが無難でしょう。
名前はパッケージ名、クラス名、メソッド名など、文脈に沿ったものにすると余計な修飾語を削っても意味が通じます。
名前は、内部実装を記述する視点と、外部からAPIを呼び出す視点の両方からチェックし、違和感や矛盾がないととても良いです。
ローカルスコープでは、短い変数名でも許容できることもあります。
この辺りはリーダブルコードによくまとまっているのでオススメです。
Extract
Extractには、変数、メソッド、関数、クラスなどを対象にしたものがあります。IDEのショートカットはそれぞれ分かれているかと思います。
変数の抽出では説明変数というテクニックを使います。純粋に動作だけを目的としたコードでは必要ではない変数をあえて作りそこに名前をつけることでプログラムの文脈や全体像をわかりやすくします。
初めて見る複雑なコードを理解する時にはこのテクニックは特に有効で、ブランチを作ってリファクタリングしながら読解していくといいです。無理にマージしようとせず大胆にやってみるのがコツで、理解できたらリファクタリングしたブランチは捨てても構いません。
このようにコードをリファクタリングしながら整えていくと、潜在的なバグや不整合、デッドコードを発見することもよくあります。
またExtractを組み合わせて特定の部分を交換可能にした上で切り出すというテクニックも重要です。これはまず交換可能にしたい部分を先にExtractで切り出してコードの上の方に移動してよけておきます。それからもとの狙いの部分を関数として切り出して交換部品を引数で受け取るようにします。これは実際の開発現場で多用するテクニックなのでズムーズにできるように練習しておくととても役に立ちます。
Inline
インラインとは、Extractで抽出した変数やメソッドを元に戻すことです。
これは一見地味ですがとても重要です。
粘土で何かの形をつくることをイメージしてみてください。少しずつ形を変え行きすぎたら戻してという細かい動きを繰り返して、全体のバランスとあうような大きさと形を決めていくと思います。
この時、彫刻のように一度動かした形をもとに戻せなかったらどうでしょう。相当難易度が違うと思いませんか?
Inlineはソフトウェア開発を粘土にするために必須のプロセスです。Inlineをしないソフトウェア開発は彫刻のようなものです。
間違えたオブジェクト指向で書かれたコードは単純な手続き型プログラミングよりもたちが悪いと聞くことがありますが、Inlineを使えれば心配入りません。戻して整理して再びExtractしていけばいいだけです。
Inlineには注意点が一つあって、コンパイラの機能にもInlineというものがあってこちらの方がメジャーな言葉です。Inline単体で検索するとほぼすべてコンパイラのほうのInlineがヒットします。コンパイラの機能の話と誤解が生じないように気をつけてください。
基本の原則
自動的にチェックする
自動的に継続的にチェックをするとリファクタリングに失敗しなくなります。コードの変更を検知してユニットテストが自動で流れるようになっていれば十分です。
コンパイラが良い感じのメッセージを出してくれる静的型付言語を使っている場合は、常時コンパイルしつつ時々テストというスタイルもバランスがいいです。
駆け出しエンジニアやTDDをやっていない人はハマりがちかと思うので是非試してみてください。
できる限りツールの力に頼る
リファクタリングをするときは、IDEのリファクタリング機能を使いましょう。人間はミスをしますが、機械はミスをしません。
たくさん参照されている部分を変更するときや、多くのパターンを重ねて適用する時に、ミスの確率が小さくても母数が増えるためにミスが発生するようになります。
リファクタリングをしても挙動が変わらないという確信があれば、チェックの手間をかなり減らすことができますが、信頼できなければその分手厚いテストを用意する必要がでてきてしまいます。
組織的な判断をする場合も信用の積み重ねが重要になります。リファクタリングをしてバグが出ないことが続けば、その組織内でリファクタリングをすることのへの抵抗が減っていきます。
また、FormatterやLinterなどの静的解析ツールも自動的に適用されるようにしておくとさらに便利になります。
Formatterは可読性を向上させるだけでなく、リファクタリング時に発生するdiffを減らすことができます。
Linterは、ベストプラクティスのためのリファクタリングや、ピットホールを避けるためのリファクタリングのアイデアを与えてくれます。
また、可能であればSonarQube、CodeGuru、CodeSceneのような自動コードレビューツールを使うと良いでしょう。
最後に
リファクタリング初心者は、2つの原則を知らずにつまずいていることがよくあるようです。
Inlineは世間ではかなり過小評価されていますが、非常に重要です。
この記事でリファクタリングが快適になれば幸いです。
この記事はこちらのブログの翻訳です。ブログの方はちょくちょく加筆修正するつもりですが、こちらへの反映は遅れるかもしれません。英語に抵抗がなければブログの方をもどうぞ。