質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
デザインパターン

デザインパターンは、ソフトウェアのデザインでよく起きる問題に対して、解決策をノウハウとして蓄積し再利用出来るようにした設計パターンを指します。

Q&A

解決済

2回答

6482閲覧

【デザインパターン】Factory MethodとStrategyの使い分けについて

takenyaan

総合スコア119

デザインパターン

デザインパターンは、ソフトウェアのデザインでよく起きる問題に対して、解決策をノウハウとして蓄積し再利用出来るようにした設計パターンを指します。

0グッド

0クリップ

投稿2016/09/22 07:19

編集2016/09/22 07:28

Factory MethodパターンとStrategyパターンは、そもそも目的自体が異なるもの(Factory Methodはオブジェクトの生成の切り替え、Strategyはオペレーションの切り替え)と理解していますが、
WikipediaのFactory Method パターンの利用例を参照していて疑問に思ったので質問させてください。

Wikipediaの利用例↓

Java

1import java.util.ArrayList; 2import java.util.Arrays; 3import java.util.Collections; 4import java.util.Comparator; 5import java.util.List; 6 7// Creatorに相当する 8abstract class ListPrinter { 9 // anOperationに相当する 10 public void printList(List<String> list) { 11 Comparator<String> comparator = createComparator(); 12 list = new ArrayList<String>(list); 13 14 Collections.sort(list, comparator); 15 16 for (String item : list) { 17 System.out.println(item); 18 } 19 } 20 21 // factoryMethodに相当する 22 protected abstract Comparator<String> createComparator(); 23} 24 25// ConcreteCreatorに相当する 26class DictionaryOrderListPrinter extends ListPrinter { 27 @Override 28 protected Comparator<String> createComparator() { 29 return new DictionaryOrderComparator(); 30 } 31} 32 33// java.util.ComparatorがProductに相当する 34 35// ConcreteProductに相当する 36class DictionaryOrderComparator implements Comparator<String> { 37 @Override 38 public int compare(String str1, String str2) { 39 return str1.compareTo(str2); 40 } 41} 42 43// ConcreteCreatorに相当する 44class LengthOrderListPrinter extends ListPrinter { 45 @Override 46 protected Comparator<String> createComparator() { 47 return new LengthOrderComparator(); 48 } 49} 50 51// ConcreteProductに相当する 52class LengthOrderComparator implements Comparator<String> { 53 public int compare(String str1, String str2) { 54 return str1.length() - str2.length(); 55 } 56} 57 58// メインクラス 59public class FactoryMethodSample { 60 public static void main(String args[]) { 61 List<String> list = Arrays.asList("いちご", "もも", "いちじく"); 62 63 System.out.println("五十音順で表示:"); 64 new DictionaryOrderListPrinter().printList(list); 65 66 System.out.println(); 67 68 System.out.println("長さ順で表示:"); 69 new LengthOrderListPrinter().printList(list); 70 } 71}

上記の利用例は下記のStrategyパターンで置き換えられると思いますが、
具体的にそれぞれのメリット・デメリットが腹に落ちず、使い分けの基準等あればご教示いただけますでしょうか。

個人的には、Strategyの方が、構成要素が少ない分利用者側での拡張が容易(Comparatorの実装クラスだけ用意すればいい)かと思います。
一方で、Printerが何のComparatorを持っているかを隠ぺいしたい場合はFactoryの方が適していると思います。
利用者側というより、提供者側でComparatorを拡張したい場合に利用者に影響がない。

import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; // Creatorに相当する class ListPrinter { Comparator<String> comparator; // コントラクタでStrategyを渡す ListPrinter(Comparator<String> comparator) { this.comparator = comparator; } // anOperationに相当する public void printList(List<String> list) { list = new ArrayList<String>(list); Collections.sort(list, comparator); for (String item : list) { System.out.println(item); } } } // ConcreteProductに相当する class DictionaryOrderComparator implements Comparator<String> { @Override public int compare(String str1, String str2) { return str1.compareTo(str2); } } // ConcreteProductに相当する class LengthOrderComparator implements Comparator<String> { public int compare(String str1, String str2) { return str1.length() - str2.length(); } } // メインクラス public class FactoryMethodSample { public static void main(String args[]) { List<String> list = Arrays.asList("いちご", "もも", "いちじく"); System.out.println("五十音順で表示:"); new ListPrinter(new DictionaryOrderComparator()).printList(list); System.out.println(); System.out.println("長さ順で表示:"); new ListPrinter(new LengthOrderComparator()).printList(list); } }

皆さまのご意見をお待ちしています。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

そもそも、ファクトリは生成、ストラテジーは交換、と目的が別です。
newを置きかえるのがファクトリ、switchを置きかえるのがストラテジーです。

ファクトリは一種類(のクラスから)しか生成しなくても構いません。
切り替えがなくても、初期化の手続きを隠蔽するメリットがあります。

ファクトリメソッドは、テンプレートメソッドのファクトリへの応用です。
そして、テンプレートメソッドとストラテジーは交換という目的が共通です。

ですから、共通点を見いだす質問者の方の気持ちも分からなくもありません。

しかし、ファクトリメソッドは生成が主、交換が従です。
生成(を隠蔽)したい → 生成物を切り替えたい → ファクトリメソッド
という順番であって、切り替えだけが目的なら、それはストラテジーの方が便利でしょう。

しかし、切り替えならストラテジーの方がスムーズだからと、
ファクトリメソッドをストラテジーで機械的に置きかえるのは疑問です。

たとえ便利でもnewをswitchで置きかえたら違和感があることの延長線上です。
部分的に行数が少なく書けるのと、プログラム全体の保守性などは別の問題ですから。

ですから、ファクトリを使うかどうかは、まずもって生成を隠蔽したいかどうかです。

投稿2016/09/22 10:02

編集2016/09/22 10:05
LLman

総合スコア5592

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

まず「Factory Methodはオブジェクトの生成の切り替え」というご認識は、表現は違えど
「オペレーションの切り替え」と同じ意味にとらえられているのではないかという印象を受けました。

そのため、Wikipediaの利用例が「オペレーション的な」使用例だから、Strategyパターンで置き換えられるのではないかと考えて違いが分からなくなって混乱されたのではないでしょうか?

私はFactory Methodは「切り替え」と表現されるほど動的でオペレーション的なニュアンスのパターンでは無いと思っています。

Factory Methodは例えば親クラスと共通の振る舞いは有るけれど少しずつ違う振る舞いや見た目を表現した子クラスを沢山作る際に、違いの部分だけを子クラスに実施させれば、親クラスの振る舞いが再利用ができて可読性や保守性が上がると考えています。

投稿2016/09/22 09:14

tomneco

総合スコア24

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takenyaan

2016/09/22 09:34

ご回答ありがとうございます。 Factory Methodのメリットについては認識相違ございませんが、 ではズバリ上記の利用例だと、Factory Methodを設けることによる恩恵は受けられているのでしょうか。 後者の例に比べて前者の例が優れている点はありますか?
tomneco

2016/09/22 10:12 編集

>後者の例に比べて前者の例が優れている点はありますか? あくまでも私の感想ですが、前者の例がFactoryMethodSampleクラスで動的に使用している感があるので、実際の開発では後者のStrategyに置き換えるのもアリだと思います。 ただ、コード例に細かく言うのも何ですが、後者の例の場合はListPrinterを2回newしているので、Strategyにする恩恵や意味が薄いと思います。 ListPrinterはシングルトンにするとか、newを1回きりにしてインスタンスをずっと変更しないまたは、変更してはいけない理由がある上に、挙動を動的に切り替えたい場合にStrategyを使うものだと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問