【あるあるbot連動企画】あるあるbotに投稿したけど採用されなかったあるある募集

いつも参考にさせて頂いてます。java で良い設計ができないか試行錯誤している時に、デザインパターンなる定石パターンがあることを知りました。そこで、Factory Method パターンについて、

・どのようなケースで利用するのか?
・利点は何か?

を簡単に説明して頂けたらと思います。お願いします。

A 回答 (9件)

#3です。


自信なし。
---

(たぶん)最重要な側面(のうちの1つ)を書き忘れてました。

前述の「CollectionとIterator」の例で言うと、
sunのJDKについてくる「標準クラスライブラリとしてのCollection」は、
Vectorだったり、LinkedListだったり、
その他いろいろ、
いくつか「すでに存在」していると思うのですが、

ここで、「sunの標準ライブラリに存在しない、
自分オリジナルのCollection、MySupecialListというのを作りたい!」
と思ったとします。さらに
「このMySupecialListに合うIteratorは、
既存のsun標準ライブラリのIteratorの中には見当たらず、
これも自作するほかないんだよな~」
というケース。

いわば、
『"プラグインな"Collectionを使う時』
です。

このケースでは、
自作者は単に

class MySpecialList implements Collection{
 …
 (中略)
 …

 public Iterator iterator(){
  return new MyIterator();
 }
}

というクラスを作成するだけで、
利用者はこの自作Collectionを、
他のCollectionを使う時と"全く同じやり方で"使えるようになります。
(そうであるべき。)

仮に「Factory Methodパターン」が使われていなかった場合、
利用者が自作Collectionを使う際には
他の標準Collectionを使う時と"全く同じやり方で"使う手立ては
ないと思います。

『Collectionプラグイン』開発者としては、
利用者に"特別な使用法"を強いるわけにはいきませんし…。

---
言い換えれば

「追加しても」利用者にばれない

というカンジでしょうか…。

---

おそらく同様のケースは、
「フレームワーク(※Eclipseとか、いろいろ)の、
プラグイン部」(プラグイン開発)
で「必須な」ような気がします。
    • good
    • 0

FactoryMethodパターンについてお答えします。


FactoryMethodは、サブクラスとサブクラスを必ずペアで利用する場合に利用するパターンです。

例えば、人と携帯電話の関係を表す場合に利用します。
人は「田中さん」「鈴木さん」、携帯電話は「X999」「Y111」があるとし
「田中さん」が「X999」、「鈴木さん」が「Y111」という携帯電話を持っていたとします。
classは以下とします。

abstract class 人間(){
abstract public String get名前();
}

class manA extends 人間{
public String get名前(){return "田中"}
}

class manB extends 人間{
public String get名前(){return "鈴木"}
}

abstract class 携帯電話(){
abstract public String get機種();
}

class telephoneA extends 携帯電話{
public String get機種(){return "X999"}
}

class telephoneB extends 携帯電話{
public String get機種(){return "Y111"}
}


「田中さん」と持っている「携帯電話」の情報を知りたい場合、

人間 man = new manA();
携帯電話 telephone = new telephoneA();
System.out.println(man.get名前());
System.out.println(telephone.get機種());

とすれば、表示出来ますが、
情報を探す際に、「田中さん」が「X999」を持っている(組合せを知っている)必要があります。
「田中さん」「鈴木さん」だけなら良いのですが、これが100人、1000人となった場合、全部の組合せを覚えておくのは大変です。

そこで、プログラムの中に記述してしまいます。

abstract class 人間(){
abstract public String get名前();
abstract public 携帯電話 create携帯電話();
}
class manA extends 人間{
public String get名前(){return "田中"}
public 携帯電話 create携帯電話(){return new telephoneA()}
}

class manB extends 人間{
public String get名前(){return "鈴木"}
public 携帯電話 create携帯電話(){return new telephoneB()}
}


