Featured image of post Visitorパターン(Visitor Design Pattern)調査ドキュメント

Visitorパターン(Visitor Design Pattern)調査ドキュメント

Visitorパターン(GoF Behavioral Pattern)についての包括的な調査。基本構造、用途、利点・欠点、Perl実装例、内部リンク候補を含む。

Visitorパターン(Visitor Design Pattern)調査ドキュメント

調査概要

調査対象: Visitorパターン(GoFデザインパターン - 振る舞いに関するパターン)
調査実施日: 2025年12月31日
調査者: 10年以上の経験を持つ調査・情報収集専門家
調査目的: Visitorパターンに関する最新かつ信頼性の高い情報を収集し、ブログ記事作成のための基盤資料とする


1. Visitorパターンの概要

1.1 GoFデザインパターンにおける位置づけ

要点:

  • Visitorパターンは、GoF(Gang of Four)の23のデザインパターンの一つ
  • **振る舞いに関するパターン(Behavioral Patterns)**に分類される(全11種類のうちの1つ)
  • オブジェクト間の責任分担とコミュニケーションを管理するパターン群に属する

根拠:

  • GoF書籍「Design Patterns: Elements of Reusable Object-Oriented Software」(1994年)にて定義
  • 振る舞いパターンの中でも、特に「オブジェクト構造を変更せずに新しい操作を追加する」という明確な目的を持つ

出典:

信頼度: 高(公式文献および著名な技術サイト)


1.2 パターンの目的

要点: Visitorパターンの主な目的は「オブジェクト構造(例:複数の異なる型の集合体)を変更せず、新しい操作(処理)を容易に追加できるようにする」こと。

詳細説明:

  • アルゴリズム(処理ロジック)をオブジェクト(データ構造)から分離する
  • オブジェクトの要素クラスを変更せずに、新しい操作を追加できる
  • 開放閉鎖原則(Open/Closed Principle)に準拠:拡張には開いており、修正には閉じている

具体例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
状況: 複数の異なる型の要素(FileノードとDirectoryノード)から成るファイルシステム
要求: 
  - サイズ計算機能を追加したい
  - 後日、権限チェック機能も追加したい
  - さらに、ウイルススキャン機能も追加したい

従来の方法:
  各要素クラス(File, Directory)にcalcSize(), checkPermission(), scanVirus()メソッドを追加
  → クラスが肥大化し、責任が不明確になる

Visitorパターン:
  SizeVisitor, PermissionVisitor, VirusScanVisitorを個別に作成
  → 要素クラスは変更不要、新しい操作はVisitorとして追加

根拠:

  • 要素の型が固定されており、新しい処理(ビジター)を追加していきたいケースに最適
  • 複数型の集合体を横断的に処理したい場合に有効(if文やinstanceofチェックを排除)

出典:

信頼度: 高


1.3 基本的な構造(登場人物/要素)

要点: Visitorパターンは以下の4つの主要な要素(役割)から構成される。

要素名役割説明
Visitor(訪問者)インターフェース各要素型ごとの訪問メソッド(visit)を定義
ConcreteVisitor(具体訪問者)実装クラス実際の処理ロジックを実装
Element(要素)インターフェースVisitorを受け入れるaccept(visitor)メソッドを定義
ConcreteElement(具体要素)実装クラス個々のデータ構造を表し、acceptメソッドで自分自身をVisitorに渡す
ObjectStructure(オブジェクト構造)コレクションElementのコレクション(配列やツリー構造など)

構造図(簡略版):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<<interface>> Visitor
  + visitElementA(ElementA)
  + visitElementB(ElementB)
      | implements
ConcreteVisitor
  + visitElementA(ElementA) { /* 処理A */ }
  + visitElementB(ElementB) { /* 処理B */ }

<<interface>> Element
  + accept(Visitor)
      | implements
ConcreteElementA          ConcreteElementB
  + accept(v) {             + accept(v) {
      v.visitElementA(this)     v.visitElementB(this)
    }                         }

ダブルディスパッチ(Double Dispatch)の仕組み:

  • Visitorパターンの核心概念
  • element.accept(visitor)visitor.visit(element) の2段階呼び出し
  • 要素型(Element)とビジター型(Visitor)の両方に基づいて処理を決定

根拠:

  • GoF書籍およびパターン解説文献で一貫した構造定義
  • ダブルディスパッチにより、単一ディスパッチ(通常のメソッド呼び出し)では実現困難な柔軟性を実現

