Skilore

生成パターン(Creational Patterns)

オブジェクトの生成方法を柔軟にする設計パターン。Factory、Abstract Factory、Builder、Singleton、Prototype の5つの主要パターンの「なぜ必要か」「いつ使うか」を実践的に解説。

98 分で読めます48,622 文字

生成パターン(Creational Patterns)

オブジェクトの生成方法を柔軟にする設計パターン。Factory、Abstract Factory、Builder、Singleton、Prototype の5つの主要パターンの「なぜ必要か」「いつ使うか」を実践的に解説。

この章で学ぶこと

  • 各生成パターンの目的と使い分けを理解する
  • 各パターンの実装方法を複数言語で把握する
  • アンチパターンとしての Singleton の問題を学ぶ
  • 現代のフレームワークでの生成パターンの応用を知る
  • テスタビリティを考慮した生成パターンの設計ができるようになる

前提知識

このガイドを読む前に、以下の知識があると理解が深まります:

  • 基本的なプログラミングの知識
  • 関連する基礎概念の理解

1. Factory Method パターン

1.1 概要と目的

目的: オブジェクトの生成ロジックをカプセル化する

いつ使うか:
  → 生成するクラスを実行時に決定したい
  → 生成ロジックが複雑
  → new を直接使わせたくない
  → テスト時にモックオブジェクトに差し替えたい

構造:
CreatorProduct
(ファクトリ)────────→(生成物)
factoryMethod()operation()
│                           │
ConcreteCreatorConcreteProduct
────────→

1.2 Simple Factory(静的ファクトリメソッド)

// Simple Factory: 最も基本的なファクトリ
interface Notification {
  send(message: string): void;
  getChannel(): string;
}
 
class EmailNotification implements Notification {
  constructor(private to: string) {}
  send(message: string) {
    console.log(`Email to ${this.to}: ${message}`);
  }
  getChannel() { return "email"; }
}
 
class SmsNotification implements Notification {
  constructor(private phoneNumber: string) {}
  send(message: string) {
    console.log(`SMS to ${this.phoneNumber}: ${message}`);
  }
  getChannel() { return "sms"; }
}
 
class SlackNotification implements Notification {
  constructor(private channel: string) {}
  send(message: string) {
    console.log(`Slack #${this.channel}: ${message}`);
  }
  getChannel() { return "slack"; }
}
 
class PushNotification implements Notification {
  constructor(private deviceToken: string) {}
  send(message: string) {
    console.log(`Push to ${this.deviceToken}: ${message}`);
  }
  getChannel() { return "push"; }
}
 
// ファクトリ: 生成ロジックを集約
interface NotificationConfig {
  type: "email" | "sms" | "slack" | "push";
  target: string;
}
 
class NotificationFactory {
  static create(config: NotificationConfig): Notification {
    switch (config.type) {
      case "email":
        return new EmailNotification(config.target);
      case "sms":
        return new SmsNotification(config.target);
      case "slack":
        return new SlackNotification(config.target);
      case "push":
        return new PushNotification(config.target);
      default:
        throw new Error(`Unknown notification type: ${config.type}`);
    }
  }
 
  // 複数の通知を一括生成
  static createBatch(configs: NotificationConfig[]): Notification[] {
    return configs.map(config => this.create(config));
  }
}
 
// 利用側は具象クラスを知らなくてよい
const notification = NotificationFactory.create({
  type: "email",
  target: "tanaka@example.com",
});
notification.send("Hello!");
 
// 複数チャネルへの一括送信
const notifications = NotificationFactory.createBatch([
  { type: "email", target: "tanaka@example.com" },
  { type: "slack", target: "general" },
  { type: "push", target: "device-token-123" },
]);
notifications.forEach(n => n.send("重要なお知らせ"));

1.3 Factory Method(テンプレートメソッドとの組み合わせ)

// Factory Method: サブクラスが生成するオブジェクトの型を決定
abstract class DocumentExporter {
  // テンプレートメソッド
  async export(data: ReportData): Promise<Buffer> {
    const formatter = this.createFormatter();
    const header = formatter.formatHeader(data.title);
    const body = formatter.formatBody(data.content);
    const footer = formatter.formatFooter(data.metadata);
    return Buffer.from(header + body + footer);
  }
 
  // Factory Method: サブクラスが実装
  protected abstract createFormatter(): DocumentFormatter;
}
 
interface DocumentFormatter {
  formatHeader(title: string): string;
  formatBody(content: string[]): string;
  formatFooter(metadata: Record<string, string>): string;
}
 
class PdfExporter extends DocumentExporter {
  protected createFormatter(): DocumentFormatter {
    return new PdfFormatter();
  }
}
 
class HtmlExporter extends DocumentExporter {
  protected createFormatter(): DocumentFormatter {
    return new HtmlFormatter();
  }
}
 
class MarkdownExporter extends DocumentExporter {
  protected createFormatter(): DocumentFormatter {
    return new MarkdownFormatter();
  }
}
 
class CsvExporter extends DocumentExporter {
  protected createFormatter(): DocumentFormatter {
    return new CsvFormatter();
  }
}
 
// 各フォーマッタの実装
class PdfFormatter implements DocumentFormatter {
  formatHeader(title: string): string {
    return `%PDF-1.4\n/Title (${title})\n`;
  }
  formatBody(content: string[]): string {
    return content.map(line => `  ${line}`).join('\n');
  }
  formatFooter(metadata: Record<string, string>): string {
    return `\n/Author (${metadata.author ?? "Unknown"})`;
  }
}
 
class HtmlFormatter implements DocumentFormatter {
  formatHeader(title: string): string {
    return `<!DOCTYPE html>\n<html><head><title>${title}</title></head><body>\n`;
  }
  formatBody(content: string[]): string {
    return content.map(line => `<p>${line}</p>`).join('\n');
  }
  formatFooter(metadata: Record<string, string>): string {
    return `\n<footer>Author: ${metadata.author ?? "Unknown"}</footer></body></html>`;
  }
}
 
class MarkdownFormatter implements DocumentFormatter {
  formatHeader(title: string): string {
    return `# ${title}\n\n`;
  }
  formatBody(content: string[]): string {
    return content.join('\n\n');
  }
  formatFooter(metadata: Record<string, string>): string {
    return `\n\n---\n*Author: ${metadata.author ?? "Unknown"}*`;
  }
}
 
