Hikaru's notes

A collection of notes and resources

View the Project on GitHub HikaruMizoe/notes

GoFデザインパターン学習ノート(システムアーキテクト試験対策)

1. GoFデザインパターンとは

GoF(Gang of Four: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides)が1994年の著書 “Design Patterns: Elements of Reusable Object-Oriented Software” で示した、オブジェクト指向設計における 23種類の再利用可能な設計パターン。目的による3分類で整理されている。

カテゴリ 概要 主なパターン数
生成(Creational) オブジェクトの生成方法を抽象化し、生成と利用を分離する 5
構造(Structural) クラスやオブジェクトを組み合わせて、より大きな構造を作る 7
振る舞い(Behavioral) オブジェクト間の責務分担・通信方法を整理する 11

2. システムアーキテクト試験での出題傾向

本ノートでは上記の試験頻出パターンを中心に、目的・構造図(クラス図)・TypeScriptサンプルコード・ 試験で問われやすいポイントをまとめる。


第1部 生成に関するパターン(Creational Patterns)

オブジェクトの「生成方法」を、利用する側のコードから分離することが目的。 具体的なクラス名に依存せず、インターフェースや抽象クラスを通じてオブジェクトを生成できるようにする。

1-1. Singleton(シングルトン)

目的: クラスのインスタンスがただ1つだけ存在することを保証し、そのインスタンスへの グローバルなアクセス手段を提供する。

構造:

classDiagram
    class Singleton {
        -static instance: Singleton
        -Singleton()
        +static getInstance(): Singleton
        +operation(): void
    }
    note for Singleton "instanceは自分自身のクラス型\nコンストラクタはprivate"

TypeScript例:

class Singleton {
  private static instance: Singleton;

  // コンストラクタをprivateにして外部からのnewを禁止
  private constructor(private value: number = 0) {}

  public static getInstance(): Singleton {
    if (!Singleton.instance) {
      Singleton.instance = new Singleton();
    }
    return Singleton.instance;
  }

  public increment(): number {
    return ++this.value;
  }
}

const a = Singleton.getInstance();
const b = Singleton.getInstance();
console.log(a === b); // true: 同一インスタンス

試験で問われやすいポイント:


1-2. Factory Method(ファクトリーメソッド)

目的: オブジェクトの生成処理をサブクラスに委ね、どのクラスをインスタンス化するかを サブクラス側で決定できるようにする。

構造:

classDiagram
    class Creator {
        +factoryMethod(): Product
        +operation(): void
    }
    class ConcreteCreatorA {
        +factoryMethod(): Product
    }
    class ConcreteCreatorB {
        +factoryMethod(): Product
    }
    class Product {
        <<interface>>
    }
    class ConcreteProductA
    class ConcreteProductB

    Creator <|-- ConcreteCreatorA
    Creator <|-- ConcreteCreatorB
    Product <|.. ConcreteProductA
    Product <|.. ConcreteProductB
    ConcreteCreatorA ..> ConcreteProductA : creates
    ConcreteCreatorB ..> ConcreteProductB : creates

TypeScript例:

interface NotificationSender {
  send(message: string): void;
}

class EmailNotification implements NotificationSender {
  send(message: string): void {
    console.log(`Email送信: ${message}`);
  }
}

class SmsNotification implements NotificationSender {
  send(message: string): void {
    console.log(`SMS送信: ${message}`);
  }
}

abstract class NotifierFactory {
  // サブクラスが生成方法を決定する
  abstract createNotification(): NotificationSender;

  notify(message: string): void {
    const notification = this.createNotification();
    notification.send(message);
  }
}

class EmailNotifierFactory extends NotifierFactory {
  createNotification(): NotificationSender {
    return new EmailNotification();
  }
}

class SmsNotifierFactory extends NotifierFactory {
  createNotification(): NotificationSender {
    return new SmsNotification();
  }
}

const factory: NotifierFactory = new SmsNotifierFactory();
factory.notify("会議は15時からです"); // SMS送信: 会議は15時からです

試験で問われやすいポイント:


