Visitorパターン

📢 この記事は gemini-2.5-flash によって翻訳されました

Visitorパターン オブジェクトの振る舞いパターン

意図

あるオブジェクト構造内の各要素に作用する操作を表現するよ。要素のクラスを変更せずに、これらの要素に作用する新しい操作を定義することを可能にするね。

構造

Visitorパターン

ここでね:

  • Visitor (訪問者) は、このオブジェクト構造内の ConcreteElement の各クラスに対して、Visit 操作を宣言するんだ。この操作名と特別な識別子によって、その訪問者へのVisitリクエストを送信するクラスが特定できるから、訪問者は現在訪問されている要素の具体的なクラスを特定できるよ。これで、訪問者はその要素の特定のインターフェースを通じて直接アクセスできるね。
  • ConcreteVisitor (具体的な訪問者) は、Visitorが宣言した各操作を実装するよ。各操作はアルゴリズムの一部を実装し、そのアルゴリズムの断片は構造内のオブジェクトのクラスに対応する。ConcreteVisitorはこのアルゴリズムのコンテキストを提供し、その局所状態を保持する。この状態は、構造を巡回する過程で蓄積されることが多いね。
  • Element (要素) は、訪問者を引数とするAccept操作を定義する。
  • ConcreteElement (具体的な要素) は、訪問者を引数とするAccept操作を実装する。
  • ObjectStructure (オブジェクト構造) は、その要素を列挙できるよ。訪問者がその要素にアクセスできるように、高レベルのインターフェースを提供できる。コンポジットや、リストや順序なし集合のようなコレクションであってもいいんだ。

適用性

Visitorパターンは、こんなときに使えるよ:

  • あるオブジェクト構造が多くのクラスオブジェクトを含んでいて、それらが異なるインターフェースを持っていて、ユーザーがそれらの具体的なクラスに依存する操作を実行したいとき。
  • あるオブジェクト構造内のオブジェクトに対して、たくさん異なる、そして関連性のない操作を実行する必要があるのに、それらの操作がオブジェクトのクラスを「汚染」するのを避けたいとき。Visitorパターンを使うと、関連する操作を一つのクラスにまとめて定義できるよ。そのオブジェクト構造が多くのアプリケーションで共有されている場合、Visitorパターンを使えば、各アプリケーションが必要な操作だけを含むようにできるんだ。
  • オブジェクト構造を定義するクラスはほとんど変更されないけど、この構造上で新しい操作を頻繁に定義する必要があるとき。オブジェクト構造クラスを変更すると、すべての訪問者のインターフェースを再定義する必要があって、それは大きなコストがかかる可能性があるんだ。もしオブジェクト構造クラスが頻繁に変わるなら、たぶんそれらのクラスの中で操作を定義する方がいいかもしれないね。

例文 1

ある図書管理システムでは、書籍と論文の2種類の文献を管理しているんだ。今、すべての所蔵文献の総ページ数を集計するという要求があって、それをVisitorパターンで実装するよ。クラス図はこんな感じ:

Visitorパターン-例1

 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
67
68
69
70
71
72
interface LibraryVisitor{
    void visit(Book p_book);
    void visit(Article p_article);
    void printSum();
}

class LibrarySumPrintVisitor implements LibraryVisitor{ // 印刷総ページ数
    private int sum = 0;

    @Override
    public void visit(Book p_book) {
        sum = sum + p_book.getNumberOfPages();
    }

    @Override
    public void visit(Article p_article) {
        sum = sum + p_article.getNumberOfPages();
    }

    @Override
    public void printSum() {
        System.out.println("SUM = " + sum);
    }
}

interface LibraryItemInterface{
    void accept(LibraryVisitor visitor);
}

class Article implements LibraryItemInterface{
    private String m_title; // 論文名
    private String m_author; // 論文著者
    private int m_start_page;
    private int m_end_page;
    
    public Article(String p_author, String p_title, int p_start_page, int p_end_page){
        m_title = p_title;
        m_author = p_author;
        m_start_page = p_start_page;
        m_end_page = p_end_page;
    }
    
    public int getNumberOfPages(){
        return m_end_page - m_start_page;
    }