class CsvFormatter implements DocumentFormatter {
  formatHeader(title: string): string {
    return `"Title","${title}"\n`;
  }
  formatBody(content: string[]): string {
    return content.map((line, i) => `"${i + 1}","${line}"`).join('\n');
  }
  formatFooter(metadata: Record<string, string>): string {
    return `\n"Author","${metadata.author ?? "Unknown"}"`;
  }
}
 
// 使用例: エクスポート形式を動的に選択
function getExporter(format: string): DocumentExporter {
  switch (format) {
    case "pdf": return new PdfExporter();
    case "html": return new HtmlExporter();
    case "markdown": return new MarkdownExporter();
    case "csv": return new CsvExporter();
    default: throw new Error(`Unsupported format: ${format}`);
  }
}

1.4 Java での Factory Method

// Java: Factory Method パターンの実装
public interface Logger {
    void log(String level, String message);
    void close();
}
 
public class ConsoleLogger implements Logger {
    @Override
    public void log(String level, String message) {
        System.out.printf("[%s] %s: %s%n",
            LocalDateTime.now(), level, message);
    }
 
    @Override
    public void close() {
        // コンソールは閉じる必要なし
    }
}
 
public class FileLogger implements Logger {
    private final PrintWriter writer;
 
    public FileLogger(String filePath) throws IOException {
        this.writer = new PrintWriter(
            new FileWriter(filePath, true), true);
    }
 
    @Override
    public void log(String level, String message) {
        writer.printf("[%s] %s: %s%n",
            LocalDateTime.now(), level, message);
    }
 
    @Override
    public void close() {
        writer.close();
    }
}
 
public class DatabaseLogger implements Logger {
    private final Connection connection;
 
    public DatabaseLogger(String jdbcUrl) throws SQLException {
        this.connection = DriverManager.getConnection(jdbcUrl);
    }
 
    @Override
    public void log(String level, String message) {
        try (PreparedStatement stmt = connection.prepareStatement(
                "INSERT INTO logs (level, message, created_at) VALUES (?, ?, ?)")) {
            stmt.setString(1, level);
            stmt.setString(2, message);
            stmt.setTimestamp(3, Timestamp.valueOf(LocalDateTime.now()));
            stmt.executeUpdate();
        } catch (SQLException e) {
            System.err.println("Failed to log: " + e.getMessage());
        }
    }
 
    @Override
    public void close() {
        try { connection.close(); } catch (SQLException ignored) {}
    }
}
 
// ファクトリクラス
public class LoggerFactory {
    public static Logger createLogger(String type) {
        return createLogger(type, Map.of());
    }
 
    public static Logger createLogger(String type, Map<String, String> config) {
        switch (type.toLowerCase()) {
            case "console":
                return new ConsoleLogger();
            case "file":
                String path = config.getOrDefault("path", "app.log");
                try {
                    return new FileLogger(path);
                } catch (IOException e) {
                    throw new RuntimeException("Failed to create file logger", e);
                }
            case "database":
                String url = config.getOrDefault("url", "jdbc:h2:mem:logs");
                try {
                    return new DatabaseLogger(url);
                } catch (SQLException e) {
                    throw new RuntimeException("Failed to create database logger", e);
                }
            default:
                throw new IllegalArgumentException("Unknown logger type: " + type);
        }
    }
}
 
// 使用例
Logger logger = LoggerFactory.createLogger("file",
    Map.of("path", "/var/log/myapp.log"));
logger.log("INFO", "Application started");

1.5 登録ベースのファクトリ(拡張可能なファクトリ)

// 登録ベース: 新しいタイプを動的に追加可能
type Creator<T> = (...args: any[]) => T;
 
class PluggableFactory<T> {
  private creators = new Map<string, Creator<T>>();
 
  register(type: string, creator: Creator<T>): void {
    if (this.creators.has(type)) {
      throw new Error(`Type "${type}" is already registered`);
    }
    this.creators.set(type, creator);
  }
 
  unregister(type: string): boolean {
    return this.creators.delete(type);
  }
 
  create(type: string, ...args: any[]): T {
    const creator = this.creators.get(type);
    if (!creator) {
      const available = [...this.creators.keys()].join(", ");
      throw new Error(
        `Unknown type: "${type}". Available: ${available}`
      );
    }
    return creator(...args);
  }
 
  getRegisteredTypes(): string[] {
    return [...this.creators.keys()];
  }
 
  has(type: string): boolean {
    return this.creators.has(type);
  }
}
 
// 使用例: プラグイン可能な通知システム
interface NotificationPlugin {
  send(message: string, target: string): Promise<void>;
  getName(): string;
}
 
const notificationFactory = new PluggableFactory<NotificationPlugin>();
 
// コアプラグインの登録
notificationFactory.register("email", (smtpConfig: SmtpConfig) => ({
  async send(message: string, target: string) {
    // SMTP送信
    console.log(`Email to ${target}: ${message}`);
  },
  getName() { return "Email"; },
}));
 
notificationFactory.register("webhook", (url: string) => ({
  async send(message: string, target: string) {
    await fetch(url, {
      method: "POST",
      body: JSON.stringify({ message, target }),
    });
  },
  getName() { return "Webhook"; },
}));
 
// サードパーティがプラグインを追加
notificationFactory.register("teams", (webhookUrl: string) => ({
  async send(message: string, target: string) {
    await fetch(webhookUrl, {
      method: "POST",
      body: JSON.stringify({ text: `${target}: ${message}` }),
    });
  },
  getName() { return "Microsoft Teams"; },
}));
 
// 利用
const emailPlugin = notificationFactory.create("email", { host: "smtp.example.com" });
const teamsPlugin = notificationFactory.create("teams", "https://outlook.webhook.office.com/...");

2. Abstract Factory パターン

2.1 概要と目的

目的: 関連するオブジェクト群を、具象クラスを指定せずに生成する

いつ使うか:
  → 関連するオブジェクト群を一貫して生成したい
  → 製品ファミリーを切り替えたい(テーマ、プラットフォーム)
  → 具象クラスからクライアントを完全に分離したい

構造:
AbstractFactory
createProductA()
createProductB()
Factory1Factory2

2.2 UIテーマの切り替え

// Abstract Factory: 関連するオブジェクト群を生成
interface Button {
  render(): string;
  onClick(handler: () => void): void;
}
 
interface Input {
  render(): string;
  getValue(): string;
  setValue(value: string): void;
}
 
interface Modal {
  render(): string;
  open(): void;
  close(): void;
}
 
interface Card {
  render(): string;
  setContent(title: string, body: string): void;
}
 
interface UIFactory {
  createButton(label: string): Button;
  createInput(placeholder: string): Input;
  createModal(title: string): Modal;
  createCard(): Card;
}
 