1-3. Abstract Factory(抽象ファクトリー)

目的: 互いに関連性・依存性のある複数のオブジェクト群(製品ファミリー)を、 具体的なクラスを指定せずに生成するためのインターフェースを提供する。

構造:

classDiagram
    class AbstractFactory {
        <<interface>>
        +createButton(): Button
        +createCheckbox(): Checkbox
    }
    class WindowsFactory {
        +createButton(): Button
        +createCheckbox(): Checkbox
    }
    class MacFactory {
        +createButton(): Button
        +createCheckbox(): Checkbox
    }
    class Button {
        <<interface>>
    }
    class Checkbox {
        <<interface>>
    }
    class WindowsButton
    class MacButton
    class WindowsCheckbox
    class MacCheckbox

    AbstractFactory <|.. WindowsFactory
    AbstractFactory <|.. MacFactory
    Button <|.. WindowsButton
    Button <|.. MacButton
    Checkbox <|.. WindowsCheckbox
    Checkbox <|.. MacCheckbox
    WindowsFactory ..> WindowsButton
    WindowsFactory ..> WindowsCheckbox
    MacFactory ..> MacButton
    MacFactory ..> MacCheckbox

TypeScript例:

interface Button {
  render(): string;
}
interface Checkbox {
  render(): string;
}

class WindowsButton implements Button {
  render() { return "[Windows Button]"; }
}
class WindowsCheckbox implements Checkbox {
  render() { return "[Windows Checkbox]"; }
}
class MacButton implements Button {
  render() { return "(Mac Button)"; }
}
class MacCheckbox implements Checkbox {
  render() { return "(Mac Checkbox)"; }
}

interface UiFactory {
  createButton(): Button;
  createCheckbox(): Checkbox;
}

class WindowsUiFactory implements UiFactory {
  createButton(): Button { return new WindowsButton(); }
  createCheckbox(): Checkbox { return new WindowsCheckbox(); }
}

class MacUiFactory implements UiFactory {
  createButton(): Button { return new MacButton(); }
  createCheckbox(): Checkbox { return new MacCheckbox(); }
}

function renderUi(factory: UiFactory): void {
  console.log(factory.createButton().render());
  console.log(factory.createCheckbox().render());
}

renderUi(new MacUiFactory());
// (Mac Button)
// (Mac Checkbox)

試験で問われやすいポイント:


1-4. Builder(ビルダー)

目的: 複雑なオブジェクトの生成過程(組み立て手順)と表現(最終的な構造)を分離し、 同じ生成過程で異なる表現の結果を作れるようにする。

構造:

classDiagram
    class Director {
        -builder: Builder
        +construct(): void
    }
    class Builder {
        <<interface>>
        +buildPartA(): void
        +buildPartB(): void
        +getResult(): Product
    }
    class ConcreteBuilder {
        -product: Product
        +buildPartA(): void
        +buildPartB(): void
        +getResult(): Product
    }
    class Product

    Director --> Builder
    Builder <|.. ConcreteBuilder
    ConcreteBuilder --> Product

TypeScript例:

class Pizza {
  private toppings: string[] = [];
  private size = "M";

  setSize(size: string) { this.size = size; return this; }
  addTopping(topping: string) { this.toppings.push(topping); return this; }

  describe(): string {
    return `サイズ:${this.size} トッピング:${this.toppings.join(", ")}`;
  }
}

class PizzaBuilder {
  private pizza = new Pizza();

  setSize(size: string): this {
    this.pizza.setSize(size);
    return this;
  }
  addTopping(topping: string): this {
    this.pizza.addTopping(topping);
    return this;
  }
  build(): Pizza {
    return this.pizza;
  }
}

// メソッドチェーンで段階的にオブジェクトを組み立てる
const pizza = new PizzaBuilder()
  .setSize("L")
  .addTopping("チーズ")
  .addTopping("ベーコン")
  .build();

console.log(pizza.describe()); // サイズ:L トッピング:チーズ, ベーコン

試験で問われやすいポイント:


第2部 構造に関するパターン(Structural Patterns)

