この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:6
ページ更新者:atom
更新日時:2026-06-11 07:10:02

タイトル: デザインパターン
SEOタイトル: Java デザインパターン完全ガイド — GoF 23 パターン

この記事の要点
  • GoF (Gang of Four) 23 パターン生成 / 構造 / 振舞いの 3 系統に分類
  • 生成系: Singleton / Factory Method / Abstract Factory / Builder / Prototype
  • 構造系: Adapter / Decorator / Facade / Composite / Proxy / Bridge / Flyweight
  • 振舞い系: Observer / Strategy / Command / Iterator / Template Method / State / Chain of Responsibility / Visitor / Mediator / Memento / Interpreter
  • Spring は DI / Singleton / Proxy / Template Method (JdbcTemplate) 等を内部で活用。現代では関数型 interface + ラムダが Strategy / Command の代替に

23 パターン分類

系統パターン名用途
生成 CreationalSingletonインスタンス唯一性
Factory Methodサブクラスに生成委譲
Abstract Factory関連オブジェクト群を一括生成
Builder複雑なオブジェクトを段階的に組立
Prototype既存インスタンスをクローン
構造 StructuralAdapter非互換 IF を変換
Bridge抽象と実装を分離
Composite木構造を一様に扱う
Decorator機能を動的に追加
Facadeサブシステムへの統一窓口
Flyweight多数オブジェクトの共有
Proxy代理オブジェクトでアクセス制御
振舞 BehavioralChain of Responsibility要求を順に処理
Command処理をオブジェクト化
Interpreter言語の文法を表現
Iterator集約への走査手段
Mediatorオブジェクト間調停
Memento状態のスナップショット
Observer状態変化を通知
State状態ごとの振舞い切替
Strategyアルゴリズム差替
Template Method骨格を親クラスで定義
Visitor構造への操作追加

Singleton

// enum で実装するのが最も安全(Joshua Bloch 推奨)
public enum Config {
    INSTANCE;
    private String name = "default";
    public String getName() { return name; }
}

Config.INSTANCE.getName();

// 古典: private constructor + static field
public final class Logger {
    private static final Logger INSTANCE = new Logger();
    private Logger() {}
    public static Logger getInstance() { return INSTANCE; }
}

Factory Method

interface Shape { void draw(); }
class Circle implements Shape { public void draw() {} }
class Square implements Shape { public void draw() {} }

class ShapeFactory {
    public static Shape create(String type) {
        return switch (type) {
            case "circle" -> new Circle();
            case "square" -> new Square();
            default       -> throw new IllegalArgumentException(type);
        };
    }
}

Shape s = ShapeFactory.create("circle");

Builder

public class User {
    private final String name;
    private final int age;
    private final String email;

    private User(Builder b) {
        this.name = b.name; this.age = b.age; this.email = b.email;
    }
    public static Builder builder() { return new Builder(); }

    public static class Builder {
        private String name; private int age; private String email;
        public Builder name(String n)  { this.name  = n; return this; }
        public Builder age(int a)      { this.age   = a; return this; }
        public Builder email(String e) { this.email = e; return this; }
        public User build() { return new User(this); }
    }
}

User u = User.builder().name("Taro").age(30).email("t@x").build();

Adapter

// 旧 IF
interface OldPrinter { void printText(String s); }

// 新 IF(呼び出し側はこちらを使いたい)
interface NewPrinter { void print(Object o); }

// アダプタで橋渡し
class PrinterAdapter implements NewPrinter {
    private final OldPrinter old;
    public PrinterAdapter(OldPrinter o) { this.old = o; }
    @Override public void print(Object o) { old.printText(o.toString()); }
}

Decorator

interface Coffee {
    int cost();
    String description();
}

class Espresso implements Coffee {
    public int cost() { return 300; }
    public String description() { return "Espresso"; }
}

// 装飾の基底
abstract class CoffeeDecorator implements Coffee {
    protected final Coffee base;
    protected CoffeeDecorator(Coffee b) { this.base = b; }
}

class Milk extends CoffeeDecorator {
    public Milk(Coffee b) { super(b); }
    public int cost() { return base.cost() + 50; }
    public String description() { return base.description() + " + Milk"; }
}

Coffee c = new Milk(new Espresso());
c.cost();         // 350
c.description();  // "Espresso + Milk"

// 実は java.io はこのパターンだらけ
// new BufferedReader(new InputStreamReader(new FileInputStream(...)))

Observer

import java.util.*;

interface Listener { void onEvent(String msg); }

class Publisher {
    private final List<Listener> listeners = new ArrayList<>();
    public void subscribe(Listener l) { listeners.add(l); }
    public void publish(String msg) {
        for (Listener l : listeners) l.onEvent(msg);
    }
}

Publisher p = new Publisher();
p.subscribe(msg -> System.out.println("A: " + msg));
p.subscribe(msg -> System.out.println("B: " + msg));
p.publish("hello");

Strategy

// Java 8+ の関数型 interface で簡潔に
import java.util.*;
import java.util.function.*;

class Sorter {
    public static <T> void sort(List<T> list, Comparator<T> strategy) {
        list.sort(strategy);
    }
}

List<String> list = new ArrayList<>(List.of("banana", "apple", "cherry"));
Sorter.sort(list, Comparator.naturalOrder());            // 昇順
Sorter.sort(list, Comparator.reverseOrder());            // 降順
Sorter.sort(list, Comparator.comparingInt(String::length)); // 長さ順

Template Method

abstract class ReportBuilder {
    // テンプレート(手順を固定)
    public final String build() {
        return header() + body() + footer();
    }
    protected String header() { return "=== Report ===\n"; }
    protected abstract String body();   // サブクラスで実装
    protected String footer() { return "\n=== End ===\n"; }
}

class SalesReport extends ReportBuilder {
    @Override protected String body() { return "Sales: 1,234,567 JPY"; }
}

// Spring JdbcTemplate / RestTemplate もこのパターン

Proxy

interface Service { String fetch(int id); }

class RealService implements Service {
    public String fetch(int id) { /* 重い処理 */ return "data:" + id; }
}

class CachingProxy implements Service {
    private final Service real;
    private final Map<Integer, String> cache = new HashMap<>();

    public CachingProxy(Service r) { this.real = r; }

    public String fetch(int id) {
        return cache.computeIfAbsent(id, real::fetch);
    }
}

// Spring AOP / トランザクション境界もこのパターン

現代の Java での代替手段

古典パターン現代の代替
Strategy関数型 interface (Function / Predicate) + ラムダ
CommandRunnable / Consumer + ラムダ
SingletonSpring の @Component(DI コンテナで管理)
ObserverSpring の @EventListener / リアクティブストリーム
FactoryDI コンテナの bean 定義
Iterator拡張 for / Stream

FAQ

Q: 全 23 パターンを覚える必要ある?
A: 不要。実務で頻出は Singleton / Factory / Builder / Strategy / Observer / Decorator / Proxy / Template Method の 8 個程度。

Q: パターンを使うと過剰設計にならない?
A: なります。必要になったときに導入するのが鉄則。YAGNI (You Aren't Gonna Need It) 原則。

Q: Spring を使うとどのパターンが自動で適用される?
A: DI による Strategy / Factory、シングルトン Bean、AOP の Proxy、JdbcTemplate / RestTemplate の Template Method など。