2010年06月04日

Factory(ファクトリ)パターン

Factoryパターンって、Factory Methodパターンのことを言ってるの?それともAbstract Factoryパターンのこと?
と疑問に思う方がいらっしゃると思いますが、ここでは両者をまとめて解説します。

 あえて断定しますが、両者にはたいした違いはありません。
この「たいした」の部分のについては、説明を読んでいただければ、わかるかと思います。さて、Factoryってなんでしょう?
まぁ、直訳すれば工場です。
何の工場かといえば、オブジェクト指向のハナシをしてるんですか
ら、決まってますよね。
オブジェクトですよ。
オブジェクトのインスタンスを生成する、工場なわけです。

aクラスのインスタンスを生成するためのaファクトリとか、xクラスのインスタンスを生成するxファクトリとかがあるわけです。

それをやるとなにが嬉しいのか?
オブジェクトのインスタンスを作る箇所での、結合度を下げることができるのです。
もっと具体的に見てみましょう。
class A {
  // なにかの処理
}

class B {
  method(){
    A a = new A();
  }
}

この擬似コードでは、クラスBはクラスAに依存しています。
"A"というクラスを、そのまま変数の型に使用しているからです。
もし同じインタフェースをもつ別のクラスを作っても、クラスBの"method"メソッドは、変更なしにそれに対応することはできません。
では、依存度を下げるためにインタフェースを用意しましょう。
interface C {
// いくつかのメソッド
}

class A implements C {
// インタフェースCの実装
}

クラスBは、以下のようになるでしょう。
class B {
method(){
C c = new A();
}

...おや?これでも依存度は下がっていないことが、わかりますね。
そう、問題は「new A();」の部分です。
問題は、コンストラクタにあるのです。

これがここに書いてある限り、インタフェースCは、依存度を下げる役にたっていません。
new ...を排除する必要がありますね。

コンストラクタの呼び出しをコードのいろんなところに散りばめれば、コード全体に接着剤をぶちまけるようなものです。
でも、どこかしらでクラスを明示してコンストラクタを使用しなければ、オブジェクトを生成することはできませんよね。
リフレクションでも使えば別ですが、それはそれで問題があります。

避けられないのなら、一箇所にまとめて隔離しましょう。
それがファクトリってわけです。
ファクトリを使うと、たとえばクラスBはこうなります。
class B {
method(){
C c = Cfactory.newInstance();
}
}
Cfactoryは、newInstance()メソッド内でクラスAのインスタンスを生成して、返します。
クラスBからはクラスAのコンストラクタ呼び出しは排除され、CfactoryのnewInstanceメソッド内に隠蔽されました。
これで、このメソッドはインタフェースCをインプリメントしているものならなんでも、変更なしに扱うことができます。
まぁ、このメソッドがちゃんと実装してあればのハナシですが。

ここで、Cfactoryには大きな可能性が秘められていることに注目してください。
newInstanceメソッドは、クラスAを返すかもしれません
し、クラスAのサブクラスを返すかもしれません。
あるいは、インタフェースCをインプリメントしているものの、クラスAとはまったく関係がないクラスを返すかもしれません。
どのクラスを返すかは、クラスBとは関係ない別のところで決めることができるのです。
クラスBを作ったときには、まったく考えもしなかったクラスをあとから追加して、newInstanceメソッドで返すようにすることさえできます。
これぞまさに自由!
インスタンス生成に自由をもたらすことが、ファクトリの目的です。


では、今度は少しだけファクトリの中身に注目してみましょう。
ファクトリで得た自由を、どう生かすかということです。
代表的な2つのテクニックが知られています。


サブクラスで生成する

ファクトリにサブクラスを作って、そちらでオブジェクトを生成します。
どのオブジェクトを作るかは、主にファクトリのどのサブクラスが使用されたかで決まります。
class SuperFactory{
abstract C newInstance();
}

class SubFactoryA{
C newInstance(){
return new A();
}

class SubFactoryB{
C newInstance(){
return new B();
}

てな具合です。
newInstance()メソッドの中では、単にインスタンスを生成するだけでなく、他の条件に応じて、生成するインスタンスを切り替えることもできますね。
ファクトリSuperFactoryは、それ自体インスタンス化の必要がない場合、抽象クラスにした方がいいでしょう。
SuperFactoryがサブクラスにインスタンスの生成を委譲しているのがミソですね。
インタフェースCを実装する新たなクラスを追加したい場合には、ファクトリにそれに対応するクラスを追加すればよく、クライアントには一切触れる必要がありません。
これが、Factory Methodパターンです。


関連するオブジェクトの生成をまとめてファクトリ化

interface A {
// いくつかのメソッド
}

interface B {
// いくつかのメソッド
}

class SuperFactory {
abstract A newAInstance();
abstract B newBInstance();
}

class Alpha implements A {
// インタフェースAの実装
}

class Beta implements B {
// インタフェースBの実装
}

class SubFactory{
A newAInstance(){
return new Alpha();
}
B newBInstance(){
return new Beta();
}
}
こんなカンジ。
ここでは、インタフェースAとインタフェースBには何らかの関係があるとします。
関係があるので、実装を切り替えるときには、一緒に切り替えたいわけです。
クラスSubFactroyに代わるSuperFactoryのサブクラスを作れば、インタフェースAとインタ
フェースBの実装を同時に切り替えることができます。
どちらか一方だけ切り替えてしまうという間違いを犯す心配がなくなります。
また、どれを使うかを選択するコードが、たくさんのクライアントに散りばめられることも避けられます。
これが、Abstract Factoryパターンです。

つまり、関連するオブジェクトのためのFactory Methodをひとつにまとめたものが、Abstract Factoryということですね。
おっと、でもちょっと待って下さい。
これはかなり乱暴な言い方で、Abstract Factoryの一面しか表していません。
Abstract Factoryがやりたいのは、あくまで「関連するオブジェクトの生成をまとめる」ということです。
それは、Factory Methodを使用することで実現することができますが、他にもテはあります。
Abstract Factoryにとって、Factory Methodはあくまでひとつの手段に過ぎません。
状況によっては、Prototypeパターンなど、他のパターンを利用して実装されることもあ
ります。
実装方法はたいした問題ではありません。

Abstract FactoryとFactory Methodには、このような違いがあるわけですが、どちらも生成す
るオブジェクトを自由に切り替える
ためにあるというところを、おわかりいただけたでしょうか。
posted by craftsman at 08:10 | 東京 🌁 | Comment(0) | TrackBack(0) | デザインパターン
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント: [必須入力]

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
この記事へのトラックバックURL
http://blog.seesaa.jp/tb/127557478
※言及リンクのないトラックバックは受信されません。

この記事へのトラックバック
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。