// Material Design ファクトリ
class MaterialUIFactory implements UIFactory {
  createButton(label: string): Button {
    return {
      render: () => `<button class="mdc-button mdc-button--raised">${label}</button>`,
      onClick: (handler) => { /* Material Ripple + handler */ },
    };
  }
 
  createInput(placeholder: string): Input {
    let value = "";
    return {
      render: () => `
        <div class="mdc-text-field">
          <input class="mdc-text-field__input" placeholder="${placeholder}">
          <label class="mdc-floating-label">${placeholder}</label>
        </div>`,
      getValue: () => value,
      setValue: (v) => { value = v; },
    };
  }
 
  createModal(title: string): Modal {
    let isOpen = false;
    return {
      render: () => `
        <div class="mdc-dialog ${isOpen ? "mdc-dialog--open" : ""}">
          <div class="mdc-dialog__title">${title}</div>
        </div>`,
      open: () => { isOpen = true; },
      close: () => { isOpen = false; },
    };
  }
 
  createCard(): Card {
    let content = { title: "", body: "" };
    return {
      render: () => `
        <div class="mdc-card">
          <div class="mdc-card__title">${content.title}</div>
          <div class="mdc-card__body">${content.body}</div>
        </div>`,
      setContent: (title, body) => { content = { title, body }; },
    };
  }
}
 
// Ant Design ファクトリ
class AntDesignFactory implements UIFactory {
  createButton(label: string): Button {
    return {
      render: () => `<button class="ant-btn ant-btn-primary">${label}</button>`,
      onClick: (handler) => { /* Ant Button handler */ },
    };
  }
 
  createInput(placeholder: string): Input {
    let value = "";
    return {
      render: () => `
        <span class="ant-input-affix-wrapper">
          <input class="ant-input" placeholder="${placeholder}">
        </span>`,
      getValue: () => value,
      setValue: (v) => { value = v; },
    };
  }
 
  createModal(title: string): Modal {
    let isOpen = false;
    return {
      render: () => `
        <div class="ant-modal ${isOpen ? "ant-modal-visible" : ""}">
          <div class="ant-modal-header">${title}</div>
        </div>`,
      open: () => { isOpen = true; },
      close: () => { isOpen = false; },
    };
  }
 
  createCard(): Card {
    let content = { title: "", body: "" };
    return {
      render: () => `
        <div class="ant-card">
          <div class="ant-card-head">${content.title}</div>
          <div class="ant-card-body">${content.body}</div>
        </div>`,
      setContent: (title, body) => { content = { title, body }; },
    };
  }
}
 
// テーマを変更するだけで全UIコンポーネントが切り替わる
function buildDashboard(factory: UIFactory) {
  const searchInput = factory.createInput("検索...");
  const submitButton = factory.createButton("送信");
  const detailModal = factory.createModal("詳細");
  const summaryCard = factory.createCard();
  summaryCard.setContent("今月の売上", "¥1,234,567");
 
  return {
    render: () => `
      <div class="dashboard">
        ${searchInput.render()}
        ${submitButton.render()}
        ${summaryCard.render()}
        ${detailModal.render()}
      </div>
    `,
  };
}
 
// 設定に応じてファクトリを選択
const theme = process.env.UI_THEME ?? "material";
const factory: UIFactory = theme === "ant"
  ? new AntDesignFactory()
  : new MaterialUIFactory();
 
const dashboard = buildDashboard(factory);

2.3 データベース抽象化レイヤー

// Abstract Factory: データベースの抽象化
interface DbConnection {
  connect(): Promise<void>;
  disconnect(): Promise<void>;
  isConnected(): boolean;
}
 
interface DbQueryBuilder {
  select(table: string, columns: string[]): DbQueryBuilder;
  where(condition: string, params: unknown[]): DbQueryBuilder;
  orderBy(column: string, direction: "asc" | "desc"): DbQueryBuilder;
  limit(count: number): DbQueryBuilder;
  build(): { sql: string; params: unknown[] };
}
 
interface DbMigrationRunner {
  up(sql: string): Promise<void>;
  down(sql: string): Promise<void>;
  getVersion(): Promise<number>;
}
 
interface DatabaseFactory {
  createConnection(config: DbConfig): DbConnection;
  createQueryBuilder(): DbQueryBuilder;
  createMigrationRunner(connection: DbConnection): DbMigrationRunner;
}
 
// PostgreSQL 実装
class PostgresFactory implements DatabaseFactory {
  createConnection(config: DbConfig): DbConnection {
    return new PostgresConnection(config);
  }
 
  createQueryBuilder(): DbQueryBuilder {
    return new PostgresQueryBuilder();
  }
 
  createMigrationRunner(connection: DbConnection): DbMigrationRunner {
    return new PostgresMigrationRunner(connection);
  }
}
 
// MySQL 実装
class MySQLFactory implements DatabaseFactory {
  createConnection(config: DbConfig): DbConnection {
    return new MySQLConnection(config);
  }
 
  createQueryBuilder(): DbQueryBuilder {
    return new MySQLQueryBuilder();
  }
 
  createMigrationRunner(connection: DbConnection): DbMigrationRunner {
    return new MySQLMigrationRunner(connection);
  }
}
 
// SQLite 実装(テスト用)
class SQLiteFactory implements DatabaseFactory {
  createConnection(config: DbConfig): DbConnection {
    return new SQLiteConnection(config);
  }
 
  createQueryBuilder(): DbQueryBuilder {
    return new SQLiteQueryBuilder();
  }
 
  createMigrationRunner(connection: DbConnection): DbMigrationRunner {
    return new SQLiteMigrationRunner(connection);
  }
}
 
// アプリケーション層はデータベースの種類を知らない
class UserRepository {
  private queryBuilder: DbQueryBuilder;
 
  constructor(private dbFactory: DatabaseFactory) {
    this.queryBuilder = dbFactory.createQueryBuilder();
  }
 
  async findActiveUsers(limit: number = 10): Promise<User[]> {
    const query = this.queryBuilder
      .select("users", ["id", "name", "email", "role"])
      .where("active = ?", [true])
      .orderBy("created_at", "desc")
      .limit(limit)
      .build();
 
    // execute query...
    return [];
  }
}
 
// 環境に応じてファクトリを切り替え
function createDatabaseFactory(env: string): DatabaseFactory {
  switch (env) {
    case "production": return new PostgresFactory();
    case "development": return new MySQLFactory();
    case "test": return new SQLiteFactory();
    default: return new SQLiteFactory();
  }
}

2.4 Python での Abstract Factory

