1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【C++】Factory Methodパターンで生成を集約!

Last updated at Posted at 2026-01-11

開発をしていると、種類の増えるオブジェクトを扱う場面が必ず出てきます。
小規模であればnewで直接生成しても問題ないですが、規模が大きくなると密結合になり、変更に弱い実装となってしまいます。
そこで役に立つのがFactory Methodパターンです。
Factory Methodパターンを使用することで変更に強い生成の実装を可能にします。
本記事では、ゲーム開発を模したコードを使って Factory Methodパターンについてなるべくわかりやすく解説します。

前提知識

  • C++の基礎文法を理解している

Factory Methodパターンとは

Factory Methodパターンは、どの具体クラスを生成するかという判断をサブクラスに委譲するためのデザインパターンです。

  • 新しい種類のオブジェクトを追加しても既存コードを触らずに済む
  • テスト用のダミーオブジェクトを差し替えることができる
  • 生成処理を整理して保守性を高められる
    といったメリットがあります。
    その反面で、
  • クラス数が増えやすい
  • 継承前提の設計になる
  • 生成処理が単純な場合はオーバーエンジニアリング気味になる
    といったデメリットも存在します。

Factory Methodパターンの用語と構成

Factory Methodパターンは以下の要素で構成されます。

  • Product
    • 生成されるオブジェクト抽象インターフェース
    • 概念だけを定義し、具体的な攻撃方法はサブクラスに任せる
  • ConcreteProduct
    • Productを継承し、実際の振る舞いを持つクラス
    • ConcreteCreatorから生成される
  • Creator
    • 生成処理を宣言するクラス
    • どの具体クラスを生成するかは知らない
    • 共通処理を持ち、生成だけをサブクラスに委譲する
  • ConcreteCreator
    • Creatorを継承し、実際にどのConcreteProductを生成するかを決めるクラス
    • 生成する種類ごとにこのクラスを用意する

構造の図解

サンプルコード

ゲーム開発の敵を模したFactory Methodパターンのサンプルコード

Enemy.h
//----------------------------------------------------------
//! @file   Enemy.h
//! @brief  敵キャラクターの基底クラス定義
//! @author つきの
//----------------------------------------------------------
#pragma once
#include <iostream>
#include <memory>
//----------------------------------------------------------
//! @class  Enemy
//! @brief  敵キャラクターの基底クラス
//! @note   Factory MethodパターンのProductを担う抽象クラス
//----------------------------------------------------------
class Enemy {
public:
	//----------------------------------------------------------
	//! @brief 敵キャラクターの攻撃処理(純粋仮想関数)
	//----------------------------------------------------------
	virtual void attack() = 0;

	//----------------------------------------------------------
	//! @brief デストラクタ
	//----------------------------------------------------------
	virtual ~Enemy() = default;
};
Slime.h
//----------------------------------------------------------
//! @file	Slime.h
//! @brief	スライムの定義
//! @author つきの
//----------------------------------------------------------
#pragma once
#include "Enemy.h"
//----------------------------------------------------------
//! @class	Slime
//! @brief	スライムのクラス
//! @note	Factory MethodパターンのConcreteProductを担う具体クラス
//----------------------------------------------------------
class Slime : public Enemy {
public:
	//----------------------------------------------------------
	// スライムの攻撃処理
	//! @note  Enemyクラスの純粋仮想関数をオーバーライド
	//----------------------------------------------------------
	void attack() override;
};
Slime.cpp
//----------------------------------------------------------
//! @file	Slime.cpp
//! @brief	スライムの実装
//! @author つきの
//----------------------------------------------------------
#include <iostream>
#include "Slime.h"
//----------------------------------------------------------
//! @brief スライムの攻撃処理
//----------------------------------------------------------
void Slime::attack() {
	std::cout << "ぷるぷるしている!\n";
}
Goblin.h
//----------------------------------------------------------
//! @file   Goblin.h
//! @brief  ゴブリンの定義
//! @author つきの
//----------------------------------------------------------
#pragma once
#include "Enemy.h"
//----------------------------------------------------------
//! @class  Goblin
//! @brief  ゴブリンのクラス
//! @note   Factory MethodパターンのConcreteProductを担う具体クラス
//----------------------------------------------------------
class Goblin : public Enemy {
public:
	//----------------------------------------------------------
	// ゴブリンの攻撃処理
	//! @note  Enemyクラスの純粋仮想関数をオーバーライド
	//----------------------------------------------------------
	void attack() override;
};
Goblin.cpp
//----------------------------------------------------------
//! @file   Goblin.cpp
//! @brief  ゴブリンの実装
//! @author つきの
//----------------------------------------------------------
#include <iostream>
#include "Goblin.h"
//----------------------------------------------------------
//! @brief ゴブリンの攻撃処理
//----------------------------------------------------------
void Goblin::attack() {
	std::cout << "こん棒で攻撃!\n";
}
EnemyFactory.h
//----------------------------------------------------------
//! @file   EnemyFactory.h
//! @brief  敵キャラクター生成の基底クラス定義
//! @author つきの
//----------------------------------------------------------
#pragma once
#include <memory>
#include "Enemy.h"
//----------------------------------------------------------
//! @class  EnemyFactory
//! @brief  敵キャラクター生成の基底クラス
//! @note   Factory MethodパターンのCreatorを担う抽象クラス
//----------------------------------------------------------
class EnemyFactory {
public:
	//----------------------------------------------------------
	//! @brief 敵キャラクター生成の純粋仮想関数
	//! @return 生成された敵キャラクターのポインタ
	//----------------------------------------------------------
	virtual std::unique_ptr<Enemy> createEnemy() const = 0;

