プログラミングの魔物

エラー、バグ、仕様変更と戦うブログ

リファクタリング「コードの不吉な匂い」

リファクタリング 3章

リファクタリングを行うタイミングについては明確な定義を持つことが出来ない。
だから経験の中から「匂い」を感じて時期を決める。

この章と本の裏表紙にある表にはいつリファクタリングを行うかのヒントが書かれている。

重複したコード

言うまでもなく無駄。

  • 同一クラスの複数メソッドに同じ式>「メソッドの抽出」
  • 重複しているコードが複数の兄弟クラスに存在する場合>「メソッドの抽出」後、「メソッドの引き上げ」
  • コードが似ているけど完全に同一ではない場合>「メソッドの抽出」後、場合によっては「TemplateMethodの形成」
  • 複数のメソッドが同じ処理を異なるアルゴリズムで実装している>「アルゴリズムの取り替え」
  • 関係のないクラス間で重複コード>「クラスの抽出」をして新しいクラスに委譲、あるいはどちらかのクラスに持って委譲

長すぎるメソッド

メソッドは長いほど理解しにくくなる。

  • メソッドを短くする、注釈コメントの付いているコード片>「メソッドの抽出」
  • 引数や一時変数が多すぎるメソッド>「問い合わせによる一時変数の置き換え」
  • 長い引数リスト>「引数オブジェクトの導入」や「オブジェクトそのものの受け渡し」
  • 一時変数や引数をさらに減らす>「メソッドオブジェクトによるメソッドの置き換え」
  • 条件分岐やループ>「条件記述の分解」

巨大なクラス

たいていインスタンス変数の持ちすぎ。コードが重複している可能性もある

  • 大きすぎるクラス>「クラスの抽出」
  • 新しく出来たコンポーネントがサブクラスになりそうな場合>「サブクラスの抽出」
  • クラスがインスタンス変数を全部使っていない>「クラスの抽出」、「サブクラスの適用」
  • クラスの重複部分削除
  • コードが多すぎるクラス>「クラスの抽出」や「サブクラスの抽出」
  • クラスの利用者が利用しているインターフェースを考えて分割>「インターフェースの抽出」
  • GUIの場合、データと振舞いを分離したドメインクラスに移すこともある。重複したデータを同期する場合>「観察されるデータの複製」

多すぎる引数

引数が多いとわかりづらい。オブジェクトやメソッドに置き換える

  • 既知のオブジェクトに問い合わせることができる場合>「メソッドによる引数の置き換え」
  • 複数の引数が1つのオブジェクトにまとまっていれば>「オブジェクトそのものの受け渡し」
  • 引数をオブジェクトにまとめる>「引数オブジェクトの導入」

※ただし、依存関係を持ちたくない場合は引数で渡す

変更の発散

1度の変更で1つのクラスの複数メソッドを修正する必要が有る場合は2つのクラスに分割したほうがスッキリする。

  • 修正が起きる部分を別クラスに分離>「クラスの抽出」

変更の分散

1つの変更が様々なクラスの変更を引き起こすケース

  • 1度の変更で複数クラスが書き換わる場合>「メソッドの移動」や「フィールドの移動」
  • 既存のクラスに適切な候補がない>クラスの新規作成や「クラスのインライン化」

属性、操作の横恋慕

オブジェクト指向は処理及び処理に必要なデータを1つにまとめるという考え方。自分のクラスより他のクラスに興味を持っている場合は誤り。

  • 他のオブジェクトのgetを何度も呼び出しているメソッド>「メソッドの移動」
  • メソッドの一部が上記のような状態>「メソッドの抽出」→「メソッドの移動」
  • メソッド内で様々なクラスのデータを参照>大部分のデータをあるクラスに持たせてから「メソッドの移動」。「メソッドの抽出」をしてからだと移動しやすい

※例外があり、振る舞いのみの変更に対処するために外に出ている場合もある。StrategyやVisitorで振る舞いのみを容易に変更できる

データの群れ

データはあちこちに飛び回る。データをまとめただけでも前進

  • 群れをなしたデータ(属性)をまとめる>「クラスの抽出」
  • メソッドのシグネチャの場合>「引数オブジェクトの導入」や「オブジェクトそのものの受け渡し」

基本データ型への執着

たとえ基本型に近くてもオブジェクト化するのは悪くない。他のクラスから抽出したメソッドの受け入れ先になる可能性がある

  • 個々のデータをオブジェクト化する>「オブジェクトによるデータ値の置き換え」
  • データがタイプコード(列挙子や定数)でその値が全体に影響しない場合>「クラスによるタイプコードの置き換え」
  • タイプコードによる条件分岐>「サブクラスによるタイプコードの置き換え」または「Sata/Strategyによるタイプコードの置き換え」
  • 属性をまとめる>「クラスの抽出」
  • 基本データ型が引数リストに沢山>「引数オブジェクトの導入」
  • 配列が面倒>「オブジェクトによる配列の置き換え」

スイッチ文

