【実務・中級編】【初心者】ステンシル内の特定マスターシェイプを「名前の一部」で曖昧検索してドロップする – Visio VBA解析バイブル

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

【Visio VBA極限講義】マスターシェイプの曖昧検索:現場で「使える」コードと「壊れる」コードの境界線

業務自動化の現場において、「特定のパーツを探して配置する」という動作は、一見すると単純な作業に見える。しかし、プロフェッショナルなエンジニアとして、この「単純な作業」を実装する際に、君はどのような設計思想を持つだろうか?

初心者は、単に `For Each` でループを回し、`If Name Like “*keyword*” Then` と書くだけで満足する。だが、それは「動くだけのコード」だ。実務におけるツールは、「想定外の入力」や「環境の変化」に耐えうる堅牢性(Robustness)がなければ、現場の信頼を失い、保守コストという名の負債を積み上げる。

本稿では、ステンシル内のマスターシェイプを名前の一部で曖昧検索し、ドキュメント上にドロップするロジックを題材に、「プロダクション品質」のコードとは何かを徹底的に解説する。

1. なぜ、単純なループ実装では不十分なのか

まず、多くの開発者が陥る「アンチパターン」を整理しておこう。

アンチパターン:場当たり的な実装

' 典型的な「動くだけ」の悪い例
Sub BadExample()
    Dim mst As Master
    For Each mst In Application.Stencils(1).Masters ' ステンシル番号の直指定は危険
        If mst.Name Like "*Server*" Then
            ActivePage.Drop mst, 1, 1
            Exit Sub
        End If
    Next
End Sub

このコードには、プロフェッショナルとして見過ごせない致命的な欠陥が3つある。

1. オブジェクト参照の脆弱性: `Stencils(1)` のようにインデックスを直指定するのは、ユーザーがステンシルを開閉した瞬間にクラッシュする原因となる。
2. 例外処理の欠如: 指定した名前のマスターが見つからなかった場合、あるいはステンシルが読み取り専用だった場合に、ユーザーに無慈悲なランタイムエラーを突きつける。
3. 検索ロジックの硬直性: 大文字・小文字の区別や、複数の候補が見つかった場合のハンドリングが考慮されていない。

2. 設計思想:堅牢な検索エンジンの要件

実務で通用する検索ロジックを設計する場合、以下の3点を設計指針(Design Principles)に据えるべきだ。

① 検索対象の明示化(Explicit Scope)

「すべてのステンシル」を探すのか、「現在開いている特定のステンシル」を探すのか。これを曖昧にしてはいけない。本稿では、「現在開いているすべてのステンシル」をスキャン対象とする、汎用性の高い設計を採用する。

② 失敗を前提としたエラーハンドリング

「見つからないこと」はエラーではない。それは「検索結果がゼロである」という正常な状態(Valid State)だ。プログラムを異常終了させるのではなく、呼び出し元に結果を適切に返す設計にする。

③ 結合度の低減(Decoupling)

「検索する機能」と「ドロップする機能」を分離せよ。検索ロジックが「ドロップ」という副作用(Side Effect)を直接持つと、ユニットテストが困難になり、再利用性が著しく低下する。

3. プロダクション・コード:マスターシェイプ検索エンジン

以下に、私が設計する「実務用テンプレート」を示す。このコードは、単なる検索ではなく、「検索結果のオブジェクトを返す」という責務に特化している。

''
' @module: ShapeSearchEngine
' @description: マスターシェイプの高度な検索および配置制御
''

Option Explicit