出典:

信頼度: 高


2. Visitorパターンの用途

2.1 どのような問題を解決するのか

要点: Visitorパターンは以下のような問題を解決する。

  1. クラスの肥大化問題

    • 複数の異なる操作を同一クラスに追加していくと、クラスが肥大化し、単一責任原則(SRP)に違反する
    • Visitorパターンでは、各操作を独立したVisitorクラスとして分離可能
  2. if/switch文の乱立

    • 型判定(instanceof、type checking)による条件分岐が増加
    • Visitorパターンではダブルディスパッチにより型判定を排除
  3. 開放閉鎖原則(OCP)の違反

    • 新しい操作を追加するたびに既存クラスを修正する必要がある
    • Visitorパターンでは新しいConcreteVisitorを追加するだけで済む

解決前のコード例(アンチパターン):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 悪い例:各要素クラスに複数の操作を詰め込む
package File;
sub calc_size { ... }
sub check_permission { ... }
sub scan_virus { ... }
sub compress { ... }
# → クラスが肥大化、責任が不明確

# または、型判定による分岐
sub process_element {
    my ($element) = @_;
    if ($element->isa('File')) {
        # File固有の処理
    } elsif ($element->isa('Directory')) {
        # Directory固有の処理
    }
    # → 新しい型を追加するたびに修正が必要
}

Visitorパターンでの解決:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 各操作を独立したVisitorとして分離
package SizeVisitor;
sub visit_file { ... }
sub visit_directory { ... }

package PermissionVisitor;
sub visit_file { ... }
sub visit_directory { ... }

# 要素クラスはacceptメソッドだけを持つ
package File;
sub accept {
    my ($self, $visitor) = @_;
    $visitor->visit_file($self);
}

根拠:

  • SOLID原則(特にSRP, OCP)への準拠を実現
  • 実務における保守性・拡張性の向上が報告されている

出典:

信頼度: 高


2.2 実際のユースケース(コンパイラ、ASTツリー処理など)

要点: Visitorパターンは以下のような実世界のシステムで広く活用されている。

ユースケース1: コンパイラと抽象構文木(AST)処理

詳細:

  • 問題: コンパイラでは、ソースコードを解析して抽象構文木(AST)を構築する。ASTに対して複数の操作(型チェック、コード生成、最適化、評価など)を実行する必要がある。
  • Visitorパターンの適用: 各操作をVisitorとして実装
    • TypeCheckVisitor: 型の整合性チェック
    • CodeGenVisitor: 中間コードやアセンブリの生成
    • OptimizationVisitor: 定数畳み込み、デッドコード削除
    • EvaluationVisitor: 式の評価

具体例(JavaによるAST処理):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
interface Expression {
    void accept(ExpressionVisitor visitor);
}

class Number implements Expression {
    int value;
    public void accept(ExpressionVisitor visitor) {
        visitor.visit(this);
    }
}

class Addition implements Expression {
    Expression left, right;
    public void accept(ExpressionVisitor visitor) {
        visitor.visit(this);
    }
}

interface ExpressionVisitor {
    void visit(Number number);
    void visit(Addition addition);
}

// 評価用Visitor
class EvaluationVisitor implements ExpressionVisitor {
    int result;
    public void visit(Number number) {
        result = number.value;
    }
    public void visit(Addition addition) {
        addition.left.accept(this);
        int leftResult = result;
        addition.right.accept(this);
        result = leftResult + result;
    }
}

実例:

  • Java Compiler(Eclipse JDT)
  • C++コンパイラフロントエンド
  • Crystal言語の構文解析
  • LLVM IR の変換パス

出典:

信頼度: 高(学術的文献および実装例が豊富)


ユースケース2: ドキュメント処理システム

詳細:

  • 問題: ドキュメントは複数の要素(段落、テーブル、画像、リストなど)から構成される。各要素に対して複数の操作(レンダリング、スペルチェック、PDF出力、統計情報取得)が必要。
  • Visitorパターンの適用:
    • RenderVisitor: HTML/PDF形式でレンダリング
    • SpellCheckVisitor: スペルチェック実行
    • StatisticsVisitor: 文字数・単語数などの統計情報収集

実例:

  • Microsoft WordやGoogle Docsのような文書処理システム
  • LaTeX/Markdown処理エンジン

ユースケース3: ファイルシステム操作