クラスやオブジェクトを組み合わせることで、より大きな構造を実現することが目的。 継承による静的な組み合わせと、委譲(コンポジション)による動的な組み合わせの両方を含む。

2-1. Adapter(アダプター)

目的: あるクラスのインターフェースを、クライアントが期待する別のインターフェースに変換する。 既存クラスをそのまま再利用したい場合に有効。

構造:

classDiagram
    class Target {
        <<interface>>
        +request(): void
    }
    class Adapter {
        -adaptee: Adaptee
        +request(): void
    }
    class Adaptee {
        +specificRequest(): void
    }

    Target <|.. Adapter
    Adapter --> Adaptee : delegates

TypeScript例:

// 既存の(変更できない)クラス
class LegacyPrinter {
  printOldFormat(text: string): void {
    console.log(`[LEGACY] ${text}`);
  }
}

// クライアントが期待するインターフェース
interface ModernPrinter {
  print(text: string): void;
}

// Adapter: LegacyPrinterをModernPrinterに適合させる
class PrinterAdapter implements ModernPrinter {
  constructor(private legacy: LegacyPrinter) {}

  print(text: string): void {
    this.legacy.printOldFormat(text);
  }
}

const printer: ModernPrinter = new PrinterAdapter(new LegacyPrinter());
printer.print("見積書を印刷します"); // [LEGACY] 見積書を印刷します

試験で問われやすいポイント:


2-2. Decorator(デコレーター)

目的: オブジェクトに動的に新しい機能を追加する。サブクラス化による機能拡張の代替手段で、 機能の組み合わせを柔軟に行える。

構造:

classDiagram
    class Component {
        <<interface>>
        +operation(): string
    }
    class ConcreteComponent {
        +operation(): string
    }
    class Decorator {
        <<abstract>>
        -component: Component
        +operation(): string
    }
    class ConcreteDecoratorA {
        +operation(): string
    }
    class ConcreteDecoratorB {
        +operation(): string
    }

    Component <|.. ConcreteComponent
    Component <|.. Decorator
    Decorator <|-- ConcreteDecoratorA
    Decorator <|-- ConcreteDecoratorB
    Decorator --> Component : wraps

TypeScript例:

interface Coffee {
  cost(): number;
  description(): string;
}

class SimpleCoffee implements Coffee {
  cost(): number { return 300; }
  description(): string { return "コーヒー"; }
}

abstract class CoffeeDecorator implements Coffee {
  constructor(protected coffee: Coffee) {}
  cost(): number { return this.coffee.cost(); }
  description(): string { return this.coffee.description(); }
}

class MilkDecorator extends CoffeeDecorator {
  cost(): number { return super.cost() + 50; }
  description(): string { return super.description() + " + ミルク"; }
}

class SyrupDecorator extends CoffeeDecorator {
  cost(): number { return super.cost() + 30; }
  description(): string { return super.description() + " + シロップ"; }
}

// 機能を入れ子にして動的に組み合わせる
let coffee: Coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new SyrupDecorator(coffee);

console.log(`${coffee.description()} : ${coffee.cost()}円`);
// コーヒー + ミルク + シロップ : 380円

試験で問われやすいポイント:


2-3. Facade(ファサード)

目的: 複数のサブシステム(クラス群)に対する統一された簡易インターフェースを提供し、 利用者がサブシステムの複雑さを意識せずに使えるようにする。

構造:

classDiagram
    class Facade {
        +operation(): void
    }
    class SubsystemA {
        +operationA(): void
    }
    class SubsystemB {
        +operationB(): void
    }
    class SubsystemC {
        +operationC(): void
    }
    class Client

    Client --> Facade
    Facade --> SubsystemA
    Facade --> SubsystemB
    Facade --> SubsystemC

TypeScript例:

class InventorySystem {
  checkStock(item: string): boolean {
    console.log(`${item} の在庫を確認`);
    return true;
  }
}

class PaymentSystem {
  charge(amount: number): void {
    console.log(`${amount}円を決済`);
  }
}