# Python: Abstract Factory パターン
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Protocol
 
# 抽象プロダクト
class Serializer(Protocol):
    def serialize(self, data: dict) -> str: ...
    def deserialize(self, raw: str) -> dict: ...
 
class Validator(Protocol):
    def validate(self, data: dict, schema: dict) -> list[str]: ...
 
class Formatter(Protocol):
    def format(self, data: dict) -> str: ...
 
# 抽象ファクトリ
class DataProcessingFactory(ABC):
    @abstractmethod
    def create_serializer(self) -> Serializer: ...
 
    @abstractmethod
    def create_validator(self) -> Validator: ...
 
    @abstractmethod
    def create_formatter(self) -> Formatter: ...
 
# JSON ファミリー
class JsonSerializer:
    def serialize(self, data: dict) -> str:
        import json
        return json.dumps(data, ensure_ascii=False, indent=2)
 
    def deserialize(self, raw: str) -> dict:
        import json
        return json.loads(raw)
 
class JsonValidator:
    def validate(self, data: dict, schema: dict) -> list[str]:
        errors = []
        for field, rules in schema.items():
            if rules.get("required") and field not in data:
                errors.append(f"Missing required field: {field}")
        return errors
 
class JsonFormatter:
    def format(self, data: dict) -> str:
        import json
        return json.dumps(data, ensure_ascii=False, indent=4)
 
class JsonProcessingFactory(DataProcessingFactory):
    def create_serializer(self) -> Serializer:
        return JsonSerializer()
 
    def create_validator(self) -> Validator:
        return JsonValidator()
 
    def create_formatter(self) -> Formatter:
        return JsonFormatter()
 
# XML ファミリー
class XmlSerializer:
    def serialize(self, data: dict) -> str:
        def dict_to_xml(d: dict, root: str = "root") -> str:
            xml = f"<{root}>"
            for key, value in d.items():
                xml += f"<{key}>{value}</{key}>"
            xml += f"</{root}>"
            return xml
        return dict_to_xml(data)
 
    def deserialize(self, raw: str) -> dict:
        # 簡易的なXMLパーサー
        import xml.etree.ElementTree as ET
        root = ET.fromstring(raw)
        return {child.tag: child.text for child in root}
 
class XmlValidator:
    def validate(self, data: dict, schema: dict) -> list[str]:
        # XSD ベースのバリデーション
        errors = []
        for field, rules in schema.items():
            if rules.get("required") and field not in data:
                errors.append(f"Missing required element: <{field}>")
        return errors
 
class XmlFormatter:
    def format(self, data: dict) -> str:
        lines = ['<?xml version="1.0" encoding="UTF-8"?>']
        lines.append("<root>")
        for key, value in data.items():
            lines.append(f"  <{key}>{value}</{key}>")
        lines.append("</root>")
        return "\n".join(lines)
 
class XmlProcessingFactory(DataProcessingFactory):
    def create_serializer(self) -> Serializer:
        return XmlSerializer()
 
    def create_validator(self) -> Validator:
        return XmlValidator()
 
    def create_formatter(self) -> Formatter:
        return XmlFormatter()
 
# 使用例
def process_data(factory: DataProcessingFactory, data: dict, schema: dict) -> str:
    validator = factory.create_validator()
    errors = validator.validate(data, schema)
    if errors:
        raise ValueError(f"Validation errors: {errors}")
 
    serializer = factory.create_serializer()
    serialized = serializer.serialize(data)
 
    formatter = factory.create_formatter()
    return formatter.format(data)

3. Builder パターン

3.1 概要と目的

目的: 複雑なオブジェクトの構築過程を分離する

いつ使うか:
  → コンストラクタの引数が多い(5個以上)
  → オプショナルなパラメータが多い
  → 段階的に構築したい
  → 同じ構築プロセスで異なる表現を生成したい

構造:
Director─────→Builder─────→Product
(指揮者)(構築者)(製品)

3.2 Fluent Builder(メソッドチェーン)

// Builder パターン: HTTPリクエストの構築
class HttpRequest {
  readonly method: string;
  readonly url: string;
  readonly headers: Record<string, string>;
  readonly body?: string;
  readonly timeout: number;
  readonly retries: number;
  readonly auth?: { type: string; credentials: string };
  readonly queryParams: Record<string, string>;
 
  private constructor(builder: HttpRequestBuilder) {
    this.method = builder.method;
    this.url = builder.url;
    this.headers = { ...builder.headers };
    this.body = builder.body;
    this.timeout = builder.timeout;
    this.retries = builder.retries;
    this.auth = builder.auth ? { ...builder.auth } : undefined;
    this.queryParams = { ...builder.queryParams };
  }
 
  static builder(method: string, url: string): HttpRequestBuilder {
    return new HttpRequestBuilder(method, url);
  }
}
 
class HttpRequestBuilder {
  headers: Record<string, string> = {};
  body?: string;
  timeout: number = 5000;
  retries: number = 0;
  auth?: { type: string; credentials: string };
  queryParams: Record<string, string> = {};
 
  constructor(
    public readonly method: string,
    public readonly url: string,
  ) {}
 
  setHeader(key: string, value: string): this {
    this.headers[key] = value;
    return this; // メソッドチェーン
  }
 
  setBody(body: string): this {
    this.body = body;
    return this;
  }
 
  setJsonBody(data: unknown): this {
    this.body = JSON.stringify(data);
    this.headers["Content-Type"] = "application/json";
    return this;
  }
 
  setTimeout(ms: number): this {
    this.timeout = ms;
    return this;
  }
 
  setRetries(n: number): this {
    this.retries = n;
    return this;
  }
 
  setBasicAuth(username: string, password: string): this {
    this.auth = {
      type: "basic",
      credentials: Buffer.from(`${username}:${password}`).toString("base64"),
    };
    return this;
  }
 
  setBearerToken(token: string): this {
    this.auth = { type: "bearer", credentials: token };
    return this;
  }
 
  addQueryParam(key: string, value: string): this {
    this.queryParams[key] = value;
    return this;
  }
 
  build(): HttpRequest {
    // バリデーション
    if (!this.url) throw new Error("URL is required");
    if (!this.method) throw new Error("Method is required");
    if (this.timeout < 0) throw new Error("Timeout must be non-negative");
    if (this.retries < 0) throw new Error("Retries must be non-negative");
 
    return new (HttpRequest as any)(this);
  }
}
 
