Visio VBAの「スパゲッティ」を撲滅せよ:Shapeを抽象化するクラス設計の極意
多くのエンジニアがVisio VBAのプロジェクトで陥る罠がある。それは、`ActivePage.Shapes(i).CellsU(“Prop.DeviceID”).Formula = “…”` といった呪文のようなコードを、モジュール内に散乱させることだ。
数ヶ月後、仕様変更が突きつけられたとき、あなたは数千行の中から特定のプロパティを探し出し、修正し、そして新しいバグを生む。「VisioのShapeオブジェクトをそのまま触るな」――これが、大規模自動化を成功させる唯一の防波堤だ。
今回は、標準のShapeを「業務ドメイン」に応じたクラスにラップし、堅牢でメンテナンス可能なアーキテクチャを構築する極意を伝授する。
—
1. なぜ「生」のShape操作は破滅を招くのか
VisioのAPIは強力だが、あまりに「プリミティブ」すぎる。
- 型安全性の欠如: `Shape`オブジェクトは、線なのか、四角形なのか、それともデータを持つ「デバイス」なのかを判別するロジックを開発者が都度書かねばならない。
- マジックストリングの氾濫: `CellsU(“Prop.AssetID”)` といった文字列ベースの操作は、タイプミスをコンパイル時に検知できない。
- ロジックの散逸: 描画位置の計算や、DBとの同期ロジックがメインのプロシージャに混在し、テストコードが書けない状態になる。
これらを解決するのが「ラッパー・クラス」による抽象化だ。
—
2. 実践:`DeviceShape` クラスの設計
例として、ネットワーク図のデバイスを表現するクラスを作成しよう。このクラスは、Shapeの複雑なセル操作を隠蔽し、`ID`や`Status`といったビジネス上のプロパティとして提供する。
クラスモジュール: `clsDevice`
Option Explicit
‘ 内部で保持するShapeオブジェクト
Private m_shp As Visio.Shape
‘ 初期化用メソッド
Public Sub Initialize(ByRef shp As Visio.Shape)
If shp Is Nothing Then Err.Raise 91, , “Shapeが指定されていません”
Set m_shp = shp
End Sub
‘ プロパティ:資産ID(Cell操作を隠蔽)
Public Property Get AssetID() As String
AssetID = m_shp.CellsU(“Prop.AssetID”).ResultStr(“”)
End Property
Public Property Let AssetID(ByVal Value As String)
m_shp.CellsU(“Prop.AssetID”).Formula = “””” & Value & “”””
End Property
‘ メソッド:デバイスのステータスを更新(ロジックの集約)
Public Sub SetStatus(ByVal status As String)
‘ ここに色を変える、ラベルを更新するなどの複合ロジックを閉じ込める
m_shp.CellsU(“Char.Color”).Formula = IIf(status = “Active”, “RGB(0,255,0)”, “RGB(255,0,0)”)
m_shp.Text = “Status: ” & status
End Sub
—
3. 実行エンジン:保守性を最大化する呼び出し方
クラス化したことで、メインロジックは「何をするか」という宣言的な記述に集中できる。
Sub UpdateNetworkDiagram()
Dim shp As Visio.Shape
Dim dev As clsDevice
‘ プロセス管理:特定のレイヤーや条件を持つShapeのみを処理
For Each shp In ActivePage.Shapes
‘ ここで型をチェックし、目的のオブジェクトのみをラップする
If shp.CellExists(“Prop.AssetID”, 0) Then
Set dev = New clsDevice
dev.Initialize shp
‘ 業務ロジックを実行
dev.AssetID = “SVR-001”
dev.SetStatus “Active”
End If
Next shp
End Sub
—
4. 伝説的アーキテクトからの「3つの戒律」
この設計をプロダクション環境で運用するために、以下の規律を守ってほしい。
① オブジェクトのライフサイクルを管理せよ
`Set dev = New clsDevice` をループ内で多用すると、メモリ管理が杜撰になりがちだ。可能であれば、`Collection`や`Scripting.Dictionary`を用いて、一度生成したクラスインスタンスをキャッシュし、再利用する仕組み(ファクトリパターン)を導入せよ。
② ファイル・DB連携は「DTO」を介せ
ShapeオブジェクトをDB接続ロジックに直接渡してはならない。必ず「Shape」→「業務データクラス(DTO)」→「DB」というレイヤー分離を徹底すること。これにより、VisioのバージョンアップやDBのスキーマ変更にも、片方の修正だけで対応できるようになる。
③ 「CellExists」による防御的プログラミング
Visioの図面は、ユーザーが勝手にShapeを削除したり、名前を変更したりする。`m_shp.CellsU` にアクセスする前に必ず `CellExists` で生存確認を行え。これを怠ると、ユーザーの操作一つであなたのツールは簡単にクラッシュする。
—
結びに:コードは「書く」ものではなく「育てる」もの
クラスモジュールによる抽象化は、最初は手間だと感じるかもしれない。しかし、複雑な図面自動生成ツールにおいて、この設計は「強固な土台」となる。
バグの起きないコードとは、「どこが間違っているかを特定しやすいコード」のことだ。Shapeをクラスという箱に閉じ込め、責務を明確にすれば、あなたのコードはVisioの不安定な挙動を超えて、長期間組織の資産として輝き続けるだろう。
さあ、今すぐ `Sheet1` に書かれた長大な手続きコードを、意味のあるオブジェクトへと再構築しよう。それが、プロのエンジニアの仕事だ。