class ShippingSystem {
  arrangeShipping(item: string): void {
    console.log(`${item} の発送手続き`);
  }
}

// Facade: 複雑なサブシステム群をまとめて操作する単一の窓口
class OrderFacade {
  private inventory = new InventorySystem();
  private payment = new PaymentSystem();
  private shipping = new ShippingSystem();

  placeOrder(item: string, amount: number): void {
    if (this.inventory.checkStock(item)) {
      this.payment.charge(amount);
      this.shipping.arrangeShipping(item);
      console.log("注文完了");
    }
  }
}

new OrderFacade().placeOrder("ノートPC", 120000);

試験で問われやすいポイント:


2-4. Proxy(プロキシ)

目的: 他のオブジェクトへのアクセスを制御する代理オブジェクトを提供する。 生成コストの高い処理の遅延実行、アクセス権限制御、リモートオブジェクトへの中継などに使う。

構造:

classDiagram
    class Subject {
        <<interface>>
        +request(): void
    }
    class RealSubject {
        +request(): void
    }
    class Proxy {
        -realSubject: RealSubject
        +request(): void
    }

    Subject <|.. RealSubject
    Subject <|.. Proxy
    Proxy --> RealSubject : controls access

TypeScript例:

interface Image {
  display(): void;
}

// 生成コストが高い実オブジェクト
class HighResolutionImage implements Image {
  constructor(private filename: string) {
    console.log(`${filename} を読み込み中...(時間がかかる処理)`);
  }
  display(): void {
    console.log(`${this.filename} を表示`);
  }
}

// Proxy: 実際に必要になるまで生成を遅延する(遅延初期化)
class ImageProxy implements Image {
  private realImage: HighResolutionImage | null = null;

  constructor(private filename: string) {}

  display(): void {
    if (!this.realImage) {
      this.realImage = new HighResolutionImage(this.filename);
    }
    this.realImage.display();
  }
}

const image: Image = new ImageProxy("photo.jpg");
console.log("画像オブジェクト生成完了(まだ読み込まれていない)");
image.display(); // ここで初めて読み込み処理が走る

試験で問われやすいポイント:


2-5. Composite(コンポジット)

目的: 個々のオブジェクト(葉)と、それらを組み合わせた集合体(複合オブジェクト)を 同一のインターフェースで扱えるようにし、再帰的な木構造(階層構造)を表現する。

構造:

classDiagram
    class Component {
        <<interface>>
        +operation(): void
    }
    class Leaf {
        +operation(): void
    }
    class Composite {
        -children: Component[]
        +add(c: Component): void
        +operation(): void
    }

    Component <|.. Leaf
    Component <|.. Composite
    Composite --> Component : children *

TypeScript例:

interface FileSystemNode {
  getSize(): number;
  print(indent?: string): void;
}

// Leaf: これ以上分解できない要素
class FileNode implements FileSystemNode {
  constructor(private name: string, private size: number) {}

  getSize(): number { return this.size; }

  print(indent = ""): void {
    console.log(`${indent}- ${this.name} (${this.size}KB)`);
  }
}

// Composite: 子要素を持ち、Leafと同じインターフェースで扱える
class FolderNode implements FileSystemNode {
  private children: FileSystemNode[] = [];

  constructor(private name: string) {}

  add(node: FileSystemNode): void {
    this.children.push(node);
  }

  getSize(): number {
    return this.children.reduce((sum, c) => sum + c.getSize(), 0);
  }

  print(indent = ""): void {
    console.log(`${indent}+ ${this.name} (${this.getSize()}KB)`);
    this.children.forEach((c) => c.print(indent + "  "));
  }
}

const root = new FolderNode("プロジェクト");
const src = new FolderNode("src");
src.add(new FileNode("main.ts", 10));
src.add(new FileNode("util.ts", 5));
root.add(src);
root.add(new FileNode("README.md", 2));

root.print();
// + プロジェクト (17KB)
//   + src (15KB)
//     - main.ts (10KB)
//     - util.ts (5KB)
//   - README.md (2KB)