// 可読性の高いオブジェクト構築
const request = HttpRequest.builder("POST", "https://api.example.com/users")
  .setHeader("Accept", "application/json")
  .setBearerToken("token123")
  .setJsonBody({ name: "田中", email: "tanaka@example.com" })
  .setTimeout(10000)
  .setRetries(3)
  .addQueryParam("version", "v2")
  .build();

3.3 Step Builder(段階的ビルダー)

// Step Builder: 必須パラメータを型で強制
interface NeedsRecipient {
  to(recipient: string): NeedsSubject;
}
 
interface NeedsSubject {
  subject(subject: string): EmailBuilder;
}
 
interface EmailBuilder {
  body(body: string): EmailBuilder;
  cc(address: string): EmailBuilder;
  bcc(address: string): EmailBuilder;
  attachment(file: Buffer, name: string): EmailBuilder;
  replyTo(address: string): EmailBuilder;
  priority(level: "low" | "normal" | "high"): EmailBuilder;
  build(): Email;
}
 
class Email {
  constructor(
    public readonly to: string,
    public readonly subject: string,
    public readonly body: string,
    public readonly cc: string[],
    public readonly bcc: string[],
    public readonly attachments: { file: Buffer; name: string }[],
    public readonly replyTo?: string,
    public readonly priority: "low" | "normal" | "high" = "normal",
  ) {}
 
  static create(): NeedsRecipient {
    return new EmailStepBuilder();
  }
}
 
class EmailStepBuilder implements NeedsRecipient, NeedsSubject, EmailBuilder {
  private _to = "";
  private _subject = "";
  private _body = "";
  private _cc: string[] = [];
  private _bcc: string[] = [];
  private _attachments: { file: Buffer; name: string }[] = [];
  private _replyTo?: string;
  private _priority: "low" | "normal" | "high" = "normal";
 
  to(recipient: string): NeedsSubject {
    this._to = recipient;
    return this;
  }
 
  subject(subject: string): EmailBuilder {
    this._subject = subject;
    return this;
  }
 
  body(body: string): EmailBuilder {
    this._body = body;
    return this;
  }
 
  cc(address: string): EmailBuilder {
    this._cc.push(address);
    return this;
  }
 
  bcc(address: string): EmailBuilder {
    this._bcc.push(address);
    return this;
  }
 
  attachment(file: Buffer, name: string): EmailBuilder {
    this._attachments.push({ file, name });
    return this;
  }
 
  replyTo(address: string): EmailBuilder {
    this._replyTo = address;
    return this;
  }
 
  priority(level: "low" | "normal" | "high"): EmailBuilder {
    this._priority = level;
    return this;
  }
 
  build(): Email {
    return new Email(
      this._to, this._subject, this._body,
      this._cc, this._bcc, this._attachments,
      this._replyTo, this._priority,
    );
  }
}
 
// 使用例: 型システムが順序を強制
const email = Email.create()
  .to("tanaka@example.com")     // 必須: 最初に指定
  .subject("月次レポート")       // 必須: 次に指定
  .body("添付のレポートをご確認ください。") // オプション
  .cc("manager@example.com")
  .priority("high")
  .build();
 
// Email.create().subject("...")  // コンパイルエラー: to() が先に必要

3.4 Director パターン(定型構築)

// Director: 定型的な構築手順をカプセル化
class QueryDirector {
  // ページネーション付きリスト取得
  static paginatedList<T>(
    builder: QueryBuilder<T>,
    page: number,
    pageSize: number,
  ): Query<T> {
    return builder
      .orderBy("created_at", "desc")
      .limit(pageSize)
      .offset((page - 1) * pageSize)
      .build();
  }
 
  // 検索クエリ
  static search<T>(
    builder: QueryBuilder<T>,
    keyword: string,
    searchFields: string[],
  ): Query<T> {
    const conditions = searchFields.map(
      field => `${field} LIKE '%${keyword}%'`
    );
    return builder
      .whereRaw(conditions.join(" OR "))
      .orderBy("relevance", "desc")
      .limit(50)
      .build();
  }
 
  // ソフトデリート済みを除外した取得
  static activeOnly<T>(
    builder: QueryBuilder<T>,
  ): Query<T> {
    return builder
      .where("deleted_at", "IS NULL")
      .orderBy("updated_at", "desc")
      .build();
  }
}

3.5 Java での Builder パターン

// Java: Builder パターン(Lombokなしの手書き版)
public class ServerConfig {
    private final String host;
    private final int port;
    private final boolean ssl;
    private final int maxConnections;
    private final int timeoutSeconds;
    private final String certPath;
    private final String keyPath;
    private final List<String> allowedOrigins;
    private final Map<String, String> customHeaders;
    private final boolean gzipEnabled;
    private final int maxRequestSize;
 
    private ServerConfig(Builder builder) {
        this.host = builder.host;
        this.port = builder.port;
        this.ssl = builder.ssl;
        this.maxConnections = builder.maxConnections;
        this.timeoutSeconds = builder.timeoutSeconds;
        this.certPath = builder.certPath;
        this.keyPath = builder.keyPath;
        this.allowedOrigins = List.copyOf(builder.allowedOrigins);
        this.customHeaders = Map.copyOf(builder.customHeaders);
        this.gzipEnabled = builder.gzipEnabled;
        this.maxRequestSize = builder.maxRequestSize;
    }
 
    // getter メソッド群...
    public String getHost() { return host; }
    public int getPort() { return port; }
    public boolean isSsl() { return ssl; }
    // ... 省略
 
    public static Builder builder(String host, int port) {
        return new Builder(host, port);
    }
 
    public static class Builder {
        // 必須
        private final String host;
        private final int port;
        // オプション(デフォルト値付き)
        private boolean ssl = false;
        private int maxConnections = 100;
        private int timeoutSeconds = 30;
        private String certPath = "";
        private String keyPath = "";
        private List<String> allowedOrigins = new ArrayList<>();
        private Map<String, String> customHeaders = new HashMap<>();
        private boolean gzipEnabled = true;
        private int maxRequestSize = 10 * 1024 * 1024; // 10MB
 
        private Builder(String host, int port) {
            this.host = host;
            this.port = port;
        }
 
        public Builder ssl(boolean ssl) {
            this.ssl = ssl;
            return this;
        }
 
        public Builder maxConnections(int max) {
            this.maxConnections = max;
            return this;
        }
 
        public Builder timeout(int seconds) {
            this.timeoutSeconds = seconds;
            return this;
        }
 
        public Builder cert(String certPath, String keyPath) {
            this.certPath = certPath;
            this.keyPath = keyPath;
            this.ssl = true;
            return this;
        }
 
