現場の開発では、GoFデザインパターンを全て完璧に覚える必要はありませんが、よく使うパターンを押さえておくことで、設計やリファクタリング、チーム開発のコミュニケーションが一気に楽になります。
本記事では「現場で本当に頻出する」パターンに絞って、具体的なイメージと簡単なコード例を交えながら解説します。
よく使うGoFデザインパターンの全体像

GoFの23パターンのうち、一般的な業務システムやWebアプリケーションでよく名前が挙がるのは次のあたりです。
- 生成に関するパターン
- Singleton
- Factory Method
- Abstract Factory
- 構造に関するパターン
- Adapter
- Facade
- Decorator
- 振る舞いに関するパターン
- Strategy
- Observer
- Template Method
- Command
この記事では、この中でも「頻度が高く、理解しておくと得をしやすいパターン」に的を絞って説明していきます。
Singletonパターン
Singletonとは何か

Singletonパターンは「クラスのインスタンスを1個だけに制限し、どこからでも同じインスタンスにアクセスできるようにする」パターンです。
設定情報の管理、ログ出力クラス、接続プールなど、アプリケーション全体で共有すべきオブジェクトに用いられます。
実務では、フレームワーク側がDIコンテナなどで実質的なSingletonを提供していることも多く、自分でベタにSingletonを書く頻度は減ってきていますが、概念としては依然よく登場します。
Singletonのシンプルな実装例(C++風)
#include <iostream>
#include <string>
// ロガークラスをSingletonとして定義
class Logger {
private:
// 唯一のインスタンスへのポインタ
static Logger* instance;
// 外からnewできないようにコンストラクタをprivateにする
Logger() {}
public:
// コピーコンストラクタと代入演算子を禁止
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
// インスタンス取得メソッド(グローバルアクセスポイント)
static Logger* getInstance() {
if (instance == nullptr) {
instance = new Logger();
}
return instance;
}
// ログ出力メソッド
void log(const std::string& message) {
std::cout << "[LOG] " << message << std::endl;
}
};
// 静的メンバ変数の定義
Logger* Logger::instance = nullptr;
int main() {
// どこから呼んでも同じインスタンスが返される
Logger* logger1 = Logger::getInstance();
Logger* logger2 = Logger::getInstance();
logger1->log("アプリケーション開始");
logger2->log("同じインスタンスからログを出力");
// ポインタが同じであることを確認
if (logger1 == logger2) {
std::cout << "logger1とlogger2は同じインスタンスです" << std::endl;
}
return 0;
}
[LOG] アプリケーション開始
[LOG] 同じインスタンスからログを出力
logger1とlogger2は同じインスタンスです
Singletonを使うときの注意点
「どこからでもアクセスできる=グローバル変数に近い」という面があるため、テストがしづらくなったり、依存関係が見えづらくなったりします。
実務では次のような方針がよく取られます。
- テスト可能性を重視する場合は、DIコンテナなどでスコープを管理し、明示的に依存を注入する
- どうしてもグローバルな1インスタンスが必要な場合のみSingletonを使う
Factory Method / Abstract Factory
工場パターンのイメージ

Factory MethodとAbstract Factoryはどちらも「生成処理をカプセル化する」パターンです。
現場では、次のように理解しておくと整理しやすくなります。
- Factory Method
- 1種類の製品に対して、「どの具体クラスをnewするか」をサブクラス側に任せる
- Abstract Factory
- 関連する複数の製品群(ボタン+テキストボックスなど)を、まるごと切り替えられるようにする
フレームワークやライブラリが内部実装で使っていることが多く、「自分で0から書く」というよりは「コードリーディングでよく出会う」パターンです。
Factory Methodの簡単な例(Java風)
// 製品インターフェース
interface Message {
String getContent();
}
// 具体的な製品A
class HelloMessage implements Message {
public String getContent() {
return "Hello!";
}
}
// 具体的な製品B
class ByeMessage implements Message {
public String getContent() {
return "Good bye!";
}
}
// Creator(生成側の抽象クラス)
abstract class MessageCreator {
// Factory Method: 具体的に何を作るかはサブクラスに任せる
protected abstract Message createMessage();
// 共通の処理フロー
public void print() {
Message msg = createMessage();
System.out.println(msg.getContent());
}
}
// 具体Creatorその1
class HelloMessageCreator extends MessageCreator {
protected Message createMessage() {
return new HelloMessage();
}
}
// 具体Creatorその2
class ByeMessageCreator extends MessageCreator {
protected Message createMessage() {
return new ByeMessage();
}
}
public class Main {
public static void main(String[] args) {
MessageCreator helloCreator = new HelloMessageCreator();
MessageCreator byeCreator = new ByeMessageCreator();
helloCreator.print(); // Hello!
byeCreator.print(); // Good bye!
}
}
Hello!
Good bye!
「生成処理だけを差し替え、それ以外の処理フローは共通化したい」ときに役立ちます。
Strategyパターン
Strategyとは何か