詳細:

  • 問題: ファイルシステムはファイルとディレクトリから成る階層構造。サイズ計算、権限チェック、バックアップなどの操作を追加したい。
  • Visitorパターンの適用:
    • SizeCalculatorVisitor: ファイル・ディレクトリのサイズ計算
    • PermissionCheckerVisitor: アクセス権限の確認
    • BackupVisitor: バックアップ対象のファイル収集

Perlでの実装例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package File;
sub new {
    my ($class, $name, $size) = @_;
    bless { name => $name, size => $size }, $class;
}
sub accept {
    my ($self, $visitor) = @_;
    $visitor->visit_file($self);
}

package Directory;
sub new {
    my ($class, $name, $items) = @_;
    bless { name => $name, items => $items }, $class;
}
sub accept {
    my ($self, $visitor) = @_;
    $visitor->visit_directory($self);
}

package SizeVisitor;
sub new { bless { total_size => 0 }, shift; }
sub visit_file {
    my ($self, $file) = @_;
    $self->{total_size} += $file->{size};
}
sub visit_directory {
    my ($self, $dir) = @_;
    for my $item (@{$dir->{items}}) {
        $item->accept($self);
    }
}
sub get_total_size { shift->{total_size} }

ユースケース4: オンラインショッピング・金融システム

詳細:

  • 問題: ショッピングカートには異なる種類の商品(Book、Electronics、Clothingなど)が混在。価格計算、割引適用、配送料計算、税金計算などの操作が必要。
  • Visitorパターンの適用:
    • PriceCalculatorVisitor: 合計金額計算
    • DiscountVisitor: 割引適用
    • ShippingCostVisitor: 配送料計算
    • TaxCalculatorVisitor: 税金計算

実例:

  • ECサイトのカート処理
  • 金融システムの取引処理(手数料計算、レポート生成など)

出典:

信頼度: 高


2.3 適用すべき場面と適用すべきでない場面

適用すべき場面(When to Use)

要点:

  1. 要素の型が安定しており、操作が頻繁に追加される場合

    • オブジェクト構造(要素の種類)が固定的
    • 新しい操作を継続的に追加する必要がある
    • 例:コンパイラ(ASTの構造は安定、新しい最適化パスを追加)
  2. 複数の無関係な操作を一元管理したい場合

    • 各操作のロジックを1箇所(Visitorクラス)に集約
    • オブジェクト構造の各要素に操作ロジックを分散させたくない
  3. if/switch文による型判定を排除したい場合

    • ダブルディスパッチにより、型判定なしで適切なメソッドを呼び出せる
  4. 公開API設計で、ユーザーが振る舞いを拡張できるようにしたい場合

    • コアコードを変更せずに新しい操作を追加可能
    • プラグイン機構として活用

具体的な判断基準:

  • 「新しい要素型を追加する頻度」 < 「新しい操作を追加する頻度」の場合

根拠:

  • GoFパターンのガイドライン
  • 実務での成功事例が蓄積

出典:

信頼度: 高


適用すべきでない場面(When NOT to Use)

要点:

  1. 要素の型が頻繁に変更される場合

    • 新しい要素型を追加するたびに、すべてのVisitorクラスを修正する必要がある
    • 保守コストが非常に高くなる
    • 例:頻繁に新しい商品カテゴリが追加されるECシステム
  2. 操作が単純で、型ごとの処理分岐が少ない場合

    • シンプルなポリモーフィズム(仮想関数)で十分
    • Visitorパターンは過剰設計(オーバーエンジニアリング)になる
  3. パターンマッチングや代数的データ型を持つ言語の場合

    • Haskell、Scala、OCaml、Rust、最新のC#/F#など
    • これらの言語ではパターンマッチングでより簡潔に記述可能
  4. 小規模システムで追加のレイヤーが負担になる場合

    • Visitor、ConcreteVisitor、Element、ConcreteElement、ObjectStructureと多層化
    • システムの複雑性がメリットを上回る
  5. カプセル化を厳密に守りたい場合

    • Visitorは要素の内部状態にアクセスすることが多い
    • 要素とVisitorの結合度が高くなる可能性

具体的な判断基準:

  • 「新しい要素型を追加する頻度」 > 「新しい操作を追加する頻度」の場合は避ける

根拠:

  • パターン誤用の事例研究
  • 関数型言語の代替手段との比較研究

出典:

信頼度: 高


3. 利点と欠点

3.1 メリット(Advantages)

要点:

メリット説明具体例
1. 操作の追加が容易既存の要素クラスを変更せず、新しいConcreteVisitorを追加するだけで新機能を実装可能コンパイラに新しい最適化パス(Visitor)を追加
2. 関心の分離(Separation of Concerns)操作ロジックがVisitorに集約され、データ構造(Element)と処理(Visitor)が分離ファイル処理とサイズ計算ロジックの分離
3. 開放閉鎖原則(OCP)への準拠拡張には開いており(新Visitor追加)、修正には閉じている(既存Element変更不要)既存コードに影響を与えず拡張可能
4. 関連操作のグループ化同じ目的の処理が1つのVisitorクラスにまとまるレポート生成ロジックがReportVisitorに集約
5. 型安全性の向上ダブルディスパッチにより、if/instanceofなどの型チェックが不要コンパイル時の型エラー検出
6. 複雑なアルゴリズムの一元管理複数のクラスにまたがる処理を1箇所で管理ASTの複雑な変換ロジックを1つのVisitorに集約

詳細説明:

1. 操作の追加が容易(開放閉鎖原則)

1
2
3
4
5
6
7
# 新しい操作を追加する場合
package NewOperationVisitor;
sub visit_file { ... }
sub visit_directory { ... }

# 既存の要素クラス(File, Directory)は一切変更不要
# → OCPに準拠した設計

2. 関心の分離

1
2
3
4
5
6
7
8
9
データ構造(Element)側:
  - データの保持
  - Visitorの受け入れ(accept)

処理ロジック(Visitor)側:
  - 具体的な操作の実装
  - 各要素型ごとの処理分岐

→ 責任が明確に分離され、保守性が向上

根拠:

  • GoFパターンの設計原則
  • 実務での適用事例と成功報告

出典:

信頼度: 高


3.2 デメリット(Disadvantages)

要点:

デメリット説明影響
1. 要素の追加が困難新しい要素型を追加すると、すべてのVisitorに新しいvisitメソッドを追加する必要がある保守コストの増大
2. カプセル化の破壊Visitorが要素の内部状態にアクセスする必要がある場合、privateフィールドの公開が必要になることがある情報隠蔽の原則に反する
3. 双方向依存ElementとVisitorが相互に依存する関係になる循環依存の発生
4. 複雑性の増加Visitor、ConcreteVisitor、Element、ConcreteElementと多層化小規模システムでは過剰設計
5. ダブルディスパッチの理解が必要accept → visitの2段階呼び出しを理解する必要がある学習コストの増加

詳細説明:

1. 要素の追加が困難

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 新しい要素型SymbolicLinkを追加する場合
package SymbolicLink;
sub accept {
    my ($self, $visitor) = @_;
    $visitor->visit_symbolic_link($self);  # 新メソッド
}

# 問題:すべての既存Visitorに修正が必要
package SizeVisitor;
sub visit_symbolic_link { ... }  # 追加必要

package PermissionVisitor;
sub visit_symbolic_link { ... }  # 追加必要

package BackupVisitor;
sub visit_symbolic_link { ... }  # 追加必要

# → Visitorが多数ある場合、大規模な修正が必要

2. カプセル化の破壊

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package File;
sub new {
    my ($class, $name, $size, $permissions) = @_;
    bless {
        name => $name,
        _size => $size,          # private想定
        _permissions => $permissions,  # private想定
    }, $class;
}

# Visitorがアクセスするために、getterを公開
sub get_size { shift->{_size} }
sub get_permissions { shift->{_permissions} }

# → カプセル化が弱まる

根拠:

  • パターン適用時の実務経験レポート
  • アンチパターンとしての報告事例

出典:

信頼度: 高


3.3 トレードオフの考え方

要点: Visitorパターンの採用は、以下のトレードオフを考慮する必要がある。

トレードオフの軸:

1
2
3
4
5
6
7
操作の追加頻度 vs 要素の追加頻度

高 ←─ 操作追加頻度 ─→ 低
 |                      |
Visitor有利         Visitor不利
 |                      |
低 ←─ 要素追加頻度 ─→ 高

判断基準表:

状況操作追加頻度要素追加頻度Visitor採用理由
コンパイラAST処理高(頻繁)低(安定)◎ 推奨新しい最適化パスを継続的に追加
ECサイトの商品管理低(稀)高(頻繁)× 非推奨新商品カテゴリが頻繁に追加
ドキュメント処理中(定期)低(安定)○ 採用可レンダリング、統計、変換など操作追加
小規模ツール低(稀)低(安定)△ 不要シンプルなポリモーフィズムで十分