こうすれば
人間 man = new manA();
携帯電話 telephone = man.create携帯電話();
System.out.println(man.get名前());
System.out.println(telephone.get機種());
の形式で、
人と携帯電話の組合せが分からなくても、自動的に携帯電話情報を取得できます。

一人でプログラムを作っている場合は、価値があるかどうか微妙ですが、
実際の業務では、第三者が情報を探す際に、
人classだけで、携帯電話の情報を知ることが出来れば、大変便利です。
(第三者は、組合せを知らなくても良い)
これがFactoryMethodの利点です。

FactoryMethodを利用する際に、抽象化(ポリモーフィズム)を利用しますが、それ自体が本質ではありません。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。
教えてもらった設計をすることで、利用者には誰がどの携帯電話を使ってるかまで意識する必要がなくなりますね。

質問してわかったのですが「java の抽象化の利点=Factory Method の利点」と勘違いしていたケースが多々あります。ご迷惑かけました ^^

自分も紹介された本を含め、2冊ほどデザインパターン関連の本を購読しています。解説された利点も紹介されていて、確かに使えるパターンですね。

パターンをより効率よく利用してよい設計を心がけたいものです。

お礼日時:2006/09/20 09:08

私の書き方も悪かったんでしょうけど……



>「継承はそのツリー構造内でカプセル化が破られる」
>これがFactory Method パータンにそぐわない理由は
両者は関係ありません。「Javaで実装する一般的スタイルがinterfaceな理由」を補足説明しただけで、どちらもFactoryMethodで用いて良いのです。

インターフェースの実装か継承か、という問題は既に本件から離れています。それをまずご理解ください。


>「継承はそのツリー構造内でカプセル化が破られる」とは、継承クラスで新たにメソッドやプロパティーを定義することができることをおっしゃっていますか?

いいえ、違います。機能の「追加」はカプセル化とは別です。むしろあって当然です。

1.protected宣言があるため、継承したクラスに一部のインターフェースが流出する
2.継承の機構上、親クラスの変更はどうしても全ての子クラスに波及する
が、近年継承を避けるようになった主な理由です。
しかし、goto文が「使うべきときに使えば有効」であるように、継承も「使ってはいけない」とみなされているわけではありません。
単に、「何が何でも継承」という一時の風潮を戒めただけです。

>※ 参考書は本屋で見てこようと思います。
著者じゃありませんが、買ってください。良書は身銭を切り、大事な部分に積極的に線を引いて「自分のもの」にすべきです。
    • good
    • 0
この回答へのお礼

どうも、インターフェイスについての理解が足りなかったようです。しかし、いずれのパターンも多様性を意識して作られているので、上記説明は大変参考になりました。

また、勉強していくうちに、私の返答がFactory Method に関して言及している訳ではないことも理解できました。

以降はjava の経験を積むとともに、教えて頂いた書籍で勉強しようと思います。

今回はいろいろと教えていてだき、ありがとうございました。

お礼日時:2006/09/07 08:47

#5です。



>Factory Method の利点とは
>「利用クラスがインターフェイスの実装を知る必要がないため、
>実装クラスの変更がしやすくなる、
>つまり再利用しやすいこと」
>でしょうか?


そういう一面もあります。
    • good
    • 0
この回答へのお礼

自分の理解の再確認ができ、大変参考になりました。ありがとうございます。

お礼日時:2006/09/07 08:48

#4です。

#4に捕捉。
自信なし。

---

#4で
「享受者の利点」
を書きましたが、
これは主に、

「抽象(基本)型と抽象的な操作インターフェースで
コードを記述する利点」
であって、
「Factory Methodを使う利点」
がはっきりしませんね…。

---

あるCollectionを使っている最中、
その「操作ごとに」、操作子たるIteratorを使いたい(生成したい)となれば、
普通に考えれば、
^^^^^^^^^^^^^^
"Iteratorのコンストラクタを呼んでインスタンスを得る"
しかありませんが、
コンストラクタ呼び出しをしてしまえば、