''' <summary>
''' ステンシル内から部分一致でマスターシェイプを検索する
''' </summary>
''' <param name="partialName">検索キーワード</param>
''' <returns>一致した最初のMasterオブジェクト。見つからない場合はNothing</returns>
Public Function FindMasterByPartialName(ByVal partialName As String) As Visio.Master
    Dim stenc As Visio.Stencil
    Dim mst As Visio.Master
    
    ' 入力値のバリデーション
    If Trim(partialName) = "" Then
        Debug.Print "Error: Search keyword is empty."
        Set FindMasterByPartialName = Nothing
        Exit Function
    End If

    On Error GoTo ErrorHandler

    ' 全ての開いているステンシルを走査
    ' ※Application.Stencilsは、現在開いているステンシルセットを管理している
    For Each stenc In Application.Stencils
        For Each mst In stenc.Masters
            ' InStr関数を用い、大文字小文字を区別せずに検索 (vbTextCompare)
            ' Like演算子よりも、意図的な比較制御が容易である
            If InStr(1, mst.Name, partialName, vbTextCompare) > 0 Then
                Set FindMasterByPartialName = mst
                Exit Function ' 最初に見つかったものを返す(設計方針により変更可)
            End If
        Next mst
    Next stenc

    ' 見つからなかった場合
    Set FindMasterByPartialName = Nothing
    Exit Function

ErrorHandler:
    ' ログ出力(実務ではファイルやDBへ記録すべき)
    Debug.Print "Critical Error in FindMasterByPartialName: " & Err.Description
    Set FindMasterByPartialName = Nothing
End Function

''' <summary>
''' 検索したマスターを現在のページに配置する
''' </summary>
''' <param name="keyword">検索キーワード</param>
''' <param name="x">配置X座標</param>
''' <param name="y">配置Y座標</param>
Public Sub DropMasterByKeyword(ByVal keyword As String, ByVal x As Double, ByVal y As Double)
    Dim targetMaster As Visio.Master
    Dim droppedShape As Visio.Shape
    
    ' 1. 検索ロジックの呼び出し(責務の分離)
    Set targetMaster = FindMasterByPartialName(keyword)
    
    ' 2. 結果の判定
    If targetMaster Is Nothing Then
        MsgBox "キーワード「" & keyword & "」に一致するマスターは見つかりませんでした。", vbExclamation, "検索失敗"
        Exit Sub
    End If
    
    ' 3. 配置処理
    On Error GoTo DropError
    Set droppedShape = ActivePage.Drop(targetMaster, x, y)
    
    ' 配置成功時のフィードバック(必要に応じて)
    Debug.Print "Successfully dropped: " & droppedShape.Name
    Exit Sub

DropError:
    MsgBox "シェイプの配置に失敗しました。ページの状態を確認してください。", vbCritical, "配置エラー"
End Sub

4. アーキテクトの視点:さらに高みを目指すために

このコードは、一般的な業務ツールとしては十分な品質を備えている。しかし、大規模なエンタープライズ・システムへとスケールさせる場合、君はさらに以下の検討を行う必要がある。

データベース(DB)との連携

マスターシェイプの名前は、ユーザーの誤操作やステンシルの更新によって容易に変更される。
「名前で検索する」というロジックを、「DB上のID(管理番号)とマスターの隠しデータ(Custom Properties)を紐付ける」方式に昇華させよ。

  • マスターに `ID_Property` というプロパティを持たせる。
  • VBAはDBから「キーワード $\to$ ID」を取得し、そのIDを持つマスターを検索する。

これにより、名前が変わっても自動化ツールが壊れない、極めて堅牢なシステムが構築できる。

パフォーマンスの最適化

数千個のマスターを持つ巨大なステンシルを扱う場合、`For Each` のループは無視できないコストになる。
もし検索頻度が高いのであれば、アプリケーション起動時にマスターの「名前とオブジェクトへの参照」を Dictionaryオブジェクト(連想配列)にキャッシュしておく設計を検討せよ。計算量は $O(n)$ から $O(1)$ へと劇的に改善される。

結論

「動くコード」を書くのは誰にでもできる。しかし、「保守でき、拡張でき、壊れにくいコード」を書ける者こそが、真のエンジニアだ。

今回紹介した `FindMasterByPartialName` のように、「検索」という単一の責務に集中させ、エラーを制御し、呼び出し元に結果を明示的に返す。 この当たり前の積み重ねが、君の作成するツールの価値を決定づける。

さあ、コードを書き直せ。美しく、そして強靭な設計へと。

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