Strategyパターンは「アルゴリズム(処理のやり方)をオブジェクトとして切り出し、実行時に差し替えできるようにする」パターンです。
実務では次のような場面でよく登場します。
- 課金計算ロジックを契約プランごとに変えたい
- 検索条件の組み合わせを、複数パターンから選べるようにしたい
- ソートやフィルタリングの方針を、設定やユーザ操作で切り替えたい
Strategyのサンプル(C++)
#include <iostream>
#include <memory>
// 戦略インターフェース
class DiscountStrategy {
public:
virtual ~DiscountStrategy() = default;
// 金額を受け取り、割引後の金額を返す
virtual int apply(int price) const = 0;
};
// 具体戦略A: 定率割引(10%OFF)
class PercentageDiscount : public DiscountStrategy {
public:
int apply(int price) const override {
return static_cast<int>(price * 0.9); // 10%割引
}
};
// 具体戦略B: 定額割引(500円OFF)
class FixedDiscount : public DiscountStrategy {
public:
int apply(int price) const override {
int discounted = price - 500;
return discounted > 0 ? discounted : 0;
}
};
// Context: 戦略を保持し、必要なときに呼び出す側
class PriceCalculator {
private:
std::shared_ptr<DiscountStrategy> strategy;
public:
// 戦略を差し替え可能にする
void setStrategy(std::shared_ptr<DiscountStrategy> s) {
strategy = s;
}
int calculate(int basePrice) const {
if (!strategy) {
// 戦略が設定されていない場合は割引なし
return basePrice;
}
return strategy->apply(basePrice);
}
};
int main() {
PriceCalculator calculator;
// プランA: 定率割引
calculator.setStrategy(std::make_shared<PercentageDiscount>());
std::cout << "定率割引後: " << calculator.calculate(3000) << "円" << std::endl;
// プランB: 定額割引
calculator.setStrategy(std::make_shared<FixedDiscount>());
std::cout << "定額割引後: " << calculator.calculate(3000) << "円" << std::endl;
// 割引なし
calculator.setStrategy(nullptr);
std::cout << "割引なし: " << calculator.calculate(3000) << "円" << std::endl;
return 0;
}
定率割引後: 2700円
定額割引後: 2500円
割引なし: 3000円
if文やswitch文でアルゴリズムが増殖してしまったとき、Strategyで外出しするとコードが整理されやすくなります。
Template Methodパターン
Template Methodのイメージ

Template Methodパターンは「処理の流れ(テンプレート)を抽象クラスで固定し、細部の処理だけサブクラスに任せる」パターンです。
フレームワークのコールバックや、バッチ処理、データ読み込み処理などでよく見られます。
Template Methodのサンプル(Java)
// 抽象クラス: テンプレートメソッドを持つ
abstract class DataImporter {
// テンプレートメソッド: 処理の流れ(骨格)を定義
public final void execute() {
open();
readData();
close();
}
// 共通の前処理・後処理(必要ならオーバーライド可)
protected void open() {
System.out.println("接続を開きます");
}
protected void close() {
System.out.println("接続を閉じます");
}
// 具体的な読み込み処理はサブクラスに任せる
protected abstract void readData();
}
// CSVインポート用のサブクラス
class CsvImporter extends DataImporter {
@Override
protected void readData() {
System.out.println("CSVファイルからデータを読み込みます");
}
}
// APIインポート用のサブクラス
class ApiImporter extends DataImporter {
@Override
protected void readData() {
System.out.println("外部APIからデータを取得します");
}
}
public class TemplateMethodSample {
public static void main(String[] args) {
DataImporter csv = new CsvImporter();
DataImporter api = new ApiImporter();
csv.execute();
System.out.println("----");
api.execute();
}
}
接続を開きます
CSVファイルからデータを読み込みます
接続を閉じます
----
接続を開きます
外部APIからデータを取得します
接続を閉じます
「前後の共通処理は一箇所にまとめたいが、真ん中だけ用途ごとに変えたい」ときに、Template Methodはとても有効です。
Observerパターン
Observerとは何か