設計判断のフローチャート:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
開始
要素の種類は安定しているか?
 ├─ No → Visitorパターンは不適切
 ↓ Yes
新しい操作を頻繁に追加するか?
 ├─ No → 他のパターンを検討
 ↓ Yes
要素数とVisitor数は管理可能か?
 ├─ No → 複雑性が高すぎる
 ↓ Yes
Visitorパターンを採用

根拠:

  • パターン選択の意思決定プロセス研究
  • 実務における成功・失敗事例の分析

出典:

信頼度: 高


4. サンプルコード実装の方向性

4.1 Perlでの実装例を想定した場合の考慮点

要点: PerlでVisitorパターンを実装する際は、以下の点を考慮する必要がある。

1. Perlの動的型システムの活用

考慮点:

  • Perlには厳密なインターフェース定義機構がないため、メソッド命名規則で代替
  • Moo/Mooseを使用したオブジェクト指向設計が推奨
  • Roleベースの設計(Moo::Role / Moose::Role)でインターフェースを定義

実装例(Moo::Role使用):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# Visitor ロールの定義
package Visitor::Role;
use Moo::Role;

requires 'visit_file';
requires 'visit_directory';

1;

# Element ロールの定義
package Element::Role;
use Moo::Role;

requires 'accept';

1;

# 具体的な要素の実装
package File;
use Moo;
with 'Element::Role';

has name => (is => 'ro', required => 1);
has size => (is => 'ro', required => 1);

sub accept {
    my ($self, $visitor) = @_;
    $visitor->visit_file($self);
}

1;

package Directory;
use Moo;
with 'Element::Role';

has name => (is => 'ro', required => 1);
has items => (is => 'ro', default => sub { [] });

sub accept {
    my ($self, $visitor) = @_;
    $visitor->visit_directory($self);
}

1;

# 具体的なVisitorの実装
package SizeVisitor;
use Moo;
with 'Visitor::Role';

has total_size => (is => 'rw', default => 0);

sub visit_file {
    my ($self, $file) = @_;
    $self->total_size($self->total_size + $file->size);
}

sub visit_directory {
    my ($self, $dir) = @_;
    for my $item (@{$dir->items}) {
        $item->accept($self);
    }
}

1;

メリット:

  • Moo::Roleのrequiresによりインターフェース制約を強制
  • 実行時にメソッドの存在をチェック
  • 型制約(isa)でオブジェクトの型を検証可能

2. ダブルディスパッチの実装

考慮点:

  • Perlでは明示的なメソッドオーバーロードがないため、メソッド名で区別
  • visit_file, visit_directoryなど、要素型ごとに異なるメソッド名を使用

実装パターン:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# Element側
sub accept {
    my ($self, $visitor) = @_;
    # 自分の型に応じた適切なvisitメソッドを呼び出す
    $visitor->visit_file($self);  # Fileの場合
    # または
    # $visitor->visit_directory($self);  # Directoryの場合
}

# Visitor側
sub visit_file {
    my ($self, $file) = @_;
    # File固有の処理
}

sub visit_directory {
    my ($self, $directory) = @_;
    # Directory固有の処理
}

3. エラーハンドリング

考慮点:

  • 必須メソッドの未実装チェック(Moo::Roleのrequiresで自動化)
  • 型の不一致エラー(isaチェック)
  • 循環参照の防止(特にツリー構造の場合)

実装例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
sub visit_directory {
    my ($self, $dir) = @_;
    
    # 型チェック
    die "Expected Directory object" unless $dir->isa('Directory');
    
    # 循環参照チェック(簡易版)
    my %visited;
    
    for my $item (@{$dir->items}) {
        next if $visited{$item}++;  # 訪問済みならスキップ
        $item->accept($self);
    }
}

4. テスタビリティ

考慮点:

  • Test2/Test::Moreを使用した単体テスト
  • Visitorのモック/スタブ化が容易
  • 各Visitorを独立してテスト可能

テスト例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
use Test2::V0;
use File;
use Directory;
use SizeVisitor;

subtest 'SizeVisitor calculates total size' => sub {
    my $file1 = File->new(name => 'file1.txt', size => 100);
    my $file2 = File->new(name => 'file2.txt', size => 200);
    my $dir = Directory->new(
        name => 'mydir',
        items => [$file1, $file2]
    );
    
    my $visitor = SizeVisitor->new;
    $dir->accept($visitor);
    
    is $visitor->total_size, 300, 'Total size is correct';
};

