【実務・中級編】【上級】マスターシェイプの依存関係を解析する再帰的アルゴリズム – Visio VBA解析バイブル

スポンサーリンク
スポンサーリンク

階層の深淵を穿つ:Visio VBAにおけるマスターシェイプ解析の再帰的アルゴリズム

現場のエンジニアが陥る最大の罠、それは「Visioのオブジェクトモデルを、単なる平坦なリストとして捉えてしまうこと」だ。

業務自動化の要件において、マスターシェイプは単なる「図形」ではない。それは、複数のパーツ、属性、そして複雑な論理構造を内包した「コンポーネント」である。特に、大規模なプロセス図や設備構成図を扱う場合、マスターシェイプはグループの中にさらにグループが含まれる、多重階層のツリー構造を形成している。

もし君が、`For Each` ループを単層で回して特定のパーツを探そうとしているなら、そのコードは設計段階で既に「失敗」している。深層に隠された重要なデータを見落とし、結果として不完全な自動化ツールを生み出すことになるからだ。

本稿では、複雑な階層構造を完璧に走査し、目的のパーツを確実に、かつ高パフォーマンスに抽出するための「再帰的探索アルゴリズム」の極意を伝授する。

1. なぜ「再帰」が必要なのか:オブジェクトのライフサイクルと構造的理解

Visioの `Shape` オブジェクトは、`visTypeGroup`(グループ型)である場合、その内部に `Shapes` コレクションを持つ。この構造は、木構造(Tree Structure)そのものである。

非効率な設計(アンチパターン)

多くの開発者は、以下のようなアプローチを取ろうとする。

  • 「グループの1層目だけをループで回す」
  • 「グループの深さが決まっている前提で、ネストしたループをハードコーディングする」

これらは、設計変更(マスターシェイプの構造変更)に対して極めて脆弱だ。グループの階層が一つ深くなっただけで、君のシステムは沈黙する。

プロフェッショナルの設計

我々が採用すべきは、「自身がグループであれば、その中身に対して自分自身を再度呼び出す」という、深さ優先探索(Depth-First Search: DFS)に基づいた再帰的アプローチだ。これにより、階層の深さが1層であろうと100層であろうと、アルゴリズムの論理は変わらず、堅牢性を維持できる。

2. 堅牢な実装のためのアーキテクチャ設計

実装に入る前に、エンジニアとして以下の3点を設計指針に組み込んでおく必要がある。

1. 副作用の分離: 探索ロジック(探すこと)と、抽出後の操作(書き換えること)を分離せよ。探索中に直接プロパティを書き換えると、再帰の途中でオブジェクトの整合性が崩れるリスクがある。
2. メモリとCOMのオーバーヘッド: Visioのオブジェクトモデルへのアクセスは、COM(Component Object Model)を介するため、非常にコストが高い。不要な `Shape` オブジェクトへの参照保持を避け、必要なデータ(TagやShapeDataの値)を即座に取得する設計にせよ。
3. エラーハンドリングの局所化: 再帰呼び出しの深層でエラーが発生した場合、スタック全体を崩壊させないよう、呼び出し元に制御を戻す仕組みが必要だ。

3. プロダクション・コード:再帰的シェイプ・アナライザー

以下に、特定の「Tag」を持つサブシェイプを、階層の深さに関わらずすべて抽出する、実戦投入レベルのコードを示す。

Option Explicit