Observerパターンは「あるオブジェクトの状態変化を、複数のオブジェクトに自動的に通知する」パターンです。
GUIのイベントリスナや、リアクティブプログラミング、ドメインイベントなど、現場でかなり頻繁に見かける概念です。
Observerの簡単な例(C++)
#include <iostream>
#include <vector>
#include <string>
#include <memory>
// 前方宣言
class Observer;
// Subject(観察される側)
class Subject {
private:
std::vector<std::weak_ptr<Observer>> observers;
std::string state;
public:
void attach(std::shared_ptr<Observer> obs) {
observers.push_back(obs);
}
void setState(const std::string& s);
const std::string& getState() const {
return state;
}
void notify();
};
// Observer(観察する側)のインターフェース
class Observer {
public:
virtual ~Observer() = default;
virtual void update(Subject& subject) = 0;
};
// 具体的Observer A
class ConsoleObserver : public Observer {
public:
void update(Subject& subject) override {
std::cout << "ConsoleObserver: state = " << subject.getState() << std::endl;
}
};
// 具体的Observer B
class LengthObserver : public Observer {
public:
void update(Subject& subject) override {
std::cout << "LengthObserver: length = "
<< subject.getState().size() << std::endl;
}
};
// Subjectのメソッド実装
void Subject::setState(const std::string& s) {
state = s;
notify(); // 状態変更時に通知
}
void Subject::notify() {
for (auto& weakObs : observers) {
if (auto obs = weakObs.lock()) {
obs->update(*this);
}
}
}
int main() {
Subject subject;
auto consoleObs = std::make_shared<ConsoleObserver>();
auto lengthObs = std::make_shared<LengthObserver>();
subject.attach(consoleObs);
subject.attach(lengthObs);
subject.setState("Hello");
subject.setState("Observer Pattern");
return 0;
}
ConsoleObserver: state = Hello
LengthObserver: length = 5
ConsoleObserver: state = Observer Pattern
LengthObserver: length = 15
GUIのイベントリスナや、JavaのPropertyChangeListenerなど、多くのフレームワークでObserverパターンが使われています。
Decoratorパターン
Decoratorのイメージ

Decoratorパターンは「元のオブジェクトをそのままラップして、振る舞いを少しずつ追加する」パターンです。
入出力ストリームや、Webフレームワークのミドルウェア、ログやキャッシュを追加するときなどに使われます。
Decoratorのサンプル(Java)
// コンポーネントのインターフェース
interface Notifier {
void send(String message);
}
// 基本の実装(メール通知)
class EmailNotifier implements Notifier {
public void send(String message) {
System.out.println("メール送信: " + message);
}
}
// デコレータの抽象クラス
abstract class NotifierDecorator implements Notifier {
protected Notifier wrappee; // ラップする対象
public NotifierDecorator(Notifier notifier) {
this.wrappee = notifier;
}
public void send(String message) {
wrappee.send(message);
}
}
// 具体的デコレータ1: SMSも送る
class SmsNotifier extends NotifierDecorator {
public SmsNotifier(Notifier notifier) {
super(notifier);
}
public void send(String message) {
super.send(message); // もとの処理(メール)を実行
System.out.println("SMS送信: " + message);
}
}
// 具体的デコレータ2: Slackにも送る
class SlackNotifier extends NotifierDecorator {
public SlackNotifier(Notifier notifier) {
super(notifier);
}
public void send(String message) {
super.send(message); // もとの処理を実行
System.out.println("Slack送信: " + message);
}
}
public class DecoratorSample {
public static void main(String[] args) {
// 基本はメールだけ
Notifier notifier = new EmailNotifier();
// メール + SMS
notifier = new SmsNotifier(notifier);
// メール + SMS + Slack
notifier = new SlackNotifier(notifier);
notifier.send("システム障害が発生しました");
}
}
メール送信: システム障害が発生しました
SMS送信: システム障害が発生しました
Slack送信: システム障害が発生しました
「継承でクラスを増やしすぎたくないが、機能を組み合わせて追加したい」ときにDecoratorが有効です。
Facadeパターン
Facadeとは何か