・「コンストラクタ呼び出し」は
「抽象的な操作インターフェース」ではないので、
「抽象的な操作インターフェース」を使うことによる利点が享受できない。
・「コンストラクタ呼び出し」するということは、
「生成すべきIteratorクラスの具体名をいちいち知っていなきゃならない」

ということでもあります。
これらの不都合を解消するのが、
「コンストラクタをメソッドにくるんで
(本来コンストラクタが持つ)インスタンス生成機能自体を
抽象操作インターフェスにする」
という方法です。(このメソッドのことをFactory Methodと言う)
    • good
    • 0

#3です。

#3の捕捉。
自信なし。
---

#3で、

>意味付けすれば、
>「ある型(Person)があり、
…中略…
>そのような機能を提供するしくみ」

と書きましたが、
これは「仕組みを構築する側の視点」であって、

「享受する側の視点」で言えば、

「(ある状況に)もっともふさわしいインスタンスが、
「一つの決まったいんた~ふぇ~す(命令)」で生成できて、
しかもそれは基本型で受け取れちゃうんだよね。
理由はよくわからないけど」

ということになると思います。
「享受者」があるCollectionオブジェクトを使っている最中に、
そのCollectionオブジェクトにふさわしいIteretorが欲しくなった時、
その「ふさわしいIteratorの具体名」を知らずとも(※というか
たいてい「知るわけない」)、
Iterator ite=coll.iterator();
という「一定の決まった記述」で、適切な(collに応じた)Iteratorが受け取れているという。
(というか享受者にとってIteratorはあくまで「単なるIterator」でしかなく、
具体的にそれが「ultraHogeHogeIterator」かどうかは関知しないし
関知するべきでない)

また、これによって、

「(collオブジェクトの種類に変更があっても)「Iterator取得部分の」
コード及びその後の「取得Iterator」に対する操作部分
には変更が生じない。
(言い換えれば、collの種類が何であろうと、
まったく一定のコードでIterator操作できる)」

ということをも意味します。つまりまるっきり「Iteratorの具体名を隠し続けて」
操作できるわけです。
    • good
    • 1
この回答へのお礼

こちらは、オブジェクトを利用する(クラス) 立場からの利点を書かれているのですね。

オブジェクトの実装クラスと利用クラスが分別できている場合、利用クラスでは Iterator ite=coll.iterator(); のように利用してもIterator の実装クラスを知る方法はなく、また知る必要もないですね。

つまり、この例でいえる事=Factory Method の利点とは、

「利用クラスがインターフェイスの実装を知る必要がないため、実装クラスの変更がしやすくなる、つまり再利用しやすいこと」

でしょうか?

お礼日時:2006/09/06 09:08

素人です。

自信ありませんが…。
---

理屈ぬきで、実例を見たほうが理解が早いかも。

Collection#iterator()
List#toArray()
などは
Factory Methodパターンだと思います。

他にも、sunのコレクションフレームワークや、
他のクラスライブラリのソースコード見ていると、
いたるところで"普通に"ちょこちょこ出てくるみたいです。
---

あと、テンプレート的・内部的な、こういうのも。

abstract class Nation{//国
 public void seizi(){//政治
  Person person =boss();//一番えらい人
  person.enzetu();//演説
  person.kaiken();//会見
  person.ohiru();//昼食
  person.Jim();//事務
 }

 abstract protected Person boss();
}

「seizi()を見るには各国、boss()を実装してください」
みたいな。
---

Factory Methodパターンとは、
個人的には、とりあえず意味付けせずに、単純に
^^^^^^^^^^
「コンストラクタ呼び出し」をメソッドでくるむことによって
そこに「いんた~ふぇ~す性」を帯びさせただけ。

という「事実だけ」を捉えるようにしてるかも。

意味付けすれば、
「ある型(Person)があり、
この型を"基本型にもつ"インスタンスを生成したいのだが、
『状況・背景(Nation)に応じて、(基本型の)継承先の型を決定』
してその継承型インスタンスを、(一環した"いんた~ふぇ~す"(命令)を通して)生成したい。
そのような機能を提供するしくみ」
てなところだと思うんですが…。

