南極の図書館

ペンギンが寝ていた…。

「リファクタリング」の完全読破。その5

今回からカタログに入る。第6章は内容が多いので2回にわけて。
今回は、総論と「メソッドの抽出(P110) 」「説明用変数の導入(P124)」をしっかりと。

第6章 メソッドの構成

総論

下記の通り重要な章。いくつかの項目を除けばクラス階層の変更が不要なので、やりやすいという面もある。
(経験上、クラスを作ったり消したりすると面倒になる職場が多い。悲しいことに。)

P109
メソッドを、適切にパッケージ化されたコードとして構成することが、本書のリファクタリングの大部分です。
問題を起こすのは、ほとんどの場合、長すぎるメソッドなのです。

メソッドを分解すると、その動作がより深く理解できます。そして、そのアルゴリズムがもっとわかりやすくできることに気づくかもしれません。


さて、いったいどこまでメソッドを小さくするといいのだろうか。
私はここに書いてある通りと考える。

P110
メソッド名とメソッド本体の間の意味的な距離が重要なのです。コードを抽出することで明快さが向上するなら、そうすればよいのです。
抽出されるコードよりも名前のほうが長いとしてもです。

ファウラーは、現実的、実務的な人だという印象が強い。(これは他の著書でも強く感じる。UMLでは顕著に。)
ここでも、見た目や体裁より如何に実際の作業で効果が上がるか、ということが書かれている。

本章の構成

本章では「メソッドの抽出(P110)」がメインとなり、それをいくつかのリファクタリングが支えている。
例えば、「問い合わせによる一時変数の置き換え(P120)」で一時変数を取り除く。
困難な場合は先に「一時変数の分離(P128)」を行う。
それでもダメなら「メソッドオブジェクトによるメソッドの置き換え(P135)」を適用する。
パラメータの場合は「パラメータの除去(P131)」を行う。
また「アルゴリズムの取替(P139)」を検討する。
などなど。(これらは次回取り上げる。)


知っての通り、今では簡単なリファクタリングは自分で書く必要はない。
例えば「メソッドの抽出」なら、Eclipse上で括って右クリックし「リファクタリング」「メソッドの抽出」を選択、そしてメソッド名を入力すると自動で変換される。
しかし、もちろんリファクタリングはそんな簡単なものばかりではない。
効果的なリファクタリングは、プログラマが意図的に書かなければならないものが多いので、しっかり学ばなければならない。

「メソッドの抽出(P110) 」と「説明用変数の導入(P124)」

「メソッドの抽出」は本章のメインとなるリファクタリングである。
「説明用変数の導入」は適用したくなる場面が多いが、ファウラーはその場合も「メソッドの抽出」を適用することを推奨している。
ここでは、両者を比較することで理解を深めようと思う。(本書でもそうしている。)


まずは修正前のコード。こういうコードをよく見る人も多いのでは。(下記のコード群は全てP125〜P127を元にしたもの。)

double price(){
  //価格は、基本価格ー数量割引+送料
  return _quantity * _itemprice - //基本価格
         Math.max(0,  _quantity - 500) * _itemPrice * 0.05 + //数量割引
         Math.min(_quantity * _itemPrice * 0.1, 100.0); //送料
}

まず、_quantity * _itempriceが複数回書かれており、意味もコメントの通りなのでbasePriceとして抽出する。
そして、Math.maxの行と、Math.minの行もコメントの通りに「説明用変数」として抽出する。
それだけで「説明用変数の導入」は完了する。


ここで重要な点は「コメントを取り除ける」ということ。修正後のコードは、コード以上のことを何も表現していないからだ。
(とはいえ、それでも書けというプロジェクトはいくらでもありそう。ご愁傷さまです。)

double price(){
final double basePrice = _quantity * _itemPrice;
final double quantityDiscount = Math.max(0,  _quantity - 500) * _itemPrice * 0.05;
final double shipping = Math.min(basePrice * 0.1, 100.0);

return basePrice - quantityDiscount + shipping;
}

十分わかりやすいが、ファウラーは上記のとおり「メソッドの抽出」を推奨している。
それを適用するとこうなる。

double price(){
  return basePrice() - quantityDiscount() + shipping();
}

private double basePrice() {
  return _quantity * _itemprice;
}

private double quantityDiscount() {
  return Math.max(0,  _quantity - 500) * _itemPrice * 0.05;
}

private double shipping() {
  return Math.min(basePrice() * 0.1, 100.0);
}

読みやすさとしては同じようなものだと思う。
メソッドにする利点は「他の部分からも使える」ことが大きく、必要に応じてprivateの解除を行って良い。
できる限り「メソッドの抽出」を行い、「説明用変数の導入」 を使うのは「メソッドの抽出」では手間がかかる場合だとファウラーは書いている。


今回書いたリファクタリングは、頭の硬い上司(やそれに準じる体制)がいてもできるのではないかと思う。
次回は、これを支えるリファクタリングについて記載する。


続く。