Facadeパターンは「複雑なサブシステム一式を、1つのシンプルな窓口クラスで隠す」パターンです。
現場では次のような場面で特に使われます。
- 外部APIやレガシーライブラリへの複雑な呼び出し手順を、単純なメソッドで包みたい
- 画面側からは、ドメインロジックを1つのサービスメソッドとして呼びたい
Facadeの簡単な例(Java)
// 複雑なサブシステムA: 認証
class AuthService {
public boolean authenticate(String user, String password) {
System.out.println("ユーザ認証中...");
return "user".equals(user) && "pass".equals(password);
}
}
// 複雑なサブシステムB: 在庫チェック
class InventoryService {
public boolean checkStock(String itemId) {
System.out.println("在庫チェック中...");
return true;
}
}
// 複雑なサブシステムC: 決済
class PaymentService {
public boolean pay(String user, int amount) {
System.out.println("決済処理中...");
return true;
}
}
// Facade: 上記の複雑な処理をまとめる窓口
class OrderFacade {
private AuthService auth = new AuthService();
private InventoryService inventory = new InventoryService();
private PaymentService payment = new PaymentService();
public boolean placeOrder(String user, String password, String itemId, int amount) {
if (!auth.authenticate(user, password)) {
System.out.println("認証失敗");
return false;
}
if (!inventory.checkStock(itemId)) {
System.out.println("在庫不足");
return false;
}
if (!payment.pay(user, amount)) {
System.out.println("決済失敗");
return false;
}
System.out.println("注文完了");
return true;
}
}
public class FacadeSample {
public static void main(String[] args) {
OrderFacade facade = new OrderFacade();
facade.placeOrder("user", "pass", "ITEM-001", 1000);
}
}
ユーザ認証中...
在庫チェック中...
決済処理中...
注文完了
Facadeを使うと、画面や外部公開APIからは「1メソッド呼べば完結する」ように見せられるため、クライアント側コードが非常にシンプルになります。
Commandパターン
Commandとは何か

Commandパターンは「操作自体をオブジェクトとして表現し、キューイング・ログ・アンドゥなどをしやすくする」パターンです。
GUIのボタン操作、ジョブキュー、Undo/Redoなどでよく利用されます。
Commandのシンプルなサンプル(Java)
// Commandインターフェース
interface Command {
void execute();
}
// Receiver: 実際の処理を行うクラス
class TextEditor {
private StringBuilder text = new StringBuilder();
public void append(String str) {
text.append(str);
}
public void print() {
System.out.println(text.toString());
}
}
// 具体的Command: 文字列を追加する
class AppendCommand implements Command {
private TextEditor editor;
private String textToAppend;
public AppendCommand(TextEditor editor, String textToAppend) {
this.editor = editor;
this.textToAppend = textToAppend;
}
public void execute() {
editor.append(textToAppend);
}
}
// Invoker: Commandを発行する側
class CommandInvoker {
public void submit(Command command) {
// 実際にはここでキューに積んだりログを残したりできる
command.execute();
}
}
public class CommandSample {
public static void main(String[] args) {
TextEditor editor = new TextEditor();
CommandInvoker invoker = new CommandInvoker();
invoker.submit(new AppendCommand(editor, "Hello "));
invoker.submit(new AppendCommand(editor, "Command "));
invoker.submit(new AppendCommand(editor, "Pattern"));
editor.print();
}
}
Hello Command Pattern
「処理内容を1つのオブジェクトに閉じ込めておきたい」とき、Commandパターンは有力な選択肢になります。
現場での優先度とざっくり使い分け

ざっくりとした「優先度」と「よくある用途」を表にまとめます。
| パターン | 現場での頻度感 | 主な用途・ポイント |
|---|---|---|
| Singleton | 中 | 設定、ログ、接続などの1インスタンス共有。ただし乱用注意。 |
| Factory Method | 中 | 生成処理を差し替えたいとき。フレームワーク内部でよく利用。 |
| Abstract Factory | 中〜低 | 関連する製品群の生成をまとめて切り替えたい。 |
| Strategy | 高 | アルゴリズムを差し替えたい。if/switchの分岐整理。 |
| Template Method | 高 | 処理フローは共通、中身だけ変えたい。フレームワークに多い。 |
| Observer | 高 | 状態変化の通知。イベントリスナやリアクティブ処理。 |
| Decorator | 中〜高 | 機能を「後付け」したい。ログやキャッシュ、通知追加など。 |
| Facade | 高 | 複雑な処理群を1つの窓口メソッドで隠したい。 |
| Command | 中 | 操作をオブジェクトとして扱い、キューやUndoを実現したい。 |
最初に押さえると実務で得をしやすいのは「Strategy」「Template Method」「Observer」「Facade」「Decorator」あたりです。
Factory系はフレームワークやライブラリを読むときに理解できていれば十分、Singletonは原則控えめに、というバランス感覚を持っておくと良いです。
まとめ
GoFデザインパターンは全部で23個ありますが、現場で頻出するのはそのうちの一部です。
本記事で紹介したSingleton / Factory系 / Strategy / Template Method / Observer / Decorator / Facade / Commandを押さえておくだけでも、既存コードの理解力やリファクタリングの選択肢が大きく広がります。
重要なのは名称を暗記することではなく、「どんな問題に対して、どのパターンが効きそうか」をイメージできるようになることです。
日々の開発で「あ、この複雑なifをStrategyにできそう」「このごちゃごちゃした処理はFacadeで隠せそう」と少しずつ意識しながら使っていくことで、自然と身についていきます。