        public Builder allowOrigin(String origin) {
            this.allowedOrigins.add(origin);
            return this;
        }
 
        public Builder header(String key, String value) {
            this.customHeaders.put(key, value);
            return this;
        }
 
        public Builder gzip(boolean enabled) {
            this.gzipEnabled = enabled;
            return this;
        }
 
        public Builder maxRequestSize(int bytes) {
            this.maxRequestSize = bytes;
            return this;
        }
 
        public ServerConfig build() {
            // バリデーション
            if (ssl && (certPath.isEmpty() || keyPath.isEmpty())) {
                throw new IllegalStateException(
                    "SSL enabled but cert/key paths not provided");
            }
            if (maxConnections <= 0) {
                throw new IllegalStateException("maxConnections must be positive");
            }
            return new ServerConfig(this);
        }
    }
}
 
// 使用例
ServerConfig config = ServerConfig.builder("0.0.0.0", 8080)
    .ssl(true)
    .cert("/etc/ssl/cert.pem", "/etc/ssl/key.pem")
    .maxConnections(500)
    .timeout(60)
    .allowOrigin("https://example.com")
    .allowOrigin("https://app.example.com")
    .header("X-Powered-By", "MyApp")
    .gzip(true)
    .build();

4. Singleton パターン

4.1 概要と注意点

目的: クラスのインスタンスが1つだけであることを保証する

注意: Singleton は「アンチパターン」として批判されることが多い

問題点:
  → グローバル状態 = テスト困難
  → 密結合 = 依存性注入の妨げ
  → 並行処理 = 競合状態のリスク
  → 隠れた依存 = コードの理解が困難

適切な用途:
  → ロガー、設定マネージャ(本当に1つでいい場合)
  → DIコンテナ側で「1つだけ」を制御する方が良い

4.2 基本実装

// Singleton(必要最小限の実装)
class AppConfig {
  private static instance: AppConfig;
 
  private constructor(
    public readonly dbUrl: string,
    public readonly apiKey: string,
    public readonly debug: boolean,
    public readonly logLevel: "debug" | "info" | "warn" | "error",
    public readonly maxRetries: number,
  ) {}
 
  static getInstance(): AppConfig {
    if (!AppConfig.instance) {
      AppConfig.instance = new AppConfig(
        process.env.DATABASE_URL ?? "localhost:5432",
        process.env.API_KEY ?? "",
        process.env.NODE_ENV !== "production",
        (process.env.LOG_LEVEL as any) ?? "info",
        parseInt(process.env.MAX_RETRIES ?? "3", 10),
      );
    }
    return AppConfig.instance;
  }
 
  // テスト用リセット
  static resetForTesting(): void {
    AppConfig.instance = undefined as any;
  }
 
  // テスト用: カスタムインスタンスの注入
  static setInstanceForTesting(config: AppConfig): void {
    AppConfig.instance = config;
  }
}

4.3 スレッドセーフな Singleton(Java)

// Java: スレッドセーフな Singleton の実装方法
 
// 方法1: Eager Initialization(最もシンプル)
public class EagerSingleton {
    // クラスロード時に生成(スレッドセーフ)
    private static final EagerSingleton INSTANCE = new EagerSingleton();
 
    private EagerSingleton() {}
 
    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
}
 
// 方法2: Double-Checked Locking(遅延初期化が必要な場合)
public class LazyThreadSafeSingleton {
    private static volatile LazyThreadSafeSingleton instance;
 
    private LazyThreadSafeSingleton() {}
 
    public static LazyThreadSafeSingleton getInstance() {
        if (instance == null) {
            synchronized (LazyThreadSafeSingleton.class) {
                if (instance == null) {
                    instance = new LazyThreadSafeSingleton();
                }
            }
        }
        return instance;
    }
}
 
// 方法3: Holder Pattern(推奨: 遅延初期化 + スレッドセーフ)
public class HolderSingleton {
    private HolderSingleton() {}
 
    // 内部クラスは最初にアクセスされるまでロードされない
    private static class Holder {
        private static final HolderSingleton INSTANCE = new HolderSingleton();
    }
 
    public static HolderSingleton getInstance() {
        return Holder.INSTANCE;
    }
}
 
// 方法4: Enum Singleton(最も安全: シリアライズ・リフレクション攻撃に耐える)
public enum EnumSingleton {
    INSTANCE;
 
    private final Map<String, String> config = new HashMap<>();
 
    public void setConfig(String key, String value) {
        config.put(key, value);
    }
 
    public String getConfig(String key) {
        return config.get(key);
    }
}

4.4 Singleton の代替: DIコンテナによるスコープ管理

// より良いアプローチ: DIコンテナでスコープ管理
interface ServiceContainer {
  register<T>(token: string, factory: () => T, scope?: "singleton" | "transient"): void;
  resolve<T>(token: string): T;
}
 
class SimpleContainer implements ServiceContainer {
  private factories = new Map<string, { factory: () => any; scope: string }>();
  private singletons = new Map<string, any>();
 
  register<T>(
    token: string,
    factory: () => T,
    scope: "singleton" | "transient" = "transient",
  ): void {
    this.factories.set(token, { factory, scope });
  }
 
  resolve<T>(token: string): T {
    const entry = this.factories.get(token);
    if (!entry) throw new Error(`Service not registered: ${token}`);
 
    if (entry.scope === "singleton") {
      if (!this.singletons.has(token)) {
        this.singletons.set(token, entry.factory());
      }
      return this.singletons.get(token);
    }
 
    return entry.factory();
  }
 
  // テスト用: シングルトンのリセット
  resetSingletons(): void {
    this.singletons.clear();
  }
}
 
// 使用例
const container = new SimpleContainer();
 
// シングルトンスコープで登録(1つのインスタンスを共有)
container.register("config", () => loadConfig(), "singleton");
container.register("logger", () => createLogger(), "singleton");
 
// トランジェントスコープで登録(毎回新しいインスタンス)
container.register("httpClient", () => new HttpClient(), "transient");
 
// Singleton の利点を活かしつつ、テスタビリティも確保
const config = container.resolve<AppConfig>("config");
const logger = container.resolve<Logger>("logger");
 
// テスト時
container.register("config", () => createTestConfig(), "singleton");
container.resetSingletons();

5. Prototype パターン

5.1 概要と目的

目的: 既存オブジェクトをコピーして新しいオブジェクトを生成する

いつ使うか:
  → 生成コストが高い(DB/APIから構築)
  → テンプレートオブジェクトを元に微調整
  → オブジェクトの状態を保存・復元(Memento との組み合わせ)
  → クラスの具体的な型を知らずにコピーしたい