    @Override
    public void accept(LibraryVisitor visitor) {
        visitor.visit(this);
    }
}

class Book implements LibraryItemInterface{
    private String m_title; // 書名
    private String m_author; // 書著者
    private int m_pages; // ページ数

    public Book(String p_author, String p_title, int p_pages){
        m_title = p_title;
        m_author = p_author;
        m_pages = p_pages;
    }

    public int getNumberOfPages(){
        return m_pages;
    }

    @Override
    public void accept(LibraryVisitor visitor) {
        visitor.visit(this);
    }
}

例文 2

  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
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import java.util.ArrayList;
import java.util.List;

public class VisitorPattern {
    public static void main(String[] args) {
        PersonStructure personStructure = new PersonStructure();

        Visitor1 visitor1 = new Visitor1();
        System.out.println("For Visitor1");
        personStructure.Accept(visitor1);
        System.out.println("The sum of student age: " + visitor1.getStudentAgeSum());
        System.out.println("The sum of teacher age: " + visitor1.getTeacherAgeSum());

        Visitor2 visitor2 = new Visitor2();
        System.out.println("For Visitor2");
        personStructure.Accept(visitor2);
        System.out.println("Max score: " + visitor2.getMaxScore());
        System.out.println("Max work year: " + visitor2.getMaxWorkYear());

    }
}

interface Visitor{
    public void VisitS(Student student);
    public void VisitT(Teacher teacher);
}

class Visitor1 implements Visitor{// 学生と教師の年齢の合計をそれぞれ集計する
    private int studentAgeSum = 0;
    private int teacherAgeSum = 0;

    public int getStudentAgeSum() {
        return studentAgeSum;
    }

    public int getTeacherAgeSum() {
        return teacherAgeSum;
    }

    @Override
    public void VisitS(Student student){
        System.out.println("Visitor1: " + student.getName() + " Age: " + student.getAge());
        studentAgeSum += student.getAge();
    }
    public void VisitT(Teacher teacher){
        System.out.println("Visitor1: " + teacher.getName() + " Age: " + teacher.getAge());
        teacherAgeSum += teacher.getAge();
    }
}

class Visitor2 implements Visitor{ // 学生の最高成績と教師の最高勤続年数をそれぞれ求める
    private int maxScore = -1;
    private int maxWorkYear = -1;

    public int getMaxScore() {
        return maxScore;
    }

    public int getMaxWorkYear() {
        return maxWorkYear;
    }

    @Override
    public void VisitS(Student student){
        System.out.println("Visitor2: " + student.getName() + " Score: " + student.getScore());
        if(student.getScore() > maxScore) maxScore = student.getScore();
        // maxScore = Math.max(maxScore, student.getScore());
    }
    public void VisitT(Teacher teacher){
        System.out.println("Visitor2: " + teacher.getName() + " WorkYear: " + teacher.getWorkYear());
        if(teacher.getWorkYear() > maxWorkYear) maxWorkYear = teacher.getWorkYear();
        // maxWorkYear = Math.max(maxWorkYear, teacher.getWorkYear());
    }
}

class PersonStructure{
    private List<Person> personList = new ArrayList<>();

    public PersonStructure(){
        personList.add(new Student("Mike", 16, 99));
        personList.add(new Student("Jane", 15, 100));

        personList.add(new Teacher("Alice mana", 20, 1));
    }
    public void Accept(Visitor visitor){
        for(Person p : personList){
            p.Accept(visitor);
        }
    }
}

abstract class Person{
    private String name;
    private int age;

    public abstract void Accept(Visitor visitor);

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

class Student extends Person{
    private int score;

    public int getScore() {
        return score;
    }

    public Student(String name, int age, int score){
        this.setName(name);
        this.setAge(age);
        this.score = score;
    }
    @Override
    public void Accept(Visitor visitor){
        visitor.VisitS(this);
    }
}

class Teacher extends Person{
    private int workYear;

    public int getWorkYear() {
        return workYear;
    }

    public Teacher(String name, int age, int workYear){
        this.setName(name);
        this.setAge(age);
        this.workYear = workYear;
    }
    @Override
    public void Accept(Visitor visitor){
        visitor.VisitT(this);
    }
}

Visits Since 2025-02-28

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