	//----------------------------------------------------------
	//! @brief デストラクタ
	//----------------------------------------------------------
	virtual ~EnemyFactory() = default;
};
SlimeFactory.h
//----------------------------------------------------------
//! @file   SlimeFactory.h
//! @brief  スライム生成クラス定義
//! @author つきの
//----------------------------------------------------------
#pragma once
#include "EnemyFactory.h"
//----------------------------------------------------------
//! @class  SlimeFactory
//! @brief  スライム生成クラス
//! @note   Factory MethodパターンのConcreteCreatorを担う具体クラス
//----------------------------------------------------------
class SlimeFactory : public EnemyFactory {
public:
    //----------------------------------------------------------
	// スライム生成の実装
	//! @return 生成されたスライムのポインタ
	//! @note   EnemyFactoryクラスの純粋仮想関数をオーバーライド
    //----------------------------------------------------------
	std::unique_ptr<Enemy> createEnemy() const override;
};
SlimeFactory.cpp
//----------------------------------------------------------
//! @file   SlimeFactory.cpp
//! @brief  スライム生成クラス実装
//! @author つきの
//----------------------------------------------------------
#include "SlimeFactory.h"
#include "Slime.h"
//----------------------------------------------------------
//! @brief  スライム生成の実装
//----------------------------------------------------------
std::unique_ptr<Enemy> SlimeFactory::createEnemy() const{
    return std::make_unique<Slime>();
}
GoblinFactory.h
//----------------------------------------------------------
//! @file   GoblinFactory.h
//! @brief  ゴブリン生成クラス定義
//! @author つきの
//----------------------------------------------------------
#pragma once
#include "EnemyFactory.h"
//----------------------------------------------------------
//! @class  GoblinFactory
//! @brief  ゴブリン生成クラス
//! @note   Factory MethodパターンのConcreteCreatorを担う具体クラス
//----------------------------------------------------------
class GoblinFactory : public EnemyFactory {
public:
    //----------------------------------------------------------
	//! @brief  ゴブリン生成の実装
	//! @return 生成されたゴブリンのポインタ
    //----------------------------------------------------------
    std::unique_ptr<Enemy> createEnemy() const override;
};
GoblinFactory.cpp
//----------------------------------------------------------
//! @file   GoblinFactory.cpp
//! @brief  ゴブリン生成クラス実装
//! @author つきの
//----------------------------------------------------------
#include "GoblinFactory.h"
#include "Goblin.h"
//----------------------------------------------------------
//! @brief ゴブリン生成の実装
//----------------------------------------------------------
std::unique_ptr<Enemy> GoblinFactory::createEnemy() const {
	return std::make_unique<Goblin>();
}
main.cpp
//----------------------------------------------------------
//! @file   main.cpp
//! @brief  Factory Methodパターンのサンプルコード
//! @author つきの
//----------------------------------------------------------
#include <iostream>
#include <memory>
#include "EnemyFactory.h"
#include "GoblinFactory.h"
#include "SlimeFactory.h"
//エントリポイント
int main() {
	//----------------------------------------------------------
	//! @brief 敵キャラクター生成用のファクトリーポインタ
	//----------------------------------------------------------
	std::unique_ptr<EnemyFactory> factory;
	//----------------------------------------------------------
	//スライムを出す
	//----------------------------------------------------------
	factory = std::make_unique<SlimeFactory>();
	auto slime = factory->createEnemy();
	slime->attack();

	//----------------------------------------------------------
	//ゴブリンを出す
	//----------------------------------------------------------
	factory = std::make_unique<GoblinFactory>();
	auto goblin = factory->createEnemy();
	goblin->attack();

	//プログラムの終了
	return 0;
}
result
ぷるぷるしている!
こん棒で攻撃!

