Skip to content

ビジターパターン

公開日:November 17, 2024更新日:November 28, 2024
TypeScriptDesign pattern📄

ビジターパターン

ビジターパターン(Visitor Pattern) は、オブジェクトの構造を変更せずに新しい操作を追加することを可能にするデザインパターンです。このパターンは、オブジェクトのデータ構造と、そこに対して行う処理を分離することにより、操作の追加を簡単に行えるようにします。

ビジターパターンは、オブジェクトのデータ構造が固定されているが、その上で行う処理が頻繁に変わる場合に有効です。たとえば、ファイルシステム内のファイルやフォルダに対して、さまざまな操作(表示、サイズ計算など)を追加する場合に役立ちます。

TypeScriptでのビジターパターンの実装

以下に、TypeScriptでビジターパターンを実装する例を紹介します。この例では、図形(サークルと四角形)に対して、異なる操作(面積計算や描画)をビジターを使って行います。

図形とビジターの例

typescript
// ビジターインターフェース
interface Visitor {
  visitCircle(circle: Circle): void;
  visitSquare(square: Square): void;
}

// 要素インターフェース(ビジターを受け入れる)
interface Shape {
  accept(visitor: Visitor): void;
}

// サークルクラス
class Circle implements Shape {
  constructor(public radius: number) {}

  accept(visitor: Visitor): void {
    visitor.visitCircle(this);
  }
}

// 四角形クラス
class Square implements Shape {
  constructor(public sideLength: number) {}

  accept(visitor: Visitor): void {
    visitor.visitSquare(this);
  }
}

// 面積計算ビジター
class AreaCalculator implements Visitor {
  visitCircle(circle: Circle): void {
    const area = Math.PI * Math.pow(circle.radius, 2);
    console.log(`Area of the circle: ${area.toFixed(2)}`);
  }

  visitSquare(square: Square): void {
    const area = Math.pow(square.sideLength, 2);
    console.log(`Area of the square: ${area.toFixed(2)}`);
  }
}

// 描画ビジター
class DrawShape implements Visitor {
  visitCircle(circle: Circle): void {
    console.log(`Drawing a circle with radius ${circle.radius}`);
  }

  visitSquare(square: Square): void {
    console.log(`Drawing a square with side length ${square.sideLength}`);
  }
}

// 実際の使用例
const shapes: Shape[] = [new Circle(5), new Square(4)];

const areaCalculator = new AreaCalculator();
const drawShape = new DrawShape();

for (const shape of shapes) {
  shape.accept(areaCalculator);
}

console.log('\n--- Drawing Shapes ---');
for (const shape of shapes) {
  shape.accept(drawShape);
}

解説

  1. Visitorインターフェース

    • Visitorインターフェースは、各図形に対する操作(visitCirclevisitSquare)を定義しています。これにより、各ビジターは図形ごとに異なる処理を実装することができます。
  2. Shapeインターフェース(要素)

    • Shapeインターフェースは、acceptメソッドを持ち、ビジターを受け入れる役割を果たします。これにより、具体的な図形クラス(CircleSquare)はビジターを受け入れて、自身に対する処理を委譲します。
  3. CircleクラスとSquareクラス(具体的な要素)

    • CircleSquareShapeインターフェースを実装しており、acceptメソッドでビジターを受け入れ、それぞれの図形に対する処理を実行します。
  4. AreaCalculatorクラスとDrawShapeクラス(具体的なビジター)

    • AreaCalculatorは、図形の面積を計算するビジターで、各図形に対する面積の計算処理を行います。
    • DrawShapeは、図形を描画するビジターで、各図形に対する描画処理を行います。
  5. 実際の使用例

    • 複数の図形を保持する配列に対して、AreaCalculatorビジターとDrawShapeビジターを使って、それぞれ面積の計算と描画を行います。これにより、図形クラスに新しい操作を追加することなく、新しいビジターを導入することで新しい処理を追加できます。

利点

  • オープン/クローズド原則の遵守: 新しい操作を追加する際に、既存の図形クラスを変更する必要がないため、オープン/クローズド原則に従った設計が可能です。
  • 処理の分離: データ構造(図形クラス)と処理(ビジター)を分離することで、各クラスの責務を明確にし、コードの保守性を向上させます。
  • 拡張性の向上: 新しいビジターを追加するだけで、新しい処理を図形に対して行うことができるため、拡張性が向上します。

使用例

  • コンパイラの構文木処理: 抽象構文木(AST)に対して、異なる処理(コードの解析、最適化、コード生成など)を追加する場合に使用されます。
  • ファイルシステムの操作: ファイルやディレクトリに対して、さまざまな処理(表示、検索、サイズ計算など)を追加する際に有効です。
  • ゲーム開発: ゲーム内のオブジェクトに対して異なる操作(レンダリング、衝突判定、更新など)を追加する場合に使用されます。

まとめ

ビジターパターンは、オブジェクトの構造を変更せずに新しい操作を追加するためのデザインパターンです。TypeScriptでの実装を通じて、データ構造とその上で行う処理を分離する方法を理解することができました。

このパターンを利用することで、新しい操作を簡単に追加することができ、コードの保守性と拡張性を向上させることが可能です。特に、オブジェクトの構造は固定されているが、新しい操作が頻繁に必要になる場合に有効です。