日本なら小泉、
ロシアならプーチン、
アメリカならブッシュ、
みたいな。
    • good
    • 1
この回答へのお礼

テンプレートパターンと併用するケースは分かりやすく、理解できました。ありがとうございます。

この例を見ても、boss() のように国によって異なる実装がある場合に戻り値としてインターフェイスを使うようですね。そして、親クラスでアルゴリズムを決めておいて、サブクラスでインターフェイスの実装を行う。だから自然と戻り値もインターフェイスとなるのですね。

テンプレートパターン、Factory Method パターンの実例があると分かりやすいです。

お礼日時:2006/09/06 08:40

>戻り値をインターフェイスにする理由はオブジェクト利用者にFactory Method が返すオブジェクトを意識させないためですよね?



違います。
書いてみると分かりますが、複数の実装をもつ場合、「クラスLoggerを継承する」か「インターフェースILoggerを実装する」しかありません。
どちらも、多態性の機能を利用して上位(クラス/インターフェース)に下位の実装クラスのインスタンスを代入して返します。

となると戻り値の型は「継承もとのクラスか」「実装元のインターフェースか」、別の意味では「各Loggerはクラスから派生するかインターフェースを実装するか?」を検討することになります。

Javaは多重継承をサポートしていません。また継承はそのツリー構造内でカプセル化が破られるという弱点があるため、現在は継承は極力使わず、インターフェースを実装するのが主流となります。

なので「戻り値の型はインターフェースが一般的」と書いたのです。
    • good
    • 0
この回答へのお礼

何度もお手数掛けますが、「継承はそのツリー構造内でカプセル化が破られる」とは、継承クラスで新たにメソッドやプロパティーを定義することができることをおっしゃっていますか?

これがFactory Method パータンにそぐわない理由は、

通常、オブジェクト利用者はメソッドを利用する場合、インターフェイスの仕様にあるメソッドを使う。が、継承クラスの場合は継承クラス独自のメソッドを実装できるため、オブジェクト利用者が知らない実装ができてしまう。つまり、継承したクラス独自のメソッドが作れてしまうことが問題。

という理解でよいのでしょうか?

お礼日時:2006/09/06 09:21

・どのようなケースで利用するのか?


主に、インタフェースに対して実装クラスが複数ある場合ですね。
FactoryMethodの場合、Javaでは戻り値の型をインタフェースにするのが一般的です。
たとえばログ出力するLoggerを生成するLogger.getLogger()の場合、
出力先は「ファイル」「コンソール」「syslog(Unix)」「イベント(Win)」「ネットワーク」「パイプ」等が考えられます。
出力先に応じた実装クラスを生成して返してくれるのが、FactoryMethdです。


・利点は何か?
関連を少なくします。
インターフェースとファクトリ以外に、クライアントは知る必要がありません。実装クラスをimportしたりする必要もないし、実装クラスが増えても関係ありません。

とりあえず、参考書籍を挙げておきます。分かりやすいですよ。

参考URL:http://www.hyuki.com/dp/
    • good
    • 0
この回答へのお礼

お返事ありがとうございます。

質問してから私もWEB+DB の特集などを参考にFactory Method パターンの利用価値について勉強してますが、なにせ初心者なのでなかなか理解できません。追加で質問させてください。

「Javaでは戻り値の型をインタフェースにする」とありますが、戻り値をインターフェイスにする理由はオブジェクト利用者にFactory Method が返すオブジェクトを意識させないためですよね?例えば、ログ出力をsyslog にする場合、getLogger( "syslog" ) のように、利用者が欲しいオブジェクトをFactory Method に教えている意味では、意識していると思うのですが。戻りをインターフェイスにする価値がいまいち分かってないと思います。

お手数ですが、よろしければ教えてください。

※ 参考書は本屋で見てこようと思います。ありがとうございます。

お礼日時:2006/09/05 14:05

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!


おすすめ情報