5.2 基本実装

// Prototype パターン
interface Cloneable<T> {
  clone(): T;
  deepClone(): T;
}
 
class DocumentTemplate implements Cloneable<DocumentTemplate> {
  constructor(
    public title: string,
    public content: string,
    public styles: Record<string, string>,
    public metadata: Record<string, string>,
    public sections: Array<{ heading: string; body: string }>,
  ) {}
 
  // シャローコピー
  clone(): DocumentTemplate {
    return new DocumentTemplate(
      this.title,
      this.content,
      { ...this.styles },
      { ...this.metadata },
      [...this.sections],  // 注意: 中のオブジェクトは共有
    );
  }
 
  // ディープコピー
  deepClone(): DocumentTemplate {
    return new DocumentTemplate(
      this.title,
      this.content,
      { ...this.styles },
      { ...this.metadata },
      this.sections.map(s => ({ ...s })),  // 各セクションもコピー
    );
  }
}
 
// テンプレートからコピーして微調整
const template = new DocumentTemplate(
  "月次レポート",
  "## 概要\n...",
  { fontSize: "14px", fontFamily: "Noto Sans JP" },
  { author: "", department: "" },
  [
    { heading: "概要", body: "" },
    { heading: "実績", body: "" },
    { heading: "課題と対策", body: "" },
    { heading: "次月の計画", body: "" },
  ],
);
 
const januaryReport = template.deepClone();
januaryReport.title = "2025年1月 月次レポート";
januaryReport.metadata.author = "田中太郎";
januaryReport.metadata.department = "開発部";
januaryReport.sections[0].body = "1月の開発進捗をまとめる";
 
const februaryReport = template.deepClone();
februaryReport.title = "2025年2月 月次レポート";
februaryReport.metadata.author = "田中太郎";
februaryReport.sections[0].body = "2月の開発進捗をまとめる";

5.3 Prototype Registry(プロトタイプの管理)

// Prototype Registry: テンプレートを管理
class PrototypeRegistry<T extends Cloneable<T>> {
  private prototypes = new Map<string, T>();
 
  register(name: string, prototype: T): void {
    this.prototypes.set(name, prototype);
  }
 
  unregister(name: string): boolean {
    return this.prototypes.delete(name);
  }
 
  create(name: string): T {
    const prototype = this.prototypes.get(name);
    if (!prototype) {
      const available = [...this.prototypes.keys()].join(", ");
      throw new Error(`Unknown prototype: "${name}". Available: ${available}`);
    }
    return prototype.clone();
  }
 
  list(): string[] {
    return [...this.prototypes.keys()];
  }
}
 
// 使用例: フォームテンプレート
interface FormField {
  name: string;
  type: "text" | "number" | "email" | "select" | "textarea";
  label: string;
  required: boolean;
  options?: string[];
}
 
class FormTemplate implements Cloneable<FormTemplate> {
  constructor(
    public name: string,
    public description: string,
    public fields: FormField[],
  ) {}
 
  clone(): FormTemplate {
    return new FormTemplate(
      this.name,
      this.description,
      this.fields.map(f => ({ ...f, options: f.options ? [...f.options] : undefined })),
    );
  }
 
  deepClone(): FormTemplate {
    return this.clone();
  }
 
  addField(field: FormField): void {
    this.fields.push(field);
  }
}
 
// レジストリに定義済みフォームテンプレートを登録
const formRegistry = new PrototypeRegistry<FormTemplate>();
 
formRegistry.register("contact", new FormTemplate(
  "お問い合わせ",
  "お問い合わせフォーム",
  [
    { name: "name", type: "text", label: "氏名", required: true },
    { name: "email", type: "email", label: "メール", required: true },
    { name: "category", type: "select", label: "カテゴリ", required: true,
      options: ["一般", "技術", "営業", "その他"] },
    { name: "message", type: "textarea", label: "メッセージ", required: true },
  ],
));
 
formRegistry.register("survey", new FormTemplate(
  "アンケート",
  "顧客満足度アンケート",
  [
    { name: "satisfaction", type: "select", label: "満足度", required: true,
      options: ["非常に満足", "満足", "普通", "不満", "非常に不満"] },
    { name: "feedback", type: "textarea", label: "ご意見", required: false },
  ],
));
 
// テンプレートからカスタマイズ
const customContact = formRegistry.create("contact");
customContact.name = "技術サポート問い合わせ";
customContact.addField({
  name: "product",
  type: "select",
  label: "製品",
  required: true,
  options: ["製品A", "製品B", "製品C"],
});

5.4 JavaScript のスプレッド構文とStructured Clone

// JavaScript/TypeScript での現代的なコピー手法
 
// 1. スプレッド構文(シャローコピー)
const original = { name: "太郎", tags: ["developer", "manager"] };
const shallow = { ...original };
shallow.tags.push("admin");  // 注意: original.tags も変更される!
 
// 2. Object.assign(シャローコピー)
const assigned = Object.assign({}, original);
 
// 3. JSON.parse/JSON.stringify(ディープコピー、制限あり)
const jsonCopy = JSON.parse(JSON.stringify(original));
// ⚠️ 制限: Date, Map, Set, RegExp, 関数, undefined は正しくコピーされない
 
// 4. structuredClone(モダンなディープコピー、Node.js 17+)
const structuredCopy = structuredClone(original);
// ✅ Date, Map, Set, ArrayBuffer, RegExp を正しくコピー
// ❌ 関数, DOM ノード, Error オブジェクトは不可
 
// 5. 実践的なディープコピーユーティリティ
function deepClone<T>(obj: T): T {
  if (obj === null || typeof obj !== "object") return obj;
  if (obj instanceof Date) return new Date(obj.getTime()) as T;
  if (obj instanceof Map) {
    const map = new Map();
    obj.forEach((value, key) => map.set(deepClone(key), deepClone(value)));
    return map as T;
  }
  if (obj instanceof Set) {
    const set = new Set();
    obj.forEach(value => set.add(deepClone(value)));
    return set as T;
  }
  if (Array.isArray(obj)) return obj.map(item => deepClone(item)) as T;
 
  const copy = {} as T;
  for (const key of Object.keys(obj as object)) {
    (copy as any)[key] = deepClone((obj as any)[key]);
  }
  return copy;
}

6. Object Pool パターン

6.1 概要と目的

目的: 生成コストの高いオブジェクトを再利用する

いつ使うか:
  → オブジェクトの生成/破棄コストが高い(DB接続、スレッド)
  → 同時に必要なオブジェクト数が限られている
  → オブジェクトが再利用可能(状態をリセットできる)

