AbstractFactory パターン — 「関連するオブジェクト群」を一式で切り替える
Factory Method との違いを 1 分で整理し、OS 別 UI / DB 別 Repository / テナント別実装など、実務での使いどころを示す。

どういう問題を解くパターンか
互いに関連する複数の型のオブジェクトを、一貫した「ファミリー」として生成 するパターンです。
- OS ごとに差し替える UI 部品 (Mac 用 Button + Mac 用 Dialog + Mac 用 Menu / Win 用 Button + …)
- DB エンジン別の Repository 一式 (MySQL 用 UserRepo + MySQL 用 OrderRepo / Postgres 用 UserRepo + Postgres 用 OrderRepo)
- SaaS でテナントごとに差し替える実装一式
Factory Method が 「1 種類の抽象プロダクト」 を扱うのに対し、 Abstract Factory は 「複数種類の抽象プロダクトを束ねて 1 ファミリー」 として扱います。
Factory Method との違い (1 分で理解)
| Factory Method | Abstract Factory | |
|---|---|---|
| 生成する型の数 | 1 種類 | 複数種類 (関連を持つ) |
| バリエーションの単位 | 型ごと | ファミリーごと |
| 典型例 | Logger.of(name) | UiToolkit.forOs(OS.MAC) |
| クラス数 | 少ない | 多い (型 × バリエーション分) |
判断軸: 作りたいオブジェクトが 互いに協調して動く必要がある (Mac Button と Mac Dialog を混ぜて使う) なら Abstract Factory。それ以外は Factory Method で足ります。
いつ使う / 使わない
| 使うべき | 使わないほうがよい |
|---|---|
| 複数の型を 「ファミリー」 として切り替える | 単一型の生成 |
| 異なるファミリーの実装を混ぜると壊れる | 混ぜても壊れない |
| バリエーション (OS/DB/テナント) が固定数 | バリエーションが頻繁に増える |
バリエーションが頻繁に増える環境では、
ファミリーの数だけファクトリクラスを増やし続ける 必要があり、
クラス爆発を招きがちです。
その場合は 「設定駆動の DI」 (Spring の @Profile や @ConditionalOnProperty) に寄せた方が楽です。
実装: UI ツールキットの例
抽象プロダクトを 2 つ (Button, Dialog)、
バリエーションを 2 つ (Mac, Windows) で考えます。
抽象プロダクト
public interface Button {
void render();
}
public interface Dialog {
void show(String message);
}
Mac ファミリー
final class MacButton implements Button {
@Override public void render() {
System.out.println("[Mac] rendering rounded button");
}
}
final class MacDialog implements Dialog {
@Override public void show(String message) {
System.out.println("[Mac] sheet: " + message);
}
}
Windows ファミリー
final class WinButton implements Button {
@Override public void render() {
System.out.println("[Win] rendering square button");
}
}
final class WinDialog implements Dialog {
@Override public void show(String message) {
System.out.println("[Win] modal: " + message);
}
}
抽象ファクトリと具象ファクトリ
public interface UiToolkit {
Button createButton();
Dialog createDialog();
static UiToolkit forOs(Os os) {
return switch (os) {
case MAC -> new MacUiToolkit();
case WINDOWS -> new WinUiToolkit();
};
}
}
final class MacUiToolkit implements UiToolkit {
public Button createButton() { return new MacButton(); }
public Dialog createDialog() { return new MacDialog(); }
}
final class WinUiToolkit implements UiToolkit {
public Button createButton() { return new WinButton(); }
public Dialog createDialog() { return new WinDialog(); }
}
public enum Os { MAC, WINDOWS }
クライアント
呼び出し側は ファクトリ 1 つだけ受け取れば、ファミリー全体を正しく使える。 Mac と Windows の実装を混ぜる事故が起きません。
public final class App {
private final UiToolkit toolkit;
public App(UiToolkit toolkit) {
this.toolkit = toolkit;
}
public void run() {
toolkit.createButton().render();
toolkit.createDialog().show("ようこそ");
}
public static void main(String[] args) {
new App(UiToolkit.forOs(Os.MAC)).run();
}
}
JDK / Spring での実例
java.sql.DriverManager+Connection/Statement/ResultSet— ドライバごとに 関連する複数型 を返す。実質的な Abstract Factoryjavax.xml.parsers.DocumentBuilderFactory— パーサ実装ファミリ (Xerces, Crimson…) を一式切り替え- Spring の
@Profile+@Configuration— 本番/開発/テスト別に Bean ファミリをまるごと差し替え- AWS SDK v2 のDynamoDbClientBuilder,S3ClientBuilderなどリージョン別の実装群 Spring を使っている場合、@Profile("prod")を付けた@Configurationを用意すれば、UserRepository,OrderRepository,PaymentGatewayなどを一括で 「本番ファミリ」 に切り替えられます。 これが実質的な Abstract Factory 相当の役割を果たします。
落とし穴
1. 新しいプロダクト型を追加するコストが高い
抽象ファクトリに createXxx() を追加すると、既存の全具象ファクトリが修正対象 になります。
Open/Closed Principle が崩れるので、プロダクト型は最初によく設計する必要あり。
2. バリエーション間でメソッドシグネチャが揃わない
OS A のダイアログはモーダル専用、OS B のダイアログは非モーダル対応…など、 現実には抽象インタフェースに揃えづらい差分が出てきます。 無理に抽象化するより、Strategy や Visitor に逃がす ほうがきれいなことも。
3. DI コンテナを使っているなら、まず @Profile を検討
モダンな Java/Spring では、Abstract Factory を手書きする代わりに プロファイルや条件付き Bean で切り替える のが読みやすい場合が多い。 手書き Abstract Factory が本当に必要か、一度立ち止まる価値があります。
まとめ: Abstract Factory は 「ファミリーの一貫性」 を型で守るための強いパターン。
ただし適用先は限定的で、Spring の @Profile などに寄せたほうが現代的なことも多い。
手書きで入れるのは、フレームワーク非依存のライブラリ や、
実行時に動的に切り替えたいケース を中心に。
関連記事
- アルゴリズム·
Adapter パターン — 外部ライブラリをドメインに染めない防波堤
既存の API を自分たちのドメインに合わせて包むアダプタ。クラスアダプタ vs オブジェクトアダプタ、SLF4J や Spring の HandlerAdapter などの実例まで。
- アルゴリズム·
Prototype パターン — `clone()` と `copy constructor`、どっちを選ぶべきか
既存インスタンスを複製して「型 + 設定済み」を使い回す Prototype。Java の Cloneable の闇と、現代的な代替 (copy constructor / toBuilder / record withers)。
- アルゴリズム·
TemplateMethod パターン — Spring JdbcTemplate に見る典型実装
処理の骨格は親、差分は子に書き分ける Template Method。Spring の `*Template` 系の設計思想と、Strategy / Decorator との使い分け。