試験で問われやすいポイント:


第3部 振る舞いに関するパターン(Behavioral Patterns)

オブジェクト間の責務分担やアルゴリズム・処理フローの組み立て方を整理することが目的。

3-1. Observer(オブザーバー)

目的: あるオブジェクト(Subject)の状態が変化したときに、それに依存する 複数のオブジェクト(Observer)に自動的に通知する1対多の依存関係を定義する。

構造:

classDiagram
    class Subject {
        -observers: Observer[]
        +attach(o: Observer): void
        +detach(o: Observer): void
        +notify(): void
    }
    class Observer {
        <<interface>>
        +update(data: any): void
    }
    class ConcreteSubject {
        -state: any
        +setState(s: any): void
    }
    class ConcreteObserverA {
        +update(data: any): void
    }
    class ConcreteObserverB {
        +update(data: any): void
    }

    Subject <|-- ConcreteSubject
    Observer <|.. ConcreteObserverA
    Observer <|.. ConcreteObserverB
    Subject --> Observer : notifies *

TypeScript例:

interface Observer {
  update(temperature: number): void;
}

class WeatherStation {
  private observers: Observer[] = [];
  private temperature = 0;

  subscribe(observer: Observer): void {
    this.observers.push(observer);
  }

  setTemperature(temp: number): void {
    this.temperature = temp;
    this.notifyAll();
  }

  private notifyAll(): void {
    this.observers.forEach((o) => o.update(this.temperature));
  }
}

class Display implements Observer {
  constructor(private name: string) {}
  update(temperature: number): void {
    console.log(`${this.name}: 現在の気温は${temperature}度です`);
  }
}

const station = new WeatherStation();
station.subscribe(new Display("リビングの表示パネル"));
station.subscribe(new Display("スマホアプリ"));

station.setTemperature(28);
// リビングの表示パネル: 現在の気温は28度です
// スマホアプリ: 現在の気温は28度です

試験で問われやすいポイント:


3-2. Strategy(ストラテジー)

目的: アルゴリズム(処理方法)をカプセル化して交換可能にし、 利用するクラスの変更なしにアルゴリズムを切り替えられるようにする。

構造:

classDiagram
    class Context {
        -strategy: Strategy
        +setStrategy(s: Strategy): void
        +executeStrategy(): any
    }
    class Strategy {
        <<interface>>
        +execute(data: any): any
    }
    class ConcreteStrategyA {
        +execute(data: any): any
    }
    class ConcreteStrategyB {
        +execute(data: any): any
    }

    Context --> Strategy
    Strategy <|.. ConcreteStrategyA
    Strategy <|.. ConcreteStrategyB

TypeScript例:

interface DiscountStrategy {
  calculate(price: number): number;
}

class NoDiscount implements DiscountStrategy {
  calculate(price: number): number { return price; }
}

class PercentageDiscount implements DiscountStrategy {
  constructor(private percent: number) {}
  calculate(price: number): number {
    return price * (1 - this.percent / 100);
  }
}

class FixedDiscount implements DiscountStrategy {
  constructor(private amount: number) {}
  calculate(price: number): number {
    return Math.max(0, price - this.amount);
  }
}

// Context: アルゴリズム(DiscountStrategy)を実行時に切り替える
class ShoppingCart {
  constructor(private strategy: DiscountStrategy) {}

  setStrategy(strategy: DiscountStrategy): void {
    this.strategy = strategy;
  }

  checkout(price: number): number {
    return this.strategy.calculate(price);
  }
}

const cart = new ShoppingCart(new NoDiscount());
console.log(cart.checkout(1000)); // 1000

cart.setStrategy(new PercentageDiscount(10));
console.log(cart.checkout(1000)); // 900

試験で問われやすいポイント:


3-3. Template Method(テンプレートメソッド)

目的: 処理の骨格(アルゴリズムの大枠)を親クラスで定義し、 一部のステップの具体的な実装をサブクラスに委ねる。

構造:

classDiagram
    class AbstractClass {
        <<abstract>>
        +templateMethod(): void
        #step1(): void
        #step2(): void
    }
    class ConcreteClassA {
        #step1(): void
        #step2(): void
    }
    class ConcreteClassB {
        #step1(): void
        #step2(): void
    }

    AbstractClass <|-- ConcreteClassA
    AbstractClass <|-- ConcreteClassB
    note for AbstractClass "templateMethod()内でstep1, step2を\n決まった順序で呼び出す"

TypeScript例:

abstract class ReportGenerator {
  // テンプレートメソッド: 処理の流れを固定する
  generate(): void {
    this.loadData();
    this.formatData();
    this.exportReport();
  }

  protected loadData(): void {
    console.log("共通: データを読み込む");
  }

  protected abstract formatData(): void;

  protected exportReport(): void {
    console.log("共通: レポートを出力する");
  }
}

class CsvReportGenerator extends ReportGenerator {
  protected formatData(): void {
    console.log("CSV形式に整形する");
  }
}

class PdfReportGenerator extends ReportGenerator {
  protected formatData(): void {
    console.log("PDF形式に整形する");
  }
}

new CsvReportGenerator().generate();
// 共通: データを読み込む
// CSV形式に整形する
// 共通: レポートを出力する

試験で問われやすいポイント:


3-4. Iterator(イテレーター)

目的: 集合体(コレクション)の内部表現を意識せずに、要素に順番にアクセスする手段を提供する。

構造:

classDiagram
    class Aggregate {
        <<interface>>
        +createIterator(): Iterator
    }
    class Iterator {
        <<interface>>
        +hasNext(): boolean
        +next(): any
    }
    class ConcreteAggregate {
        +createIterator(): Iterator
    }
    class ConcreteIterator {
        -collection: ConcreteAggregate
        -index: number
        +hasNext(): boolean
        +next(): any
    }

    Aggregate <|.. ConcreteAggregate
    Iterator <|.. ConcreteIterator
    ConcreteAggregate ..> ConcreteIterator : creates

TypeScript例:

class TaskCollection {
  private tasks: string[] = [];

  add(task: string): void {
    this.tasks.push(task);
  }

  // JavaScript/TypeScript標準のIteratorプロトコルを実装
  [Symbol.iterator](): Iterator<string> {
    let index = 0;
    const tasks = this.tasks;
    return {
      next(): IteratorResult<string> {
        if (index < tasks.length) {
          return { value: tasks[index++], done: false };
        }
        return { value: undefined, done: true };
      },
    };
  }
}

const tasks = new TaskCollection();
tasks.add("要件定義");
tasks.add("設計");
tasks.add("実装");

// 内部構造(配列)を知らずに順番にアクセスできる
for (const task of tasks) {
  console.log(task);
}
// 要件定義
// 設計
// 実装

試験で問われやすいポイント:


3-5. Command(コマンド)

目的: 要求(操作)をオブジェクトとしてカプセル化し、要求の発行者(Invoker)と 実行者(Receiver)を分離する。実行の取り消し(Undo)、再実行、キューイング、ログ記録などに応用できる。

構造:

classDiagram
    class Command {
        <<interface>>
        +execute(): void
        +undo(): void
    }
    class ConcreteCommand {
        -receiver: Receiver
        +execute(): void
        +undo(): void
    }
    class Receiver {
        +action(): void
    }
    class Invoker {
        -command: Command
        +setCommand(c: Command): void
        +executeCommand(): void
    }

    Command <|.. ConcreteCommand
    ConcreteCommand --> Receiver
    Invoker --> Command

TypeScript例:

interface Command {
  execute(): void;
  undo(): void;
}

// Receiver: 実際の処理を行うオブジェクト
class TextDocument {
  private content = "";

  append(text: string): void {
    this.content += text;
  }
  removeLast(length: number): void {
    this.content = this.content.slice(0, -length);
  }
  getContent(): string {
    return this.content;
  }
}

// ConcreteCommand: 操作をオブジェクト化し、Undoも実装
class AppendTextCommand implements Command {
  constructor(private doc: TextDocument, private text: string) {}