done_testing;

根拠:

  • Perlのベストプラクティス
  • Moo/Mooseコミュニティのガイドライン

出典:

信頼度: 高


4.2 他の言語での実装例(参考)

要点: 参考のため、他の主要言語での実装例を示す。

Java実装例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// Element interface
interface Element {
    void accept(Visitor visitor);
}

// Concrete Elements
class File implements Element {
    private String name;
    private int size;
    
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    
    public int getSize() { return size; }
}

class Directory implements Element {
    private String name;
    private List<Element> items;
    
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    
    public List<Element> getItems() { return items; }
}

// Visitor interface
interface Visitor {
    void visit(File file);
    void visit(Directory directory);
}

// Concrete Visitor
class SizeVisitor implements Visitor {
    private int totalSize = 0;
    
    public void visit(File file) {
        totalSize += file.getSize();
    }
    
    public void visit(Directory directory) {
        for (Element item : directory.getItems()) {
            item.accept(this);
        }
    }
    
    public int getTotalSize() { return totalSize; }
}

Python実装例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
from abc import ABC, abstractmethod

# Element interface
class Element(ABC):
    @abstractmethod
    def accept(self, visitor):
        pass

# Concrete Elements
class File(Element):
    def __init__(self, name, size):
        self.name = name
        self.size = size
    
    def accept(self, visitor):
        visitor.visit_file(self)

class Directory(Element):
    def __init__(self, name, items=None):
        self.name = name
        self.items = items or []
    
    def accept(self, visitor):
        visitor.visit_directory(self)

# Visitor interface
class Visitor(ABC):
    @abstractmethod
    def visit_file(self, file):
        pass
    
    @abstractmethod
    def visit_directory(self, directory):
        pass

# Concrete Visitor
class SizeVisitor(Visitor):
    def __init__(self):
        self.total_size = 0
    
    def visit_file(self, file):
        self.total_size += file.size
    
    def visit_directory(self, directory):
        for item in directory.items:
            item.accept(self)

出典:

信頼度: 高


5. 内部リンク候補の調査

5.1 /content/post配下で関連する記事の検索結果

検索実施日: 2025年12月31日

検索キーワード:

  • design-pattern, design pattern, デザインパターン
  • object-oriented, オブジェクト指向
  • perl, moo, moose
  • strategy

検索結果(関連度の高い記事):

ファイルパスタイトル(推定)内部リンク関連度関連理由
/content/post/2025/12/30/164012.md第12回-これがデザインパターンだ! - Mooを使ってディスパッチャーを作ってみよう/2025/12/30/164012/Strategyパターン(Visitorと同じく振る舞いパターン)、Moo使用、デザインパターン入門
/content/post/2025/12/30/164011.md第11回-完成!ディスパッチャー - Mooを使ってディスパッチャーを作ってみよう/2025/12/30/164011/ディスパッチャー実装(Visitorと類似の構造)、Moo使用
/content/post/2025/12/25/234500.mdJSON-RPC Request/Response実装 - 複合値オブジェクト設計【Perl×TDD】/2025/12/25/234500/Factoryパターン言及、Moo使用、Perlでのオブジェクト指向設計
/content/post/2025/12/21/234500.mdJSON-RPC 2.0仕様から学ぶ値オブジェクト設計【Perl実装】仕様書の読み方とTDD/2025/12/21/234500/値オブジェクト設計、Perl実装、設計パターンの実践
/content/post/2021/10/31/191008.md第1回-Mooで覚えるオブジェクト指向プログラミング/2021/10/31/191008/Mooによるオブジェクト指向入門、シリーズの基礎
/content/post/2016/02/21/150920.mdよなべPerl で Moo について喋ってきました/2016/02/21/150920/Moo解説、Perl OOP
/content/post/2009/02/14/105950.mdMoose::Roleが興味深い/2009/02/14/105950/Roleパターン(Visitorでも使用可能)

5.2 推奨内部リンク構成

Visitorパターン記事からのリンク先:

  1. デザインパターン関連記事(必須)

    • /2025/12/30/164012/ - Strategyパターン解説(同じ振る舞いパターン)
    • Strategyパターンとの比較・対比で相互理解を深める
  2. Moo/Mooseによるオブジェクト指向基礎(推奨)

    • /2021/10/31/191008/ - Mooの基本(前提知識)
    • /2016/02/21/150920/ - Moo詳細解説
    • /2009/02/14/105950/ - Moose::Role(Visitorでも活用可能)
  3. 実践的な設計例(関連)

    • /2025/12/25/234500/ - Factoryパターン(生成パターン)
    • /2025/12/21/234500/ - 値オブジェクト設計

