0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【デザインパターン】 ビジターパターン解説(Flutter / Android 実例付き)

0
Last updated at Posted at 2025-09-08

1. パターンの意図

ビジター(Visitor)パターン は、
データ構造を変えずに新しい操作を追加できるようにする デザインパターンです。

解決する問題

  • 既存のオブジェクト構造を変えずに、新しい処理を追加したい
  • if/else や型判定で処理を分けるのを避けたい
  • データ構造(要素)は安定しているが、操作は頻繁に追加・変更される場合

ポイント

  • Element(要素):訪問対象(データ構造の一部)
  • Visitor:操作を表すインターフェース
  • ConcreteVisitor:新しい処理を追加するクラス
  • Element は accept(visitor) を持ち、Visitor の visitXXX() を呼び出す(二重ディスパッチ)

2. UML 図


3. Flutter / Dart 実装例

Element

abstract class Element {
  void accept(Visitor visitor);
}

class ConcreteElementA implements Element {
  void operationA() => print("ElementA operation");

  @override
  void accept(Visitor visitor) => visitor.visitElementA(this);
}

class ConcreteElementB implements Element {
  void operationB() => print("ElementB operation");

  @override
  void accept(Visitor visitor) => visitor.visitElementB(this);
}

Visitor

abstract class Visitor {
  void visitElementA(ConcreteElementA element);
  void visitElementB(ConcreteElementB element);
}

class PrintVisitor implements Visitor {
  @override
  void visitElementA(ConcreteElementA element) {
    element.operationA();
    print("Visited ElementA");
  }

  @override
  void visitElementB(ConcreteElementB element) {
    element.operationB();
    print("Visited ElementB");
  }
}

利用例

void main() {
  List<Element> elements = [ConcreteElementA(), ConcreteElementB()];
  var visitor = PrintVisitor();

  for (var e in elements) {
    e.accept(visitor);
  }
}

出力:

ElementA operation
Visited ElementA
ElementB operation
Visited ElementB

4. Android / Kotlin 実装例

Element

interface Element {
    fun accept(visitor: Visitor)
}

class ConcreteElementA : Element {
    fun operationA() = println("ElementA operation")
    override fun accept(visitor: Visitor) = visitor.visitElementA(this)
}

class ConcreteElementB : Element {
    fun operationB() = println("ElementB operation")
    override fun accept(visitor: Visitor) = visitor.visitElementB(this)
}

Visitor

interface Visitor {
    fun visitElementA(element: ConcreteElementA)
    fun visitElementB(element: ConcreteElementB)
}

class PrintVisitor : Visitor {
    override fun visitElementA(element: ConcreteElementA) {
        element.operationA()
        println("Visited ElementA")
    }

    override fun visitElementB(element: ConcreteElementB) {
        element.operationB()
        println("Visited ElementB")
    }
}

利用例

fun main() {
    val elements: List<Element> = listOf(ConcreteElementA(), ConcreteElementB())
    val visitor = PrintVisitor()

    elements.forEach { it.accept(visitor) }
}

5. 実務ユースケース

Flutter

  • AST(抽象構文木)のノードを走査して、コード生成や最適化を行う
  • JSON データ構造に対して複数の処理(検証 / 出力 / 変換)を追加
  • Widget ツリーやカスタム DSL の評価

Android / Kotlin

  • コンパイラの AST 処理(Kotlin Compiler Plugin)
  • XML / JSON の構文解析・変換処理
  • ファイルシステムや UI コンポーネントツリーの走査

6. メリット / デメリット

メリット

  • 新しい処理を Visitor として追加できる(既存の Element を修正せずに拡張可能)
  • データ構造と処理を分離できる
  • OCP(開放閉鎖原則)に従いやすい

デメリット

  • Element の種類が増えると Visitor 側も修正が必要
  • 二重ディスパッチで仕組みがやや複雑
  • 小規模な処理ではオーバーエンジニアリング

まとめ

  • Visitor パターンは「データ構造を変えずに新しい処理を追加できる」
  • Flutter/Android では AST, JSON, ツリー構造の処理に有効
  • Strategy = 処理切替 / State = 状態切替 / Visitor = 処理追加 と整理するとわかりやすい

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?