最近の業務ではJava/Spring BootでのWebアプリケーション開発をやっている。 Spring Bootの書籍は読んでいるのだが、もっと基礎的なJava言語での設計についても勉強したかった。 そこで、タイトルに惹かれた「Java言語で学ぶリファクタリング入門」を読んでみることにした。
感想
以下は読書メモ。
この本の内容を読んだ感想としては、内容は「リファクタリング」というレベルのものではなく多くの人が習慣でやっているものが多々あったと思う。 なので、このメモでは自分が勉強になったところのみ抜粋して書く。
0章 リファクタリングとは
リファクタリングとは
- リファクタリングの定義
- 「外部から見たプログラムの振る舞いを変えずに、プログラム内部の構造を改善すること」
- リファクタリングの目的
- バグを見つけやすくする
- 機能追加しやすくする
リファクタリングのエッセンス
ステップ・バイ・ステップ
- 2つの修正を1度にしない。リファクタリングとは「小さいけれども確実な1歩を繰り返して改善する」技術。
- これには共感できる。2つの修正を1度にしていると、修正中に何か不具合に面した時に問題の切り分けがしづらくなる実感がある。
これは最近心がけている作業の進め方だなと思った。
業務でもプライペートでも、コードの管理にはGitを使っている。 Gitの機能を使って小さい変更をしながら、あとでメンバーに共有する直前に差分を整理することができる。
私は以下のように作業を進めている。
自分の場合のステップ・バイ・ステップ
0. featureブランチをつくる
git fetch origin
git checkout -b feature-a origin/development
1. READMEにTODOリストをつくる
変更中には、最初にREADME.md
の末尾に自分用のTODOリストをつくっておく。
* [ ] module-aをmodule-bと連携できるようにする
もしくは、ソースコード中に自分のTODOコメントとわかるように
// TODO: mook: add a function to do ...
のように書いておき、検索コマンドで抽出してTODOリストとしておくのもいいかもしれない。
ag -nr "TODO: mook:" ./src
2. 細かくコミットをつくっておく
以下のように雑にコミットをしていく。
多くても2-3ファイル、5-10行程度の変更で1コミットくらいの感覚で細かくコミットを作っていく。
git commit -m "tmp; refactor fanction a"
git commit -m "tmp; yay! finally the new module worked"
著者の結城さんは、作業Aにとりかかっているときは作業Bにはとりかからないほうがいいとしている。基本的にはそれに従えばよいが、私の場合は気になって気が散ってしまうこともある。そのような時は、小さなtmp;
から始まるコミットメッセージでコミットしてしまう。
小さくコミットをしていくと、慣れていないプラットフォーム(言語だったり、フレームワークだったり)上で開発しているときに特によいと思う。 色々なところを書き換えてしまっていてわけがわからなくなったら、一旦動いていたコミットまで戻って冷静になってからもう一度書き直すとうまくいくことも多い。
小さな作業が終わったら、上記のREADMEファイルのTODOリストにdoneマークをつけていく。 作業中に気づいた新たなTODOは、この時点でTODOリストに加える。
* [x] module-aをmodule-bと連携できるようにする
* [ ] module-bにaインターフェース追加
こうすることで、ある作業をし終わるたびにTODOリストを確認できる。 TODOリストを短い間隔で確認することで、自分が少し前に考えていたやらなければいけないことと、作業をしている途中に気づいたやらなければいけないことを整理できる。 私の場合、作業前に見積もっていた作業量と、作業に手を付けてから実際に必要そうな作業というのはずれてしまうことが多い。 TODOリストによって、自分のやりたいことまでどのくらい時間がかかりそうかの整理ができてよい。
作業が一段落したら、rebaseである1つの修正に関する小さなコミットをsquashして、レビュワがコミット単位でも何に関数変更かわかるようにしておく。 この際に、コミットログもきちんと書き直す。
# 本流の更新を確認・rebase
git fetch
git rebase origin/development
# interactive modeでコミットを整理する
git rebase -i origin/develop
git rebase -i
では、コミット行の入れ替えとreward
, fixup
をよく使う。
interactive modeに入ると、エディタ上でも使い方の説明は表示されるが、ドキュメントにも解説がある。(git-rebase(1))
5章 メソッドの抽出
1つのメソッドが長すぎる場合、グループ化できるコードを抜き出して新しいメソッドを作る。
- 新しいメソッドを作る
- 新しいメソッドが「何をするか」(※どのようにするか、ではない点に注意)を名前にする
- 元のメソッドから、新しいメソッドへコードをコピーする
- パラメータ、戻り値などを検討
- 新しいメソッドを呼び出す
- コンパイルしてテストする
6章 クラスの抽出
たくさんの責務を負っている大きなクラスから、抜き出せる責務を抜き出して新しいクラスを作る。 結果として、クラスが小さくなり可読性が上がり、クラスが果たすべき仕事が明確になる。
- 新しいクラスを作る
- 大きなクラスから、責務をどう切り出すかを決める
- 抽出元のクラスから抽出した新しいクラスへのリンクを貼る
- 逆方向のリンクはできるだけ作らない
- フィールドを移動
- メソッドを移動
- ※移動の旅にコンパイルしてテストを推奨している
- 抽出したクラスを検討する
- クラスのインターフェースを減らせないか
- 公開範囲
- 公開する場合、外部からの修正を許すか
Immutable Interface
以下のようなgetterだけを定義したinterfaceを用意しておき、状態を更新させたくない場所ではImmutable
インターフェースとして型を定義しておくやり方がある。
public interface Immutable {
public String getName();
}
7章 クラスによるタイプコードの書き換え
intなどの primitive 型を使うと、想定外の値をコンパイルエラーで検知することができない。
以下のように型を作っておくことによって、想定外の値が利用されることを防ぐことができる。
public enum ItemType {
BOOK(0),
Magazine(1);
private final int code;
private ItemType(int code) {
this.code = code;
}
public int getCode() {
return this.code;
}
}