リンクテキスト例:

1
2
3
Visitorパターンは「振る舞いに関するパターン」の一つです。同じカテゴリーの[Strategyパターン](/2025/12/30/164012/)と比較すると理解が深まります。

Perlでの実装には[Mooによるオブジェクト指向プログラミング](/2021/10/31/191008/)の知識が役立ちます。特に[Moo::Role](/2009/02/14/105950/)を活用することで、よりクリーンな設計が可能です。

6. 参考文献・出典一覧

6.1 公式書籍・定番文献

書籍名著者出版年ISBN備考
Design Patterns: Elements of Reusable Object-Oriented SoftwareErich Gamma, Richard Helm, Ralph Johnson, John Vlissides (GoF)1994978-0201633610Visitorパターンの原典
Head First Design Patterns (2nd Edition)Eric Freeman, Elisabeth Robson2020978-1492078005初心者向け、視覚的解説
Dive Into Design PatternsAlexander Shvets--Refactoring Guru著者

6.2 信頼性の高いWebリソース

英語リソース:

リソース名URL特徴信頼度
Wikipedia: Visitor patternhttps://en.wikipedia.org/wiki/Visitor_pattern包括的な解説、歴史的背景
GeeksforGeeks: Visitor design patternhttps://www.geeksforgeeks.org/system-design/visitor-design-pattern/詳細な解説、コード例豊富
SourceMaking: Visitor Design Patternhttps://sourcemaking.com/design_patterns/visitor実装例、ダイアグラム
TheLinuxCode: Visitor Design Pattern Guidehttps://thelinuxcode.com/visitor-design-pattern-a-comprehensive-guide/実践的ガイド
ScholarHat: Visitor Design Patternhttps://www.scholarhat.com/tutorial/designpatterns/visitor-design-patternチュートリアル形式

日本語リソース:

リソース名URL特徴信頼度
Refactoring Guru: Visitor(日本語版)https://refactoring.guru/ja/design-patterns/visitor視覚的解説、多言語コード例
ソフトウェア開発日記: Visitorパターンとはhttps://lightgauge.net/journal/object-oriented/visitor-pattern/日本語での丁寧な解説
Zenn: デザインパターンを学ぶ #19 ビジターhttps://zenn.dev/tajicode/articles/ab5d11802df265実装例、図解
MYNT Blog: GoF #23 Visitorhttps://blog.myntinc.com/2025/11/gof23-23-visitor.htmlGoFパターン全体との関連
Qiita: Visitorパターンhttps://qiita.com/Yankaji777/items/ac5a006057c880d3bb26Javaコード例
Tamotech: 「Visitor」パターンとは?https://tamotech.blog/2025/05/19/visitor/わかりやすい日本語解説
Programming TIPS: Java Visitor パターンhttps://programming-tips.jp/archives/a2/52/index.html図解、実装例
Oracle: Visitorデザイン・パターン 徹底解説(PDF)https://www.oracle.com/webfolder/technetwork/jp/javamagazine/Java-SO18-VisitorDesignPattern-ja.pdf詳細な解説、公式ドキュメント

6.3 コンパイラ・AST関連の専門リソース

リソース名URL特徴信頼度
Moments Log: Visitor Pattern in Compiler Designhttps://www.momentslog.com/development/design-pattern/visitor-pattern-in-compiler-design-abstract-syntax-trees-2AST処理の実例
Compiling to Assembly: Visitor Patternhttps://keleshev.com/compiling-to-assembly-from-scratch/12-visitor-patternコンパイラ実装での実践
Pat Shaughnessy: Visiting an ASThttps://patshaughnessy.net/2022/1/22/visiting-an-abstract-syntax-treeAST走査の解説
CS Colorado: AST Visitor patterns(PDF)https://www.cs.colostate.edu/~cs453/yr2014/Slides/10-AST-visitor.ppt.pdf学術的資料
Software Patterns Lexicon: Visitor Pattern Use Caseshttps://softwarepatternslexicon.com/java/behavioral-patterns/visitor-pattern/use-cases-and-examples/Java実装例

