『リファクタリング―プログラムの体質改善テクニック』の続編になっています。

例のごとく、自分で読み直したときにどんな場合にどのリファクタリングが使えるかのメモにしようと思ってます。2005年中くらいには、本書の和訳が出版される予定です。

現在、洋書読破中。記事もそのつど更新していくつもりです。(最終更新日:2004/10/29)

本家サイトはこちらになります。カタログ一覧あります。

Refactoring To Patterns

インデックス

場合別リファクタリングカタログ

カタログ名のリンクをクリックすると、別ウィンドウで本家のイメージ図が開きます。一緒に見るとイメージがわくかも。

Form Template Method

継承関係にあるサブクラス間の明白な、または微妙な重複コードを取り除く

Introduce Polymorphic Creation with Factory Method

サブクラスで似たような処理を実装している場合、オブジェクト生成段階で Template Method パターンを用いることによって重複コードを減らすことができる。

オブジェクト生成をポリモーフィズムで行うことで、オブジェクト指向っぽくなります。このリファクタリングを行った後のコードは、サブクラス間で重複した同じようなコードがなくなります。

Factory Method パターンと Template Method パターンはよく同時に使われます。

Chain Constructors

コンストラクタで似たような処理が重複している場合、このパターンで重複を除去できる。

Replace One/Many Distinctions with Composite

一つのオブジェクトもしくはオブジェクトのコレクションを処理するコードが散らばっているような場合に、重複を除去できる可能性がある。

Extract Composite

階層関係にあるサブクラスで、個々のクラスがコンポジット関係にある場合、実装はほとんど同じである場合が多い。そのようなときに使える。

Unify Interfaces with Adapter

異なったインターフェースを持つからという理由で、単に違うオブジェクトを処理するのなら、Adapterと統一のインターフェースを利用して、共通のロジックを除去出来るかもしれない。

Introduce Null Object

オブジェクトが null の場合に条件句で何の処理も行わない場合が多くある時、このパターンで重複が減らせるかもしれない。

Compose Method

長くて理解しにくいメソッドがある場合、処理を構成する部分をわける。この方法は、リファクタリングの Extract Method を多く用いる。

処理を区切るときの注意点として、同じ処理の詳細レベルになるようにメソッド化することがあげられる。

このリファクタリングを適用すると、private メソッドや小さな処理単位のメソッドが非常にたくさんできる。その場合は、Extract Class リファクタリングを用いて、クラスを分けるなどして対応するとよい。

Move Accumulation to Collectiong Parameter

情報を変換していく作業がある場合、処理を構成する別々のメソッドを定義して、処理の順番で一つの情報を構成するようにする。

Replace Conditional Dispatcher with Command

条件文 (switch 文など)によって処理を分岐させるようなコーディングは、ハードコードになり、新しい条件が付け加わったりリクエストが増えたりした場合に、修正が追いつかなくなる。

対応策として、Command パターンを用いるとよい。Command クラスにパラメータやリクエストを保持させるようにし、処理内容を Command 自体に行わせる。条件パラメータやリクエストが増えたら、新しいCommand クラスを作ることで対応できるようになる。

Move Accumulation to Visitor

異なったインターフェースのオブジェクトから情報を取得するために、巨大な switch 文を使うくらいなら、Visitor パターンを利用してデータを集めるようにする。

Replace Conditional Logic with Strategy

条件によって処理のアルゴリズムが違うような場合は、 条件句をStrategy パターンで置き換える。

このパターンを使う動機の一つとしてこのようなことがあげられる。まず、条件分岐のロジックを書く場合というのはよく、アルゴリズムを決めるためということが多い。Decompose Conditional パターンや Compose Methodパターンを用いてこのロジックの部分をシンプルに、分かりやすくすることが出来る。

しかし一方では、Decompose Conditionalパターンや Compose Method パターンを使ってロジックをメソッドに分割すると、小さなメソッドがたくさん出来てしまう。それを防ぐために、このReplace Conditional Logic with Strategy パターンを使う。

Move Embellishment to Decorator

クラスの核となる責務を装飾するようなコードは、条件句で装飾をするかどうかを判断するのではなくて、装飾するコードをDecoratorクラスに委譲するようにする。

Decorator(装飾するクラス)とDecoratee(装飾されるクラス)は同じインターフェースを持つ。しかし、オブジェクトのクラスが一致するかどうかを判定するような場合(instanceof演算子を使う場合)は注意する必要があります。Decorateされたインスタンスは、Decorateeと同じインターフェースを持ちますが、通常は継承関係にはないので、クラスが一致することはありません。

クラスの同一性に依存するようなコードがクライアントにない場合にのみ、Decoratorパターンを使います。Decorator クラスは状態(フィールド)を持たない方がいいです。なぜならDecorator クラスは不必要な状態(フィールド)があったとしても、継承せざるを得なくなるからです。

Replace State-Altering Conditionals with State

オブジェクトの状態遷移が複雑になるとき、Stateパターンを使って状態遷移を簡略化する。