オブジェクト指向プログラミングではスイッチ文が従来に比べて少なくなる
スイッチ文をみたらポリモーフィズム

  • スイッチ文を分離>「メソッドの抽出」→「メソッドの移動」→「サブクラスによるタイプコードの置き換え」または「State/Strategyによるタイプコードの置き換え」を選択
  • 継承構造ができている>「ポリモーフィズムによる条件分岐の置き換え」
  • 同一メソッドの僅かなケースで変更がない場合はポリモーフィズムにしなくてもよい>「明示的なメソッド群による引数の置き換え」
  • nullで分岐>「ヌルオブジェクトの導入」

パラレル継承

  • 新たなサブクラスの定義時に別のサブクラスも定義する必要が有る場合、どちらかが参照すると重複を取り除ける>「メソッドの移動」→「フィールドの移動」

怠け者クラス

不要になったクラス

  • サブクラスが十分な働きをしていない>「階層の平坦化」
  • 殆ど役立たないヘルパークラス>「クラスのインライン化」

疑わしき一般化

現在必要ないのに「いつか必要になるから」と用意してある複雑な機能。無用の長物は削除

  • 大した働きのない抽象クラス>「クラスのインライン化」
  • 意味のない委譲>「クラスのインライン化」
  • 使われないパラメータを持つメソッド>「引数の削除」
  • わかりにくい抽象的な名前のメソッド>「メソッド名の変更」

一時的属性

  • インスタンス変数の値が一部でしか設定されていない。普通は常に保持するものなので理解しづらい>「クラスの抽出」、「ヌルオブジェクトの導入」
  • 変数やその変数を参照しているメソッドを対象>「クラスの抽出」

メッセージの連鎖

  • メソッド呼び出しが長くなったり一時変数が増えたりしている>「委譲の隠蔽」
  • 実際にオブジェクトを使っている部分>「メソッドの抽出」→「メソッドの移動」で連鎖の端の方へ移動

仲介人

  • メソッドの大半が別のオブジェクトに委譲しているだけ>「メソッドのインライン化」
  • 何か処理が加わっている>「継承による委譲の置き換え」

不適切な関係

  • クラス同士の仲が良すぎるので引き離す>「メソッドの移動」や「フィールドの移動」、「双方向関連の単方向への変更」
  • クラスが共通の興味を持っている>「クラスの抽出」
  • 第三者のクラスを仲介人に>「委譲の隠蔽」
  • 子供が独り立ちする時>「委譲による継承の置き換え」

クラスのインターフェース不一致

  • 処理は同じでシグネチャのみが異なる>「メソッド名の変更」
  • インターフェースの不一致で上手く行かなければ>「メソッドの移動」
  • 移動にともなって重複したコードが発生したら>「スーパークラスの抽出」

未熟なクラスライブラリ

ライブラリの修正が困難なケース

  • クラスライブラリに追加したいメソッドがわずか>「外部メソッドの導入」
  • 追加される振る舞いが多くなりそう>「局所的拡張の導入」

データクラス

最初はget,setしかなくてもプロジェクトが進むに連れて育っていく可能性がある。他のクラスのget,setを取り込めないか検討したりする。

  • 公開の属性>「フィールドのカプセル化
  • コレクションクラスが属性の場合、必要に応じ>「コレクションのカプセル化
  • get、setメソッドが他のクラスのどこで使われているのかを調べ>「メソッドの移動」
  • メソッド全体を移せなければ特定部分を取り出す>「メソッドの抽出」
  • 上記の後にいくつかのget、setに対して>「メソッドの隠蔽」

相続拒否

※振る舞いを再利用するために継承していることもある。使われないことで問題が起きていなければ放っておいてもいい。

  • サブクラスが親の一部しか利用していない>兄弟クラスを新たに作成して「メソッドの引き下げ」、「フィールドの引き下げ」
  • サブクラスでスーパークラスの振る舞いは継承してもインターフェースは必要ない>「委譲による継承の置き換え」

コメント

コメントは書いてもいいが消臭剤になっていたら注意。
コメントは不明点を書き留めておく。処理を選択した理由など。リファクタリングの道標

  • メソッドの一部にコメントが必要>「メソッドの抽出」
  • メソッドが既に細かく、それでもコメントがないと理解し難い>「メソッド名の変更」
  • システムが特定の状態である必要を明確に表現したい場合>「表明の導入」

正直言うと読んだだけでは頭に入ってない部分がある。
この章は他の章を読んで、リファクタリングの名称と内容が一致した後で改めて読み返したい。

この記事で読み進めている本

リファクタリング―プログラムの体質改善テクニック (Object Technology Series)

リファクタリング―プログラムの体質改善テクニック (Object Technology Series)

  • 作者: マーチンファウラー,Martin Fowler,児玉公信,平澤章,友野晶夫,梅沢真史
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2000/05
  • メディア: 単行本
  • 購入: 93人 クリック: 3,054回
  • この商品を含むブログ (295件) を見る