  execute(): void {
    this.doc.append(this.text);
  }
  undo(): void {
    this.doc.removeLast(this.text.length);
  }
}

// Invoker: コマンドの実行履歴を管理する
class CommandHistory {
  private history: Command[] = [];

  execute(command: Command): void {
    command.execute();
    this.history.push(command);
  }

  undoLast(): void {
    const command = this.history.pop();
    command?.undo();
  }
}

const doc = new TextDocument();
const commandHistory = new CommandHistory();

commandHistory.execute(new AppendTextCommand(doc, "こんにちは"));
commandHistory.execute(new AppendTextCommand(doc, "、世界"));
console.log(doc.getContent()); // こんにちは、世界

commandHistory.undoLast();
console.log(doc.getContent()); // こんにちは

試験で問われやすいポイント:


3-6. State(ステート)

目的: オブジェクトの状態(State)に応じて振る舞いを変える処理を、 状態ごとのクラスに分割し、状態遷移を明示的に管理する。

構造:

classDiagram
    class Context {
        -state: State
        +setState(s: State): void
        +request(): void
    }
    class State {
        <<interface>>
        +handle(context: Context): void
    }
    class ConcreteStateA {
        +handle(context: Context): void
    }
    class ConcreteStateB {
        +handle(context: Context): void
    }

    Context --> State
    State <|.. ConcreteStateA
    State <|.. ConcreteStateB
    ConcreteStateA ..> ConcreteStateB : transitions to

TypeScript例:

interface OrderState {
  next(order: Order): void;
  description(): string;
}

class Order {
  private state: OrderState = new PendingState();

  setState(state: OrderState): void {
    this.state = state;
  }
  proceed(): void {
    this.state.next(this);
  }
  getStatus(): string {
    return this.state.description();
  }
}

class PendingState implements OrderState {
  next(order: Order): void { order.setState(new ShippedState()); }
  description(): string { return "保留中"; }
}

class ShippedState implements OrderState {
  next(order: Order): void { order.setState(new DeliveredState()); }
  description(): string { return "発送済み"; }
}

class DeliveredState implements OrderState {
  next(order: Order): void { /* これ以上遷移しない */ }
  description(): string { return "配達完了"; }
}

const order = new Order();
console.log(order.getStatus()); // 保留中

order.proceed();
console.log(order.getStatus()); // 発送済み

order.proceed();
console.log(order.getStatus()); // 配達完了

試験で問われやすいポイント:


第4部 まとめ:パターン早見表

パターン カテゴリ 一言でいうと
Singleton 生成 インスタンスを1つだけに限定する
Factory Method 生成 生成するクラスをサブクラスに委ねる
Abstract Factory 生成 関連する製品群をまとめて生成する
Builder 生成 複雑なオブジェクトを段階的に組み立てる
Adapter 構造 インターフェースを変換して再利用する
Decorator 構造 オブジェクトに機能を動的に追加する
Facade 構造 サブシステムへの統一窓口を提供する
Proxy 構造 本物の代わりにアクセスを制御する代理を置く
Composite 構造 葉と複合体を同一視して木構造を扱う
Observer 振る舞い 状態変化を購読者に自動通知する
Strategy 振る舞い アルゴリズムを交換可能にする
Template Method 振る舞い 処理の骨格を定義し詳細をサブクラスに委ねる
Iterator 振る舞い 内部構造を隠して順次アクセスを提供する
Command 振る舞い 操作をオブジェクト化し発行者と実行者を分離する
State 振る舞い 状態に応じて振る舞いを切り替える

学習の進め方の提案

  1. まずは上の早見表の「一言でいうと」を自分の言葉で説明できるようにする(午前対策)。
  2. 似ているパターンの組(Strategy vs State、Decorator vs Proxy、 Factory Method vs Abstract Factory)を目的の違いで比較できるようにする。
  3. 午後対策として、過去問の事例(業務システム・オンラインショップなど)に対し 「ここはObserverで実装できそう」「この拡張はDecoratorが適切」のように、 自分で適用パターンを当てはめる練習をする。