6.2 接続プールの実装

// Object Pool: データベース接続プール
interface Poolable {
  reset(): void;
  isValid(): boolean;
  destroy(): void;
}
 
class ConnectionPool<T extends Poolable> {
  private available: T[] = [];
  private inUse = new Set<T>();
  private waitQueue: Array<{
    resolve: (conn: T) => void;
    reject: (error: Error) => void;
    timer: NodeJS.Timeout;
  }> = [];
 
  constructor(
    private factory: () => T,
    private readonly minSize: number = 2,
    private readonly maxSize: number = 10,
    private readonly acquireTimeoutMs: number = 5000,
  ) {
    // 最小数のオブジェクトを事前生成
    for (let i = 0; i < minSize; i++) {
      this.available.push(factory());
    }
  }
 
  async acquire(): Promise<T> {
    // 利用可能なオブジェクトがあれば返す
    while (this.available.length > 0) {
      const obj = this.available.pop()!;
      if (obj.isValid()) {
        this.inUse.add(obj);
        return obj;
      }
      obj.destroy(); // 無効なオブジェクトは破棄
    }
 
    // 最大数に達していなければ新規生成
    if (this.inUse.size < this.maxSize) {
      const obj = this.factory();
      this.inUse.add(obj);
      return obj;
    }
 
    // 空きを待つ
    return new Promise((resolve, reject) => {
      const timer = setTimeout(() => {
        const index = this.waitQueue.findIndex(w => w.timer === timer);
        if (index >= 0) this.waitQueue.splice(index, 1);
        reject(new Error("Connection pool acquire timeout"));
      }, this.acquireTimeoutMs);
 
      this.waitQueue.push({ resolve, reject, timer });
    });
  }
 
  release(obj: T): void {
    if (!this.inUse.has(obj)) return;
    this.inUse.delete(obj);
    obj.reset();
 
    // 待機中のリクエストがあれば渡す
    if (this.waitQueue.length > 0 && obj.isValid()) {
      const waiter = this.waitQueue.shift()!;
      clearTimeout(waiter.timer);
      this.inUse.add(obj);
      waiter.resolve(obj);
    } else if (obj.isValid()) {
      this.available.push(obj);
    } else {
      obj.destroy();
    }
  }
 
  // using パターン(確実にリリースする)
  async withConnection<R>(fn: (conn: T) => Promise<R>): Promise<R> {
    const conn = await this.acquire();
    try {
      return await fn(conn);
    } finally {
      this.release(conn);
    }
  }
 
  get stats() {
    return {
      available: this.available.length,
      inUse: this.inUse.size,
      waiting: this.waitQueue.length,
      total: this.available.length + this.inUse.size,
      maxSize: this.maxSize,
    };
  }
 
  async drain(): Promise<void> {
    for (const obj of this.available) obj.destroy();
    for (const obj of this.inUse) obj.destroy();
    this.available = [];
    this.inUse.clear();
    for (const waiter of this.waitQueue) {
      clearTimeout(waiter.timer);
      waiter.reject(new Error("Pool drained"));
    }
    this.waitQueue = [];
  }
}
 
// 使用例
const dbPool = new ConnectionPool(
  () => new DatabaseConnection("postgres://localhost:5432/mydb"),
  { minSize: 5, maxSize: 20, acquireTimeoutMs: 3000 }
);
 
// using パターン(推奨)
const users = await dbPool.withConnection(async (conn) => {
  return conn.query("SELECT * FROM users WHERE active = true");
});
 
console.log(dbPool.stats);
// { available: 5, inUse: 0, waiting: 0, total: 5, maxSize: 20 }

7. 選択指針

7.1 パターン比較

パターン使う場面
Simple Factory型を実行時に決定、生成ロジック集約
Factory Methodサブクラスが生成物の型を決定
Abstract Factory関連するオブジェクト群の一貫した生成
Builderパラメータが多い複雑な構築
Step Builder必須パラメータの順序を型で強制
Singleton本当に1つだけ必要(慎重に)
Prototype既存オブジェクトを元にコピー
Object Pool生成コストが高いオブジェクトの再利用

7.2 判断フローチャート

オブジェクト生成が必要
├── 生成する型が実行時に決まる
│   ├── 関連するオブジェクト群 → Abstract Factory
│   └── 単一のオブジェクト → Factory Method / Simple Factory
├── パラメータが多い or 段階的に構築
│   ├── 必須パラメータの順序を強制したい → Step Builder
│   └── 柔軟な構築 → Fluent Builder
├── 既存オブジェクトをコピーしたい → Prototype
├── インスタンス数を制限したい
│   ├── 1つだけ → Singleton(※DI推奨)
│   └── N個まで → Object Pool
└── シンプルな生成 → コンストラクタ直接呼び出し

FAQ

Q1: このトピックを学ぶ上で最も重要なポイントは何ですか?

実践的な経験を積むことが最も重要です。理論だけでなく、実際にコードを書いて動作を確認することで理解が深まります。

Q2: 初心者がよく陥る間違いは何ですか?

基礎を飛ばして応用に進むことです。このガイドで説明している基本概念をしっかり理解してから、次のステップに進むことをお勧めします。

Q3: 実務ではどのように活用されていますか?

このトピックの知識は、日常的な開発業務で頻繁に活用されます。特にコードレビューやアーキテクチャ設計の際に重要になります。


まとめ

パターン 目的 注意点
Simple Factory 生成ロジックの集約 過剰なFactory化を避ける
Factory Method サブクラスが型を決定 テンプレートメソッドと併用
Abstract Factory 製品ファミリーの切替 インターフェース数が増大しがち
Builder 段階的構築 単純なクラスには不要
Step Builder 型安全な段階構築 インターフェース数が増える
Singleton 唯一性の保証 DIで代替を検討
Prototype コピー生成 deep/shallow コピーに注意
Object Pool オブジェクト再利用 リリース忘れに注意

次に読むべきガイド


参考文献

  1. Gamma, E. et al. "Design Patterns: Elements of Reusable Object-Oriented Software." Addison-Wesley, 1994.
  2. Bloch, J. "Effective Java." 3rd Ed, Item 2: Consider a builder when faced with many constructor parameters. 2018.
  3. Freeman, E. et al. "Head First Design Patterns." O'Reilly, 2020.
  4. Martin, R.C. "Clean Code." Chapter 3: Functions. Prentice Hall, 2008.
  5. Fowler, M. "Patterns of Enterprise Application Architecture." Addison-Wesley, 2002.