状態遷移のロジックというのは、さまざまなところに重複ロジックが散乱することが多い。状態遷移のロジックを他のクラス(Stateクラス)に移動させることで、コードがシンプルになる。ただし、最初からこのリファクタリングを適用するのではなく、通常はもっと簡単なExtract Methodパターンを適用する。

Stateパターンと Strategyパターンの違い
Stateパターンと Strategyパターンは、考え方の違いである(最終的なクラス図はほとんど同じ形)。Stateパターンは、状態クラスのインスタンス間で状態遷移のロジックが簡単になるように使われる。一方の Strategyパターンは、ストラテジークラスのインスタンスにアルゴリズムの実行を委譲(delegate)する目的で使う。

Replace Type Code with Class

プリミティブ型をつかって型(タイプ)を判別しているようなら、 Type クラスを作って、型の安全性を保証したほうがよい。

Replace Implicit Language with Interpreter

クラスの責務が、プリミティブ型の組み合わせで成り立つような場合、ある種の言語と考えて Interpreter パターンを使うといいかもしれない。

Encapsulate Composite with Builder

Compositeパターンを使った再帰的な構造を組み立てる場合に Builder を使うとクライアントはシンプルになる。

通常、Compositeパターンを使って構造を組み立てる場合、実装クラスに関連してしまう(カタログの場合 TagNode クラス)。Compositeを組み立てる Builder クラスを用意することで、実装を知らずにクライアントは構造を組み立てることが出来るようになる。

このパターンを使う動機
1. 複雑なオブジェクトを構築しなければならない場合にクライアントの実装はシンプルに保ちたい
2. クライアントをCompositeの詳細から切り離したい

CompositeパターンとBuilderパターンは同時に使われることが多い。理由は2番の動機のように、クライアントがCompositeの詳細を知らなくてもすむようにしたいから。このように設計しておくことで、Compositeの実装が変わった場合にBuilderでクライアントの変更を吸収出来る場合が多くなる

Replace Constructors with Creation Methods

クラスにコンストラクタがたくさんあり、プログラマーがどのコンストラクタを呼べばよいのか分かりにくい場合にこのリファクタリングが使える。複数のコンストラクタよりも、Create Method を用意したほうが良い理由は、メソッドに名前が付けられるから、どんな処理が行われるのか名前から判断できるから。(コンストラクタじゃ全部同じ名前で分からない)

Move Creation Knowledge to Factory

色々なクラスにインスタンス生成の情報が広がってしまっている場合、単一のFactoryクラスを作成することでシンプルになるかもしれない。

インスタンス生成の役割は色々なクラスに広がるべきではない。今回のようなaStringNodeインスタンス生成の方法をaParser、aStringParser、StringNodeに散らばらせるのではなく、NodeFactoryを導入しインスタンス生成と設定をカプセル化すべき。(クラスには単一の責務のみを持たせるべきだから)

生成するインスタンスがロジックによって違ってくるような場合、AbstractFactoryパターンを使ってFactoryを分けることで対応できるようになる。aParserにインスタンス生成の責務を負わせていると、条件分岐のロジックが重複してしまう可能性が高い。

Encapsulate Classes with Factory

クラスのクライアントが直接、実装に依存してしまう関連よりも Factory メソッドを用意して抽象に依存させるようにする。

抽象に依存せよ。実装に依存するな。」は、良いクラス設計のポイントです。このパターンを適用することでクライアントは実装に依存せずに、抽象インターフェースにのみ依存するようになります。

ただし一つ問題があります。このパターンでは、子クラスが増えた場合に Factory クラスを修正しなければなりません。インターフェースの変更(追加)を行わなければならなくなるので、子クラスがほとんど追加されないような場合にだけ使った方が良いと思います。

Inline Singleton

不適切なSingletonの使い方をしている部分をリファクタリングします。

Singletonパターンを必要としない場面は下記のようなときがあります。

  • Singletonオブジェクトを使うクラスが、Singletonオブジェクトを引数などで簡単に渡してもらえる場合
  • 些細なメモリやパフォーマンスを改善するためだけに使われるSingletonオブジェクト
  • 下層のクラスが同一層のリソース以外を使う目的で使われているSingletonオブジェクト

Singletonを使うべき理由は以下だけです。

たった一つのインスタンスであることを保証したい場合とグローバルアクセスポイントを提供したい場合

図の説明

カタログに載ってる図は、アプリケーション層にある Blackjackクラスが 上位層の Console クラスに依存している。リファクタリング前のコードでは、ConsoleクラスをHitStayResponseオブジェクトを上位層から下位層に渡すためのRegistの役割に使っている。

リファクタリング後のコードでは、素直に Blackjackクラスにセッターメソッドを用意して、無駄なRegistクラスを作らないように修正されている。

Replace Implicit Tree with Composite

ツリー構造を構成するような処理を、Composite パターンをを使って分かりやすくします。

XMLデータなどのツリー形式を構成するデータ構造を扱う場合、Composite パターンを使ってクラスを分割すると、ロジックがすっきりすることがあります。

参考


  • GoF以外のデザインパターンが載ってます。PLoPで扱われている中で主なものを選んであります。

  • エンタープライズで使えるパターン集のバイブル的本です。(洋書)