第0章_Java言語で学ぶリファクタリング入門
リファクタリングとは
リファクタリングの定義
リファクタリングとは、
外部から見たプログラムの振る舞いを変えずに、
プログラム内部の構造を改善すること。
外部から見た振る舞いが本当に変化していないことを確認するには、
ユニットテスト(単体テスト)を用いる。
リファクタリングを行うときには、
その前後にテストを行う。
リファクタリングを剃る前に、テストを行います。
↓
リファクタリングを行う。
↓
リファクタリングをした後に、テストを行う。
リファクタリングと「不吉な匂い」
不吉な匂いとは
プログラムの中でリファクタリングを必要としている部分のこと。
これは書籍「リファクタリング」に書かれている、感覚的でよい比喩。
また、この書籍では、22コの不吉な匂いが紹介されている。
不吉な匂い | 説明 |
---|---|
重複したコード | コードがあちこちでダブっている |
長すぎるメソッド | メソッドが長すぎる |
巨大なクラス | クラスが持っているフィールドメソッドが多すぎる。 |
多すぎる引数 | メソッドへ渡す引数の数多すぎる |
変更の発散 | 仕様変更が起きた時の修正箇所があちこちに散らばっている |
変更の分散 | あるクラスを修正すると、他のクラスもあわせて修正しなければならない。 |
属性、操作の横恋慕 | いつも他のクラスの中身をいじっているクラスがある。 |
データの群れ | まとめて扱うべき複数のデータが1つのクラスにまとまっていない |
基本データ型への執着 | クラスを作らず、intのような基本データ型ばかり使っている。 |
スイッチ文 | switch文やif文を使って振る舞いを分けている |
パラレル継承 | サブクラスを作ると、クラス階層の別のところにもサブクラスを作らなければならない |
怠け者クラス | クラスがたいした仕事をしていない |
疑わしき一般化 | いつかこういう拡張もするだろうと期待して、一般化しすぎる。 |
一時的属性 | 一時的にしか使わないフィールドがある |
メッセージの連鎖 | メソッド呼び出しの連鎖が多すぎる |
仲介人 | 委譲ばかりしていて、自分では仕事をしていないクラスがある。 |
不適切な関係 | その必要がないのに双方向リンクを貼っていたり、IS-A関係がないのに、継承を使っていたりする。 |
クラスのインタフェースの不一致 | クラスのインターフェース(API)が不適切である。 |
未熟なクラスライブラリ | 既存のクラスライブラリが使いにくい。 |
データクラス | フィールドとgetterメソッドとsetterメソッドしか持っていないクラスがある。 |
相続拒否 | 継承しているメソッドなのに、それを呼ぶと問題が起きる。 |
コメント | コードの不備を補うために詳しいコメントがある。 |
不吉な匂いを示すセリフ
最初から22個を全部覚えるのは大変なので、
「不吉な匂いを示すセリフ」を6つ紹介されています。
- ダブっている
- 長すぎ
- 多すぎ
- 名前が合っていない
- 公開しすぎ
- オブジェクト指向っぽくない
ダブっている
- メソッドの抽出、クラスの抽出
- nullチェックがあちこちに見つかるなら、ヌルオブジェクトの導入
- エラーチェックが多い時には、例外によるエラーコードの置き換え
長すぎ
<<メソッドの抽出>>
メソッドが長すぎると、理解するのが難しい。
<<クラスの抽出>>
クラスが大きすぎるというのは、
そのクラスが担っている責任(責務)が多すぎるということ
リファクタリングの基本
- 長過ぎたら、短くしましょう
- 大きすぎたら、小さくしましょう
多すぎ
クラスが大きいからといって、クラスを抽出し過ぎてはいけない。
理由は、クラスの抽出をやり過ぎると、今度はクラスが増えすぎてしまう。
クラスが多すぎると感じたら、<<仲介人の削除>><<クラスのインライン化>><<メソッドのインライン化>>
名前が合っていない
名前は重要です。
プログラムの四手に適切な情報を伝える役目があります。
しかも、名前は1回つけて終わりではなりません。
プログラムは生きてます。
処理のまとまりに名前をつける。
メソッド名を適切にする。<<メソッドの抽出>><<メソッド名の変更>><<説明用変数の導入>><<一時変数の分離>>
公開しすぎ
適切に情報を隠すことを一般に情報隠蔽と言う。
フィールドのカプセル化の必要性
publicになったメソっドは、他から呼ばれる可能性がある。
ということは、プログラムを改良しようと思った時に、
「このメソッド、誰か使っているかも知れないな、削除したらまずいかな?」
と悩むことになる。
FactoryMethodによるコンストラクタの置き換え(※FactoryMethodによってクラス名を隠す。)
クラスの名前も公開しすぎてはいけない。
newを使ってインスタンスを生成する時、newのあとには具体的なクラスを書きます。
この場合、クラス名を変更するのは難しくなる。
理由は、プログラム中でインスタンスをnewしている部分をすべて変更しなければならない。
委譲の隠蔽
クラス間の関係でも公開しすぎと言いたくなることがある。
シンボリック定数によるマジックナンバーの置き換え
「100」のような具体的な値(マジックナンバー)が見えているのもよくないこと。
オブジェクト指向っぽくない
- switch文やif文を使って、処理を分岐している。
- instanceofを使って、オブジェクトが属するクラスを調べている。
- intばかり使って専用のクラスを作らない。
switch文やif文を使って異なる振る舞いを分岐させるのは、良くない。
switch文やif文の代わりに、多態性(ポリモーフィズム)を使うことを検討する。
多態性が上手く使えると、switch文やif文は消え、シンプルなメソッド呼び出しに変わる。<<クラスによるタイプコードの置き換え>><<サブクラスによるタイプコードの置き換え>>
オブジェクト指向では、オブジェクト自身が自分の振る舞いを知っている。
それなので、instanceofを使って「このオブジェクトはどのクラスのインスタンスだろう」と
いちいち調べる必要は無いのが普通。
※「オブジェクト指向っぽくないコード」があったら、
絶対にリファクタリングしなければならない、
と短絡的に考えてはいけません。
あくまでも、「ここはリファクタリングしたほうがよいだろうか?」と
検討する手がかりに過ぎない。
リファクタリング・カタログ
書籍「リファクタリング」では、
各リファクタリングの目的や手順をカタログ化してまとめています。
マーチン・ファウラーのサイト(英語)
http://martinfowler.com/refactoring/
リファクタリングのエッセンス
リファクタリングQ&A
Q.リファクタリングは万能薬か
A.いいえ
リファクタリングは、内部の構造を改善し、
バグをとりやすく、機能を増やしやすく、読みやすいコードを作るだけ。
ソフトウェアの常
長期間にわたって、バグの修正、機能追加が必要になるのが、ソフトウェアの常。
※ただし、リリース直前のタイミングや
期間的にに厳しい状況では、リファクタリングをするべきではない。
Q.どこまでが外部なのか
A.場合による。
- メソッドの本体に書かれている処理内容をリファクタリングしようと考えたら、
そのメソッド本体が「内部」になり、
そのメソッドを呼び出している部分が「外部」となる。
- クラスAの中に宣言されている複数のメソッドをリファクタリングの対象とするなら、
クラスAが「内部」になり、クラスAを利用している、
他のクラスB,C,D・・・が「外部」となるでしょう。
テストは、このリファクタリングによる影響はこの範囲の外側には及んでいない。
ソフトウエアは私達が想像する以上に複雑です。
設計の段階で見えていることには限りがあります。
実装をする段階で、初期設計時には
考えられていなかった状況が見つかることはよくあります。