6.4 実装例・GitHubリポジトリ

リポジトリ言語URL説明スター数(参考)
iluwatar/java-design-patternsJavahttps://github.com/iluwatar/java-design-patterns包括的なパターン集90k+
faif/python-patternsPythonhttps://github.com/faif/python-patternsPython実装例40k+
johnbeard/visitor-double-dispatchC++https://github.com/johnbeard/visitor-double-dispatchダブルディスパッチの解説-

6.5 その他の参考資料

リソース名URL特徴信頼度
Visual Paradigm: Visitor Pattern Tutorialhttps://tutorials.visual-paradigm.com/visitor-pattern-tutorial/UML図、ステップバイステップ
GoF Pattern: Visitor Patternhttps://www.gofpattern.com/behavioral/patterns/visitor-pattern.phpパターン詳細
Generalist Programmer: What is Visitor Patternhttps://generalistprogrammer.com/glossary/visitor-pattern用語解説
Code Study: Confused About Visitor?https://www.codestudy.net/blog/confused-about-the-visitor-design-pattern/よくある疑問への回答
Coder Scratchpad: Python Visitor Patternhttps://coderscratchpad.com/python-design-patterns-visitor-pattern/Python実装

7. 調査結果のサマリーと結論

7.1 主要な発見事項

  1. Visitorパターンの本質

    • オブジェクト構造(要素)と操作(ビジター)の分離を実現
    • ダブルディスパッチによる柔軟な処理分岐
    • 開放閉鎖原則への準拠を可能にする
  2. 適用領域の明確化

    • コンパイラ(AST処理)が最も典型的で成功している適用例
    • ドキュメント処理、ファイルシステム、金融システムでも有効
    • 要素型が安定し、操作が頻繁に追加される場合に最適
  3. トレードオフの重要性

    • 要素追加が困難になるというデメリットは無視できない
    • パターンの適用判断には「変更の方向性」の分析が必須
    • 小規模システムや頻繁に要素が変わる場合は避けるべき
  4. Perlでの実装可能性

    • Moo/Mooseを活用することで、型安全性を高められる
    • Moo::Roleによるインターフェース定義が有効
    • 動的型付け言語でも十分に実装可能
  5. モダンな代替手段

    • パターンマッチングを持つ言語(Rust, Scala, Haskell等)では、Visitorパターンは不要な場合がある
    • 関数型プログラミングのアプローチも検討価値あり

7.2 不明点・今後の調査が必要な領域

  • Visitorパターンと他のパターンの組み合わせ(Composite + Visitor、Iterator + Visitorなど)
  • 大規模システムでの実装事例とベストプラクティス
  • パフォーマンス面での影響(特に深いツリー構造での再帰処理)
  • 並行処理・非同期処理とVisitorパターンの組み合わせ
  • Perlにおける既存CPANモジュールでのVisitorパターン活用例

7.3 記事執筆への推奨事項

記事構成の提案:

  1. 導入部(読者の課題提示)

    • 「クラスに処理を詰め込みすぎて保守が困難」という問題提起
    • if/switch文の乱立による可読性低下
  2. パターンの解説(理解促進)

    • 構造の図解(UML、シーケンス図)
    • ダブルディスパッチの仕組み
    • 具体例(ファイルシステムなど身近な題材)
  3. 実装例(実践)

    • Perlでの段階的な実装
    • Moo/Moo::Roleの活用
    • テストコードの提示
  4. メリット・デメリット(判断基準)

    • いつ使うべきか、いつ避けるべきか
    • トレードオフの明確化
  5. 実例紹介(信頼性向上)

    • コンパイラでの活用例
    • 実務での適用シーン
  6. 内部リンク(サイト内回遊)

    • Strategyパターンとの比較
    • Mooシリーズへのリンク
  7. まとめ(行動喚起)

    • パターン適用のチェックリスト
    • 次のステップ(他のパターン学習)

差別化ポイント:

  • Perl実装に特化(他サイトはJava/Python中心)
  • Moo/Moo::Roleの活用(Perlのモダンな実装)
  • トレードオフを明確化(過剰な推奨を避ける)
  • 実務での判断基準を提示(いつ使うべきか/いつ避けるべきか)

調査完了日: 2025年12月31日
調査実施者: 調査・情報収集専門家
信頼度総合評価: 高(公式文献、学術資料、実装事例が豊富)

comments powered by Disqus
Hugo で構築されています。
テーマ StackJimmy によって設計されています。