クラス図

Product

Createrで生成されるクラスのインターフェースです。
本記事ではスライムやゴブリンの基底クラスとして、抽象の敵クラスを定義しています。

Enemy.h
//----------------------------------------------------------
//! @file   Enemy.h
//! @brief  敵キャラクターの基底クラス定義
//! @author つきの
//----------------------------------------------------------
#pragma once
#include <iostream>
#include <memory>
//----------------------------------------------------------
//! @class  Enemy
//! @brief  敵キャラクターの基底クラス
//! @note   Factory MethodパターンのProductを担う抽象クラス
//----------------------------------------------------------
class Enemy {
public:
	//----------------------------------------------------------
	//! @brief 敵キャラクターの攻撃処理(純粋仮想関数)
	//----------------------------------------------------------
	virtual void attack() = 0;

	//----------------------------------------------------------
	//! @brief デストラクタ
	//----------------------------------------------------------
	virtual ~Enemy() = default;
};

ConcreteProduct

実際に生成されるクラス(本記事ではSlimeGoblin)です。
Product(本記事ではEnemy)を継承することで、Creatorの関数からProductのポインタとして生成されます。

Slime.h
//----------------------------------------------------------
//! @file	Slime.h
//! @brief	スライムの定義
//! @author つきの
//----------------------------------------------------------
#pragma once
#include "Enemy.h"
//----------------------------------------------------------
//! @class	Slime
//! @brief	スライムのクラス
//! @note	Factory MethodパターンのConcreteProductを担う具体クラス
//----------------------------------------------------------
class Slime : public Enemy {
public:
	//----------------------------------------------------------
	// スライムの攻撃処理
	//! @note  Enemyクラスの純粋仮想関数をオーバーライド
	//----------------------------------------------------------
	void attack() override;
};
Slime.cpp
//----------------------------------------------------------
//! @file	Slime.cpp
//! @brief	スライムの実装
//! @author つきの
//----------------------------------------------------------
#include <iostream>
#include "Slime.h"
//----------------------------------------------------------
//! @brief スライムの攻撃処理
//----------------------------------------------------------
void Slime::attack() {
	std::cout << "ぷるぷるしている!\n";
}
Goblin.h
//----------------------------------------------------------
//! @file   Goblin.h
//! @brief  ゴブリンの定義
//! @author つきの
//----------------------------------------------------------
#pragma once
#include "Enemy.h"
//----------------------------------------------------------
//! @class  Goblin
//! @brief  ゴブリンのクラス
//! @note   Factory MethodパターンのConcreteProductを担う具体クラス
//----------------------------------------------------------
class Goblin : public Enemy {
public:
	//----------------------------------------------------------
	// ゴブリンの攻撃処理
	//! @note  Enemyクラスの純粋仮想関数をオーバーライド
	//----------------------------------------------------------
	void attack() override;
};
Goblin.cpp
//----------------------------------------------------------
//! @file   Goblin.cpp
//! @brief  ゴブリンの実装
//! @author つきの
//----------------------------------------------------------
#include <iostream>
#include "Goblin.h"
//----------------------------------------------------------
//! @brief ゴブリンの攻撃処理
//----------------------------------------------------------
void Goblin::attack() {
	std::cout << "こん棒で攻撃!\n";
}

Creator

Productを生成するクラスのインターフェースです。
どのConcreteProduct(本記事ではSlimeGoblin)を生成するかは知らず、 実際の生成処理は派生クラス/ConcreteCreatorに任せます。
Creatorは共通のインターフェースだけを提供し、
利用側はCreatorを通してProduct(本記事ではEnemy)を扱うことで、 具体的なクラスに依存しない柔軟な設計を実現できます。

EnemyFactory.h
//----------------------------------------------------------
//! @file   EnemyFactory.h
//! @brief  敵キャラクター生成の基底クラス定義
//! @author つきの
//----------------------------------------------------------
#pragma once
#include <memory>
#include "Enemy.h"
//----------------------------------------------------------
//! @class  EnemyFactory
//! @brief  敵キャラクター生成の基底クラス
//! @note   Factory MethodパターンのCreatorを担う抽象クラス
//----------------------------------------------------------
class EnemyFactory {
public:
	//----------------------------------------------------------
	//! @brief 敵キャラクター生成の純粋仮想関数
	//! @return 生成された敵キャラクターのポインタ
	//----------------------------------------------------------
	virtual std::unique_ptr<Enemy> createEnemy() const = 0;

