ファクトリパターン(Factoryパターン)は、オブジェクトを作る処理を専用のクラスやメソッドに集め、呼び出し側が具体的なクラス名を知らなくても目的のオブジェクトを受け取れるようにする設計手法です。ただ、この呼び名はGoFのFactory MethodとAbstract Factory、GoFに含まれないSimple Factory、Javaでおなじみの静的ファクトリメソッドをまとめて指すことが多く、区別しないまま読むと解説記事ごとに話が食い違って見えます。この記事では4つの違いを先に整理し、実行確認したJava・Pythonのコード、Spring Bootでの書き方、使わない方がいい場面まで順に説明します。
まとめ:ファクトリパターンの要点
- 定義:生成処理(
new)を呼び出し側から切り離し、1か所に集める設計。呼び出し側はインタフェースだけに依存する。 - 4つの区別:条件分岐で作り分けるSimple Factory(GoF外のイディオム)、生成をサブクラスに任せるFactory Method、関連する部品一式を差し替えるAbstract Factory、コンストラクタ代わりの静的ファクトリメソッド。
- Spring Boot:実装クラスを
Listで受け取り、各実装が返すキーでマップを組むのが扱いやすい。Map<String, 型>を直接注入するとキーはBean名になる。 - 使いどころ:作る型が実行時の条件で変わる、生成手順が長い、型が今後増える、テストで偽物に差し替えたい、のいずれかがあるとき。
- 使わない方がいい場面:作る型が1つで増える予定もなく、生成も
new1行で済むとき。挟んでもクラスと間接参照が増えるだけになる。
ファクトリパターンの定義と解決する問題
通知クラスの生成分散による修正漏れ
通知機能を例にします。メール通知しか無かった頃に、画面・バッチ・APIの各所で new EmailNotifier(address) と書いていたとします。ここにSMS通知が加わると、呼び出し箇所をすべて探して if を足すことになり、1か所の直し漏れがそのまま不具合になります。問題の根は「どのクラスを作るか」という判断が呼び出し側に散らばっていることです。
ファクトリパターンはこの判断を1か所に集めます。呼び出し側は Notifier というインタフェースだけを扱い、どの実装を作るかはファクトリが決めます。SMSが加わっても直すのはファクトリだけで、呼び出し側は変わりません。呼び出し側を変更から守るという意味で「開放閉鎖の原則」(拡張に開き、修正に閉じる)に沿う形です(ファクトリ自体の修正が残る点は後述します)。実装をインタフェース越しに扱えるのはポリモーフィズム(多態性)のおかげです。
「factory」「ファクトリクラス」という語の範囲
英語の factory は「工場」で、プログラミングでは「オブジェクトを作って返すもの」を広く指します。オブジェクトを返す関数ならファクトリ関数、そのためのクラスならファクトリクラス(Factoryクラス)、メソッドならファクトリメソッドと呼ばれます。JavaScriptでクラスを使わずにオブジェクトリテラルを返す関数を「ファクトリ関数」と呼ぶのも同じ意味です。
紛らわしいのが、同じ名前を持つ別物です。LaravelやRuby on Railsの「モデルファクトリ」(Factory Bot等)はテスト用のダミーデータを作る仕組みで、名前の由来は同じでも本記事の設計パターンとは目的が違います。Azure Data Factoryはデータ連携サービスの製品名で、設計パターンとは無関係です。
Simple Factory・Factory Method・Abstract Factoryの違い
先に一覧で違いを示し、そのあと1つずつ説明します。
| 名称 | GoF | 作り分けの手段 | 作るもの | 向く場面 |
|---|---|---|---|---|
| Simple Factory | 含まれない | 条件分岐・辞書 | 1種類の製品 | 型が数個で分岐が単純 |
| Factory Method | 生成パターン | 継承(サブクラス) | 1種類の製品 | 手順は共通で部品だけ違う |
| Abstract Factory | 生成パターン | ファクトリ自体の差し替え | 関連する製品一式 | テーマ・DB種別の切替 |
| 静的ファクトリメソッド | 含まれない | クラスのstaticメソッド | 戻り値型に適合するインスタンス | コンストラクタの代替 |
GoF本『Design Patterns』(1994年、Gamma・Helm・Johnson・Vlissidesの4名)が定義した23パターンのうち、生成に関するパターンはAbstract Factory・Builder・Factory Method・Prototype・Singletonの5つです。「ファクトリパターン」という名前のパターンはGoFには無く、本記事では関連する4つの用法を整理しています。これらは排他的な分類ではなく、たとえばSimple Factoryを静的メソッドで実装することもできます。23パターンの全体像はGoFデザインパターン23種類の一覧にまとめています。
Simple Factory:GoFに含まれない定番イディオム
条件に応じて switch や辞書で実装を選び、インスタンスを返すだけの一番素朴な形です。『Head First デザインパターン』第4章でも、Simple FactoryはFactory MethodとAbstract Factoryに入る前の導入として扱われ、正式なデザインパターンとは区別されています。後述するJava例のNotifierFactoryがこの形です。型が数個で分岐も単純なうちは、これで十分です。
Factory Method:4つの役割とサブクラスによる生成の差し替え
GoFの定義は「オブジェクト生成のインタフェースを定め、どのクラスを生成するかはサブクラスに決めさせる」です。登場する役割は4つで、作られる側の型 Product とその実装 ConcreteProduct、生成メソッドを持つ親クラス Creator と、それをオーバーライドして具体的な製品を返す ConcreteCreator です(Wikipedia「Factory Method パターン」のクラス図もこの4つで描かれています)。後述するJava例では RowFormatter がProduct、ReportExporter がCreator、CsvExporter と TsvExporter がConcreteCreatorに当たり、各サブクラスが返すラムダ式がConcreteProductです。
後述するJava例のCreatorは共通の処理手順を持ち、その途中で生成メソッドを呼びます。ただし、Factory Method自体に共通手順の実装は必須ではありません。手順を親クラスに固定して一部だけをサブクラスに任せる形はTemplate Methodパターンそのもので、Wikipediaも「factory methodは普通template methodであるanOperationから呼ばれる」とGoFを出典に説明しています。CSV出力とTSV出力のように、流れは同じで使う部品だけが違う場合に向きます。手順側の書き方はテンプレートメソッドパターンとは?Java・Python・Spring Bootの実装例とStrategyとの違いで扱っています。
この例のように製品ごとにConcreteCreatorを分ける実装では、製品の追加に伴ってサブクラスも増えます。ただし、GoF原典には引数によって複数の製品を作り分ける実装もあり、必ず一対一で増えるわけではありません。手順の共通部分が薄く、違いが「どのクラスを作るか」だけなら、継承を使わないSimple Factoryの方が読みやすくなります。
Abstract Factory:関連する部品一式の差し替え
GoFの定義は「関連し合う、または依存し合うオブジェクト群を、具体的なクラスを指定せずに生成するインタフェースを提供する」です。ライトテーマ用のボタンとダイアログ、ダークテーマ用のボタンとダイアログのように、組み合わせを崩してはいけない部品の一式を作るときに使います。ファクトリを差し替えれば一式がまとめて入れ替わり、同じ具象ファクトリから部品を取る限りテーマは混在しません。ただし、型定義だけでは、異なるファクトリから取得した部品を混ぜて使うことまでは禁止できません。また、製品の種類(例:チェックボックス)を1つ足すと、ファクトリのインタフェースとすべての実装を直すことになります。
静的ファクトリメソッド:コンストラクタの代わりの名前付き生成
『Effective Java 第3版』の項目1「コンストラクタの代わりにstaticファクトリメソッドを検討する」で広まった書き方です。JDKの List.of・Integer.valueOf・Calendar.getInstance のように、意図を表す名前を付けたstaticメソッドがインスタンスを返します。GoFのFactory Methodとは名前が似ているだけで別物で、継承も抽象メソッドも使いません。利点は、名前で意図を示せること、毎回新しいインスタンスを作らずに済むこと、戻り値の型をサブタイプにできることです。
Javaでのファクトリパターン実装例:3種類の書き分け
Simple Factory・Factory Method・Abstract Factoryの3つを1ファイルにまとめたコードです。Java 17以降のsealedインタフェースとswitch式を使っており、Temurin 25で javac と java を実行して動作を確かめています。
import java.util.List;
// 1. Simple Factory
sealed interface Notifier permits EmailNotifier, SmsNotifier {
void send(String message);
}
record EmailNotifier(String address) implements Notifier {
public void send(String message) {
System.out.println("mail to " + address + ": " + message);
}
}
record SmsNotifier(String phone) implements Notifier {
public void send(String message) {
System.out.println("sms to " + phone + ": " + message);
}
}
final class NotifierFactory {
private NotifierFactory() {}
static Notifier create(String channel, String to) {
return switch (channel) {
case "email" -> new EmailNotifier(to);
case "sms" -> new SmsNotifier(to);
default -> throw new IllegalArgumentException("unknown channel: " + channel);
};
}
}
// 2. Factory Method
interface RowFormatter {
String format(List<String> cols);
}
abstract class ReportExporter {
// 手順は共通。何で整形するかだけをサブクラスが決める
public final String export(List<List<String>> rows) {
RowFormatter f = createFormatter();
StringBuilder sb = new StringBuilder();
for (List<String> r : rows) {
sb.append(f.format(r)).append('\n');
}
return sb.toString();
}
protected abstract RowFormatter createFormatter(); // Factory Method
}
class CsvExporter extends ReportExporter {
protected RowFormatter createFormatter() {
return cols -> String.join(",", cols);
}
}
class TsvExporter extends ReportExporter {
protected RowFormatter createFormatter() {
return cols -> String.join("\t", cols);
}
}
// 3. Abstract Factory
interface Button { String render(); }
interface Dialog { String render(); }
interface UiFactory {
Button createButton();
Dialog createDialog();
}
class LightUiFactory implements UiFactory {
public Button createButton() { return () -> "[light button]"; }
public Dialog createDialog() { return () -> "[light dialog]"; }
}
class DarkUiFactory implements UiFactory {
public Button createButton() { return () -> "[dark button]"; }
public Dialog createDialog() { return () -> "[dark dialog]"; }
}
public class Main {
static void drawScreen(UiFactory ui) {
System.out.println(ui.createButton().render() + " " + ui.createDialog().render());
}
public static void main(String[] args) {
NotifierFactory.create("email", "[email protected]").send("ビルド成功");
NotifierFactory.create("sms", "090-0000-0000").send("ビルド失敗");
List<List<String>> rows = List.of(List.of("id", "name"), List.of("1", "factory"));
System.out.print(new CsvExporter().export(rows));
System.out.print(new TsvExporter().export(rows));
drawScreen(new DarkUiFactory());
try {
NotifierFactory.create("fax", "x");
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}
}
実行結果は次のとおりです(TSVの区切りはタブ)。この例は設計パターンの説明用で、値に含まれるカンマ・引用符・改行・タブのエスケープは省略しています。実用のCSV出力には、それらを扱えるライブラリを使ってください。
mail to [email protected]: ビルド成功
sms to 090-0000-0000: ビルド失敗
id,name
1,factory
id name
1 factory
[dark button] [dark dialog]
unknown channel: fax
Simple Factoryの default で例外を投げているのは、未知の値で null を返すと、呼び出し側から遠く離れた場所で NullPointerException が出て原因をたどりにくくなるためです。Notifier をsealedにしておくと、実装を足したときに permits を書き換える必要があり、許可する実装の一覧を1か所で確認できます。ただし、この例のswitchは文字列を判定しているため、生成分岐の追加漏れをコンパイラは検出しません。通知先ごとのテストは別に必要です。
Pythonでのファクトリパターン実装例:辞書登録と代替コンストラクタ
Pythonではクラスも関数のように呼び出してインスタンスを生成できるオブジェクトなので、ファクトリクラスを作らず、生成関数を辞書に登録する形が素直です。代替コンストラクタは @classmethod で書きます。Python 3.14.6と3.9.6で実行を確認しています。
from dataclasses import dataclass
from typing import Callable, Protocol
class Notifier(Protocol):
def send(self, message: str) -> None: ...
@dataclass
class EmailNotifier:
address: str
def send(self, message: str) -> None:
print(f"mail to {self.address}: {message}")
@dataclass
class SlackNotifier:
webhook: str
def send(self, message: str) -> None:
print(f"slack {self.webhook}: {message}")
@classmethod
def from_env(cls, env: dict[str, str]) -> "SlackNotifier":
# 代替コンストラクタ(クラスメソッドのファクトリ)
return cls(env["SLACK_WEBHOOK"])
# 生成関数を辞書に登録する形のファクトリ
_REGISTRY: dict[str, Callable[[str], Notifier]] = {
"email": EmailNotifier,
"slack": SlackNotifier,
}
def create_notifier(channel: str, to: str) -> Notifier:
factory = _REGISTRY.get(channel)
if factory is None:
raise ValueError(f"unknown channel: {channel}")
return factory(to)
create_notifier("email", "[email protected]").send("deploy done")
SlackNotifier.from_env({"SLACK_WEBHOOK": "hooks/xyz"}).send("deploy done")
try:
create_notifier("fax", "x")
except ValueError as e:
print(e)
mail to [email protected]: deploy done
slack hooks/xyz: deploy done
unknown channel: fax
辞書登録にすると、新しい通知先は _REGISTRY に1行足すだけで増やせ、if/elif の列が伸び続けることもありません。_REGISTRY[channel] を try で囲んで KeyError を捕まえる場合、生成関数の呼び出しまで同じtryブロックに含めると、その内部の例外も「未知の通知先」と誤認するため、.get() で存在確認と生成を分けています。抽象基底クラスを使ったFactory Methodの書き方や、ファクトリ関数・classmethodの使い分けはPythonのFactory Method(ファクトリメソッド)実装|ファクトリ関数・classmethodとの使い分けで詳しく扱っています。
Spring Bootでのファクトリパターン:Bean選択と実行時引数による生成
Spring Bootでコンテナ管理の実装を選ぶ場合は、上のJava例のように new を並べる代わりに、実装クラスをBeanとして登録し、コンテナに集めさせたものからファクトリが選べます。以下のコードはSpring Boot 4.1.2(Spring Framework 7.0.10)とJava 25で mvn spring-boot:run を実行して確かめています。
Map<String, Bean>注入におけるBean名とキーの対応
Springは同じ型のBeanをまとめて注入できます。Spring Frameworkのリファレンスには「キーの型がStringであれば、型付きのMapも自動注入できる。値はその型のすべてのBean、キーは対応するBean名」とあります。実際に Map<String, Notifier> を受け取ってキーを出力すると [emailNotifier, smsNotifier] で、@Component を付けたクラス名の先頭を小文字にした名前でした。
手軽ですが、本記事では外部の業務キーをBean名から独立させるため、各実装がキーを返す方式を採用します。SmsNotifier に @Component("sms") と名前を付けるとキーは sms に変わりました。このようにBean名を明示すればクラス名を変えてもキーは保てますが、Bean名を外部キーとして使う限り、Bean名を変えた時点で外部から渡される値(設定ファイルやリクエストの channel)と一致しなくなります。コンパイルは通るので、気付くのは実行時です。
List注入による業務キーのマップ構築と重複検出
そこで、キーを各実装に返させます。ファクトリは List<Notifier> を受け取り、channel() の戻り値でマップを組み立てます。以下の2つのコードブロックは、前述のJava例とは別のSpring Bootプロジェクトの同じ名前付きパッケージに置き、AppクラスをApp.javaに保存します。java.utilのList・Map、java.util.functionのFunction、java.util.streamのCollectors、およびSpringの各型のimport文は省略しています。
interface Notifier {
String channel();
void send(String to, String message);
}
@Component
class EmailNotifier implements Notifier {
public String channel() { return "email"; }
public void send(String to, String message) {
System.out.println("mail to " + to + ": " + message);
}
}
@Component
class SmsNotifier implements Notifier {
public String channel() { return "sms"; }
public void send(String to, String message) {
System.out.println("sms to " + to + ": " + message);
}
}
@Component
class NotifierFactory {
private final Map<String, Notifier> byChannel;
NotifierFactory(List<Notifier> notifiers) {
this.byChannel = notifiers.stream()
.collect(Collectors.toMap(Notifier::channel, Function.identity()));
}
Notifier get(String channel) {
Notifier n = byChannel.get(channel);
if (n == null) {
throw new IllegalArgumentException("unknown channel: " + channel);
}
return n;
}
}
新しい通知先は @Component を付けたクラスを1つ足すだけで増え、ファクトリにもswitchにも手を入れません。Simple Factoryの「型を足すたびにファクトリを直す」という弱点が、ここで消えます。
もう1つの利点は、キーの重複が起動時に分かることです。channel() が同じ "email" を返す LegacyMailNotifier を足して起動すると、Collectors.toMap が IllegalStateException: Duplicate key email を投げ、notifierFactory Beanの生成に失敗してアプリが起動しません。後から登録した実装が黙って前の実装を上書きするより、起動時に止まる方が事故を防げます。
ObjectProviderによる実行時引数付きprototype Beanの生成
通知クラスのように状態を持たない実装はシングルトンのBeanで足りますが、テナントIDなど実行時の値を持たせて毎回新しく作りたいオブジェクトもあります。この場合はprototypeスコープの @Bean メソッドを用意し、ObjectProvider の getObject(Object... args) で引数を渡して生成します。ObjectProviderのJavadocはこのメソッドを、明示的なコンストラクタ引数を指定できる取得方法として説明しています。
record ReportJob(String tenant) {}
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
@Bean
@Scope("prototype")
ReportJob reportJob(String tenant) {
return new ReportJob(tenant);
}
@Bean
CommandLineRunner run(Map<String, Notifier> rawMap, NotifierFactory factory,
ObjectProvider<ReportJob> jobs) {
return args -> {
System.out.println("Map keys = " + rawMap.keySet());
factory.get("email").send("[email protected]", "build ok");
factory.get("sms").send("090-0000-0000", "build failed");
ReportJob a = jobs.getObject("tenant-a");
ReportJob b = jobs.getObject("tenant-b");
System.out.println(a + " " + b + " same=" + (a == b));
try {
factory.get("fax");
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
};
}
}
Map keys = [emailNotifier, smsNotifier]
mail to [email protected]: build ok
sms to 090-0000-0000: build failed
ReportJob[tenant=tenant-a] ReportJob[tenant=tenant-b] same=false
unknown channel: fax
呼ぶたびに別のインスタンスが返ります(same=false)。引数を渡さずに jobs.getObject() を呼ぶと、Springは String 型のBeanを探して見つからず、UnsatisfiedDependencyException になります。引数付きのprototype Beanは、必ずこの経路で取り出してください。業務システムでこうした生成の設計から相談したい場合は、Java・Spring Bootでのフルスクラッチ開発の支援も行っています。
標準ライブラリとフレームワークに組み込まれたファクトリ
ファクトリは、自分で書く前に標準ライブラリやフレームワークの中ですでに使っています。
JDKの静的ファクトリメソッド
List.of()(Java 9以降)は変更不可のリストを返しますが、戻り値の具体クラスは公開されていません。JDK 25(Temurin 25.0.4.1)で戻り値の getClass() を出力すると、要素が1〜2個なら ImmutableCollections$List12、0個や3個以上なら ImmutableCollections$ListN が返り、空リストは同じインスタンスが使い回されていました。これらは実装上の詳細で、API仕様は具体クラスやインスタンスの同一性を保証しません。要素数で実装を切り替えたりインスタンスを使い回したりできるのは、コンストラクタではなくファクトリメソッドだからです。Calendar.getInstance() は既定のタイムゾーンとロケールに応じたCalendarを返し、DocumentBuilderFactory.newInstance() はXMLパーサの実装を実行時に選びます。
SpringのBeanFactoryとFactoryBean
Spring Frameworkでは、DIコンテナそのものが BeanFactory というファクトリです。@Bean を付けたメソッドはコンテナに呼ばれるファクトリメソッドで、生成の手順が込み入ったBeanは @Bean メソッドか、FactoryBean インタフェースを実装したクラスに作らせます。コンテナの仕組みとBeanのスコープはIoCコンテナとは?制御の反転の仕組みとSpring IoCコンテナ・Beanのスコープとライフサイクルを動くコードで解説で説明しています。
Pythonの代替コンストラクタと.NETのHTTPクライアント生成
Pythonの dict.fromkeys() や datetime.fromtimestamp() はクラスメソッドによる代替コンストラクタで、静的ファクトリメソッドと同じ発想です。.NETでは IHttpClientFactory が、名前付きの設定を持つ HttpClient の生成を担います。Microsoftのドキュメントによると、ファクトリは内部のハンドラ(接続プールを持つ)をキャッシュして寿命を管理し、既定の寿命は2分です。ハンドラの過剰な生成はソケット枯渇を招くおそれがあり、接続を更新せずに使い続けるとDNS変更を反映できない場合があるため、この管理をファクトリに任せます。
ファクトリパターンの使いどころと使わない方がいい場面
ファクトリの導入条件:型選択・生成手順・拡張・テスト
次のどれかに当てはまるなら、ファクトリを挟む価値があります。1つ目は、作る型が設定値や入力で実行時に変わるとき。2つ目は、生成に設定の読み込みや依存オブジェクトの組み立てが要り、呼び出し側に書くと長くなるとき。3つ目は、対応する型が今後増える見込みがあるとき。4つ目は、テストで本物の代わりに偽物(モック)を差し込みたいときです。本記事の例でいえば、channel の値で EmailNotifier と SmsNotifier を切り替える処理が1つ目、SlackNotifier.from_env() のように、引数で受け取った設定辞書から必要な値を取り出して組み立てる処理が2つ目に当たります。2つ目と4つ目は、型が1種類でも成り立ちます。
ファクトリを挟まない方がいい場面
作る型が1つで、増える予定もなく、生成も new 1行で済むなら、ファクトリは不要です。UserFactory.create() が中で new User() を返すだけなら、読む人がファクトリを開いて確かめる手間が増えるだけです。「将来増えるかもしれない」だけを理由に先回りで作るのはやめ、2つ目の型が現れた時点で導入すれば間に合います。
もう1つの失敗パターンは、Simple Factoryの switch が数十分岐に育つことです。型を足すたびに既存の分岐を書き換えるファクトリは、それ自体が変更に閉じていません。この段階に来たら、Pythonの例のような辞書への登録か、Spring Bootの例のようにコンテナに実装を集めさせる形へ移るのが妥当です。
ファクトリパターンとStrategy・Builder・DIの違い
Strategyパターンとの違い
ファクトリは「何を作るか」を切り替える生成のパターン、Strategyは「どう処理するか」を切り替える振る舞いのパターンです。両者は対立せず、設定値から適切なStrategyの実装をファクトリで選んで渡す併用がよくあります。Spring Bootの例の NotifierFactory も、選ばれた Notifier をStrategyとして使っている形です。Strategy側の実装例はStrategyパターンとは?if文の分岐を置き換える仕組みとPHP・Java・Python・TypeScriptの実装例を参照してください。
Builderパターンとの違い
ファクトリは「どのクラスを作るか」を隠し、多くは1回の呼び出しで完成品を返します。Builderは、引数の多いオブジェクトを name() や timeout() のように1項目ずつ組み立てる手順を整理します。生成する実装や製品群を選ぶならファクトリ、構築の段階が多いならBuilderです。詳しくはBuilderパターンとは?GoFの4役割とGoのFunctional Optionsとの使い分けで解説しています。
依存性の注入(DI)との関係
ファクトリを使っても、呼び出し側が NotifierFactory.create() を自分で呼ぶ限り、ファクトリという具体物への依存は残ります。DIはさらに一歩進め、必要なオブジェクトを外から渡してもらうことで、生成を呼ぶ行為そのものを呼び出し側から消します。DIコンテナは内部でファクトリを使っており、両者は代替ではなく層の違いです。考え方の違いは依存性逆転の原則(DIP)とは?意味・違反コードの直し方とDI(依存性の注入)との違いで整理しています。
ファクトリパターンに関するよくある質問
ファクトリパターンとは何ですか?読み方は?
読みは「ファクトリーパターン」または「ファクトリパターン」で、英語では Factory pattern です。オブジェクトの生成処理を専用のクラスやメソッドに集め、呼び出し側が具体クラスを知らずに済むようにする設計です。GoFの正式名はFactory MethodとAbstract Factoryで、「ファクトリパターン」はそれらとSimple Factoryを含む総称として使われます。
Factory Methodパターンのメリットは?
どのクラスを生成するかをサブクラスに任せられることです。親クラス(Creator)の処理を書き換えずに、生成する製品だけを差し替えられます。呼び出し側は製品のインタフェースだけに依存するので、製品を足しても呼び出し側は変わりません。一方で、製品ごとに生成用サブクラスを設ける実装では、製品の追加に伴ってクラス数も増えます。
Factory MethodとAbstract Factoryの違いは?
Factory Methodは1種類の製品の生成をサブクラスに任せ、継承で作り分けます。Abstract Factoryはボタンとダイアログのような関連製品の一式を作るファクトリを丸ごと差し替え、組み合わせの整合を保ちます。単品か一式かが分かれ目です。
Spring Bootでファクトリパターンはどう書きますか?
実装クラスに @Component を付けてBeanにし、ファクトリのコンストラクタで List<インタフェース> を受け取って、各実装が返すキーでマップを組みます。Map<String, インタフェース> を直接注入するとキーはBean名になります。Bean名を外部キーに合わせて明示する方法もありますが、両者を独立させたい場合はList注入が適しています。実行時の引数が要るオブジェクトはprototypeの @Bean と ObjectProvider.getObject(args) で作ります。
ファクトリとDIコンテナはどちらを使うべきですか?
SpringやASP.NET CoreのようにDIコンテナがある環境では、まずコンテナに生成を任せ、コンテナで表しにくい条件付きの生成だけをファクトリ(Springなら @Bean メソッドや FactoryBean、実装の選択ならList注入のファクトリ)に書きます。コンテナの無い小さなスクリプトやライブラリでは、ファクトリ関数1つで足ります。