''' <summary>
''' 階層構造を解析し、特定の条件を満たすシェイプを抽出するメインコントローラー
''' </summary>
Public Sub ExecuteShapeAnalysis()
    Dim targetTag As String
    Dim foundShapes As Collection
    Dim targetPage As Visio.Page
    Dim shp As Visio.Shape
    
    ' 設定値:実務では外部設定ファイルやDBから取得すべき項目
    targetTag = "TargetComponent"
    Set foundShapes = New Collection
    Set targetPage = ActivePage
    
    On Error GoTo ErrorHandler
    
    Debug.Print "--- 解析開始 ---"
    
    ' ページ上の全シェイプに対して再帰探索を開始
    For Each shp In targetPage.Shapes
        Call RecursiveSearch(shp, targetTag, foundShapes)
    Next shp
    
    ' 結果の出力(ここではデバッグ出力だが、実務ではDB書き出し等を行う)
    Debug.Print "抽出成功数: " & foundShapes.Count
    Dim item As Visio.Shape
    For Each item In foundShapes
        Debug.Print "Found: " & item.Name & " (ID: " & item.ID & ")"
    Next item
    
    Debug.Print "--- 解析完了 ---"
    Exit Sub

ErrorHandler:
    MsgBox "致命的なエラーが発生しました: " & Err.Description, vbCritical
End Sub

''' <summary>
''' 【核心】再帰的探索アルゴリズム
''' </summary>
''' <param name="currentShape">現在解析中のシェイプ</param>
''' <param name="targetTag">検索対象のTag名</param>
''' <param name="resultCollection">見つかったシェイプを格納するコレクション</param>
Private Sub RecursiveSearch(ByVal currentShape As Visio.Shape, _
                             ByVal targetTag As String, _
                             ByRef resultCollection As Collection)
    On Error Resume Next ' 個別のシェイプへのアクセスエラーを無視して続行
    
    ' 1. 条件判定:現在のシェイプが目的のTagを持っているか
    ' Shape.Tags は、設計者が定義したメタデータへのアクセス窓口
    If currentShape.Tags(targetTag) <> "" Then
        resultCollection.Add currentShape
    End If
    
    ' 2. 再帰ステップ:現在のシェイプがグループ型の場合、その子要素を走査
    ' visTypeGroup (2) を判定基準とする
    If currentShape.Type = visTypeGroup Then
        Dim subShape As Visio.Shape
        For Each subShape In currentShape.Shapes
            ' 自分自身を呼び出す(再帰)
            Call RecursiveSearch(subShape, targetTag, resultCollection)
        Next subShape
    End If
    
    On Error GoTo 0
End Sub

コードの技術的解説

  • `ByRef resultCollection`: 再帰の各階層で同じコレクション・インスタンスを共有することで、メモリ消費を抑えつつ、発見したオブジェクトを「一本の糸」で呼び出し元まで持ち帰っている。
  • `visTypeGroup` による分岐: ここがアルゴリズムの心臓部だ。グループであれば潜り、そうでなければ止まる。このシンプルな判断が、無限ループを防ぎつつ全階層を網羅する。
  • `On Error Resume Next` の戦略的利用: 複雑なマスターシェイプ内には、アクセス権限のない保護されたシェイプや、一時的なオブジェクトが含まれることがある。個別の探索失敗が全体のプロセスを止めることは、業務自動化において許されない。

4. 実務におけるデータベース・ファイル連携の注意点

このアルゴリズムで抽出したデータを、ExcelやSQL Serverなどの外部リソースと連携させる際、初心者は必ず「速度低下」という壁にぶつかる。

パフォーマンスを最大化する設計原則

1. 「1件ずつ書き込まない」:
再帰の中で `INSERT INTO SQL…` や `Range(“A1”).Value = …` を実行してはならない。これは、COMのオーバーヘッドとネットワーク遅延を、再帰の回数分だけ累積させる自殺行為だ。

  • 正解: 再帰では `Collection` にオブジェクト(または必要な値の配列)を溜める。再帰が終了した後、一括(Bulk)でデータベースへ書き込むか、Excelの配列へ一括代入せよ。

2. 「ID(Unique Identifier)の管理」:
Visioの `Shape.ID` はセッションごとに変わる可能性がある。マスターシェイプと外部データを紐付ける際は、Visio固有のIDではなく、シェイプの `ShapeData` に埋め込んだ「業務上のユニークキー(例:部品番号)」をキーとして使用せよ。

5. 結論

マスターシェイプの解析は、単なるループ処理ではない。それは、構造化されたデータの「深さ」を理解し、それを制御する数学的なアプローチだ。

再帰アルゴリズムをマスターすることは、Visio VBAにおける「作業員」から「アーキテクト」へと進化することを意味する。設計の堅牢性を追求し、オブジェクトの階層を恐れず、常に「データの一貫性」を意識せよ。それが、バグのない、真に価値のある業務自動化ツールを生む唯一の道である。

タイトルとURLをコピーしました