	//----------------------------------------------------------
	//! @brief デストラクタ
	//----------------------------------------------------------
	virtual ~EnemyFactory() = default;
};

ConcreteCreator

Creatorを継承し、実際にどの ConcreteProductを生成するかを決めるクラスです。
createEnemy() のようなFactory Methodを実装し、SlimeGoblinといった具体的なオブジェクトを生成します。

SlimeFactory.h
//----------------------------------------------------------
//! @file   SlimeFactory.h
//! @brief  スライム生成クラス定義
//! @author つきの
//----------------------------------------------------------
#pragma once
#include "EnemyFactory.h"
//----------------------------------------------------------
//! @class  SlimeFactory
//! @brief  スライム生成クラス
//! @note   Factory MethodパターンのConcreteCreatorを担う具体クラス
//----------------------------------------------------------
class SlimeFactory : public EnemyFactory {
public:
    //----------------------------------------------------------
	// スライム生成の実装
	//! @return 生成されたスライムのポインタ
	//! @note   EnemyFactoryクラスの純粋仮想関数をオーバーライド
    //----------------------------------------------------------
	std::unique_ptr<Enemy> createEnemy() const override;
};
SlimeFactory.cpp
//----------------------------------------------------------
//! @file   SlimeFactory.cpp
//! @brief  スライム生成クラス実装
//! @author つきの
//----------------------------------------------------------
#include "SlimeFactory.h"
#include "Slime.h"
//----------------------------------------------------------
//! @brief  スライム生成の実装
//----------------------------------------------------------
std::unique_ptr<Enemy> SlimeFactory::createEnemy() const{
    return std::make_unique<Slime>();
}
GoblinFactory.h
//----------------------------------------------------------
//! @file   GoblinFactory.h
//! @brief  ゴブリン生成クラス定義
//! @author つきの
//----------------------------------------------------------
#pragma once
#include "EnemyFactory.h"
//----------------------------------------------------------
//! @class  GoblinFactory
//! @brief  ゴブリン生成クラス
//! @note   Factory MethodパターンのConcreteCreatorを担う具体クラス
//----------------------------------------------------------
class GoblinFactory : public EnemyFactory {
public:
    //----------------------------------------------------------
	//! @brief  ゴブリン生成の実装
	//! @return 生成されたゴブリンのポインタ
    //----------------------------------------------------------
    std::unique_ptr<Enemy> createEnemy() const override;
};
GoblinFactory.cpp
//----------------------------------------------------------
//! @file   GoblinFactory.cpp
//! @brief  ゴブリン生成クラス実装
//! @author つきの
//----------------------------------------------------------
#include "GoblinFactory.h"
#include "Goblin.h"
//----------------------------------------------------------
//! @brief ゴブリン生成の実装
//----------------------------------------------------------
std::unique_ptr<Enemy> GoblinFactory::createEnemy() const {
	return std::make_unique<Goblin>();
}

利用側はConcreteCreatorを差し替えるだけで動作が変わるため、モックテストが容易であり、変更にも強いです。

main.cpp
//----------------------------------------------------------
//! @file   main.cpp
//! @brief  Factory Methodパターンのサンプルコード
//! @author つきの
//----------------------------------------------------------
#include <iostream>
#include <memory>
#include "EnemyFactory.h"
#include "GoblinFactory.h"
#include "SlimeFactory.h"
//エントリポイント
int main() {
	//----------------------------------------------------------
	//! @brief 敵キャラクター生成用のファクトリーポインタ
	//----------------------------------------------------------
	std::unique_ptr<EnemyFactory> factory;
	//----------------------------------------------------------
	//スライムを出す
	//----------------------------------------------------------
	factory = std::make_unique<SlimeFactory>();
	auto slime = factory->createEnemy();
	slime->attack();

	//----------------------------------------------------------
	//ゴブリンを出す
	//----------------------------------------------------------
	factory = std::make_unique<GoblinFactory>();
	auto goblin = factory->createEnemy();
	goblin->attack();

	//プログラムの終了
	return 0;
}

総括

  • Factory Methodパターンを使用することで、テストが容易になり、変更に強い設計となる
  • ConcreteProductの分だけConcreteCreatorが必要なので、クラスの数が多くなってしまうというデメリットもある
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?