FactoryMethod パターン — Spring BeanFactory の背骨を自作する
インスタンス生成の種類を抽象側に閉じ込める Factory Method パターン。JDK/Spring の実例、Simple Factory との違い、現代 Java での書き方まで。

どういう問題を解くパターンか
ドメイン上で 「同じ目的のオブジェクトだが、生成方法が将来増える」 というときに、
new を呼び出し元から追い出し、生成責務を抽象クラスの createXxx() に委ねる設計です。
GoF 定義では 「インスタンス生成の判断を subclass に委譲する」 と表現されますが、 実務的な表現にすると以下のとおり:
- 呼び出し側は
Factoryという抽象型に対してcreate()を呼ぶだけでよい - 具体的な
new Account(...)/new Admin(...)の判断は concrete factory 側で行う - 新しい種類が増えても呼び出し側のコードは変更不要 (Open/Closed Principle)
いつ使う / 使わない
| 使うべき | 使わないほうがよい |
|---|---|
| 生成する型が将来 2 種類以上に分岐しそう | 1 種類しかない (new で十分) |
| 生成に前後処理 (登録・初期化) が必要 | Spring 等の DI が既に効いている |
| テストで差し替えたい (fake を注入したい) | record + static factory method で足りる |
Simple Factory との違い: switch 1 本で分岐する 「Simple Factory」 に対し、
Factory Method は 分岐そのものを型で表現 します。
switch を増やす代わりにクラスを増やすので、
分岐が頻繁に増える領域で効果が出ます。
実装
抽象プロダクト
public abstract class Product {
public abstract void use();
}
抽象ファクトリ
生成の 骨格 (template) と、サブクラスに委ねる プリミティブ を分けるのが肝です。
create() は final にして、派生クラスが骨格を壊せないようにしておきます。
public abstract class Factory {
/** 骨格: 生成→登録→返却の順序を固定する */
public final Product create(String name) {
Product product = createProduct(name);
register(product);
return product;
}
/** サブクラスが埋める差分 */
protected abstract Product createProduct(String name);
protected abstract void register(Product product);
}
具象プロダクトと具象ファクトリ
public class Account extends Product {
private final String name;
public Account(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public void use() {
System.out.println("Account: " + name);
}
}
public class AccountFactory extends Factory {
private final List<String> names = new ArrayList<>();
@Override
protected Product createProduct(String name) {
return new Account(name);
}
@Override
protected void register(Product product) {
names.add(((Account) product).getName());
}
public List<String> getNames() {
return names;
}
}
クライアント
呼び出し側は Factory 型に依存するだけ。
AccountFactory でも AdminFactory でも、どちらでも差し替え可能です。
public class Main {
public static void main(String[] args) {
Factory factory = new AccountFactory();
factory.create("account01");
factory.create("account02");
factory.create("account03");
}
}
JDK / Spring での実例
Factory Method は 「作って返す」 API のあちこちに潜んでいます。
java.util.Calendar#getInstance()— ロケール/タイムゾーンに応じたCalendar実装を返すjava.nio.file.Paths#get(...)— プラットフォーム依存のPathを返すjava.util.Collections.unmodifiableList(...)— 内部実装クラスのUnmodifiableListを返す- Spring
BeanFactory#getBean(...)— Bean 定義に応じて具体実装を返す- SpringLoggerFactory.getLogger(...)(SLF4J) — ランタイムの実装 (Logback/Log4j) を返す
落とし穴
1. 「Simple Factory」 との混同
switch で分岐するだけなら Factory Method ではなく Simple Factory。
Simple Factory で足りている箇所に無理に抽象クラスを足すとクラスが爆発します。
2. 骨格メソッドを final にしないと壊される
上記例の create(String) を final にしていないと、
サブクラスで 「ついでに」 オーバーライドされて 「生成→登録」 の順序が崩れがちです。
Template Method も同様の設計で final を推奨します。
3. パラメータが増えると引数の意味がぼやける
createProduct("name", "A", true, 10) のような呼び出しが出てきたら、
Builder (本連載の Builder パターン) との組み合わせを検討。
現代 Java での最小実装
分岐の軸が単純で、生成の前後処理も不要なら、
インタフェースの static ファクトリメソッド + sealed interface で十分です。
public sealed interface Product permits Account, Admin {
static Product of(String type, String name) {
return switch (type) {
case "account" -> new Account(name);
case "admin" -> new Admin(name);
default -> throw new IllegalArgumentException(type);
};
}
}
判断軸: 「生成の前後処理が複数ある」「生成器自体に状態を持たせたい」なら 古典的な Factory Method を、 「ただ型を切り替えたいだけ」なら上の最小実装で十分です。
抽象化は値段なので、支払う価値がある変更軸にだけ 使ってください。
関連記事
- アルゴリズム·
Builder パターン — 引数 4 つを超えたら迷わず導入する
コンストラクタ引数が多いクラスの可読性を救う Builder。手書き・Lombok・record の併用パターンと、Fluent Builder でよくあるハマり所。
- アルゴリズム·
Adapter パターン — 外部ライブラリをドメインに染めない防波堤
既存の API を自分たちのドメインに合わせて包むアダプタ。クラスアダプタ vs オブジェクトアダプタ、SLF4J や Spring の HandlerAdapter などの実例まで。
- アルゴリズム·
Singleton パターン — 2020 年代の Java では `enum` 一択の理由
伝統的な Singleton の 4 実装と、各々のスレッド安全性・シリアライズ耐性・リフレクション耐性。結論として現代は enum Singleton で終わり。