VBAクラスメソッドのダックタイピング
インターフェースを作らずに、同じ名前のメソッドを呼び出す設計を考える
Copyright © 2026 LWP 山中 一弘
本資料は、出典を明記いただければ、商用・非商用を問わず、ご自由に複製・改変・再配布していただけます。なお、著作権表示は改変せず、そのまま記載してご利用くださいますようお願いいたします。
記事要約
VBAでは、クラスを使うことはできますが、一般的なオブジェクト指向言語のような継承はできません。一方で、インターフェースは使えます。ただし、VBAのインターフェースは、インターフェース用クラスを作り、実装側で Implements を書き、実装メソッド名にもインターフェース名が付くため、少し大げさに感じることがあります。
この記事では、VBAで「同じ名前のメソッドを持っていれば呼び出す」という、ダックタイピング的な考え方を使う方法を整理します。中心になるのは、インスタンスを Object として受け取り、そのオブジェクトに目的のメソッドがある前提で呼び出す設計です。
この方法は、個人用マクロや小さなツールでは、処理の差し替えやテンプレートパターン的な構成を簡潔に書けることがあります。ただし、コンパイル時に型の保証が効きにくく、業務用の保守コードでは注意が必要です。便利な裏技としてではなく、使いどころと危険性を分けて理解することが重要です。
本記事の対象とゴール
想定読者
VBAでクラスを使い始めた人
VBAの Implements が少し重いと感じている人
同じ処理の流れの中で、部分的な振る舞いだけを差し替えたい人
CallByName や遅延バインディングに興味がある人
個人用マクロと業務用マクロで設計の使い分けを考えたい人
本記事で得られること
VBAでダックタイピング的な呼び出しがどういう意味か理解できます。
インターフェースを使う方法と、同名メソッドを直接呼び出す方法の違いを整理できます。
個人用マクロでは便利でも、業務コードでは慎重に扱うべき理由を説明できます。
本記事で扱わないこと
VBAのクラスモジュール全体の作り方
Implements の詳細な文法解説
CallByName の全機能解説
大規模業務システムでの設計標準
他言語におけるダックタイピングの厳密な仕様比較
先に結論
VBAでも、インスタンスに同じ名前のメソッドがあれば、それを呼び出すというダックタイピング的な書き方はできます。
ただし、これはVBAに明示的なダックタイピング機能があるというより、Object として受け取った変数に対して、実行時にメソッド呼び出しを試みる設計です。
たとえば、鳥のように鳴くオブジェクトを受け取り、DoCry メソッドがあれば呼び出す、という形です。
コード例
Sub BirdCry(ByVal bird As Object)On Error Resume Nextbird.DoCryIf Err.Number <> 0 Then Debug.Print "bird has no method DoCry" Err.ClearEnd IfOn Error GoTo 0End Subこの例では、引数 bird がどのクラスのインスタンスであるかを、コンパイル時には固定していません。重要なのは、そのオブジェクトが DoCry というメソッドを持っているかどうかです。
この考え方は、簡単な多態性を使いたいときには便利です。しかし、呼び出し先メソッドの存在確認が実行時に回るため、間違いを早い段階で見つけにくくなります。個人用の小さなマクロでは便利でも、複数人で保守する業務コードでは、インターフェースや明示的な分岐の方が安全な場合があります。
第1章(VBAのクラスで悩みやすい点)
VBAのクラスは便利ですが、他のオブジェクト指向言語と同じ感覚で使おうとすると、いくつか引っかかる点があります。
1.1 継承はできないがインターフェースは使える
VBAでは、クラスを継承して親クラスの機能を引き継ぐ、という書き方はできません。
一方で、Implements を使えば、インターフェースのような設計はできます。つまり、「このクラスは、このメソッド群を持つべきである」という約束を作れます。
ただし、VBAの Implements は書き方が少し独特です。インターフェース用のクラスモジュールを作り、実装側のクラスでは、インターフェース名とメソッド名を組み合わせた形で実装します。
そのため、小さな個人用マクロでは、「ここまで厳密に書くほどではない」と感じる場面があります。
1.2 欲しいのは継承ではなく振る舞いの差し替えである
多くのマクロでは、継承そのものが欲しいわけではありません。
欲しいのは、同じ処理の流れの中で、一部の振る舞いだけを差し替えることです。
たとえば、共通処理の中で、対象ごとに「鳴く」「出力する」「検証する」「変換する」といった部分だけを入れ替えたいことがあります。
このとき、すべてを Select Case で分岐すると、対象が増えるたびに共通処理側を修正することになります。共通処理側は、できれば「呼ぶだけ」にしておきたいところです。
第2章(ダックタイピング的に考える)
ダックタイピングとは、厳密な型名よりも、必要な振る舞いを持っているかどうかで扱う考え方です。
2.1 型名ではなくメソッド名を見る
有名な言い方では、「アヒルのように歩き、アヒルのように鳴くなら、それはアヒルとして扱える」という説明があります。
VBAで同じことを考えるなら、「そのオブジェクトが DoCry を持っているなら、鳴けるものとして扱う」という発想になります。
これは、インターフェースで型の約束を作る方法とは違います。インターフェースでは、事前に契約を作ります。ダックタイピング的な呼び出しでは、実行時にそのメソッドがあるものとして呼びます。
2.2 `Object` で受け取る
VBAでこの考え方を使う場合、引数は具体的なクラス型ではなく Object として受け取ります。
具体的なクラス型で受け取ると、その型のメソッドしか呼べません。Object として受け取ると、実行時にそのインスタンスが持っているメソッドを呼びに行けます。
ただし、これは安全性と引き換えです。メソッド名を間違えても、コンパイル時には見つからないことがあります。そのため、エラー処理やテストが重要になります。
第3章(コード例の読み方)
ここでは、先ほどのコード例を、設計上の意味から読み直します。
3.1 `BirdCry` は型ではなく役割を受け取る
BirdCry は、特定の鳥クラスを受け取っているのではありません。
受け取っているのは、DoCry という役割を持つオブジェクトです。
この発想にすると、犬でも猫でも機械でも、DoCry を持っていれば同じ処理に渡せます。もちろん、名前として BirdCry が適切かは別問題です。実務では、RunAction や ExecuteStep のように、役割を表す名前にした方がよい場合もあります。
3.2 `On Error Resume Next` は境界を狭くする
この書き方では、存在しないメソッドを呼んだときのエラーを拾うために、On Error Resume Next を使っています。
ただし、On Error Resume Next は広くかけると危険です。関係ないエラーまで握りつぶしてしまうからです。
使う場合は、対象メソッドを呼び出す直前から直後までに範囲を狭め、エラーを確認したら Err.Clear と On Error GoTo 0 で通常のエラー処理へ戻す必要があります。
3.3 `CallByName` との関係
VBAで動的にメソッドを呼ぶ方法としては、CallByName もあります。
CallByName は、メソッド名を文字列として渡して呼び出せるため、より明示的に動的呼び出しを書けます。
一方で、今回の例のように、直接 bird.DoCry と書く方法は、コードの見た目が簡潔です。文字列でメソッド名を渡すより読みやすい場合もあります。
どちらを選ぶかは、目的によります。メソッド名を設定値として扱いたいなら CallByName が向きます。決まった役割を簡潔に呼びたいだけなら、同名メソッドを直接呼ぶ方が読みやすいことがあります。
第4章(使いどころ)
この方法は、万能ではありません。向いている場面と、避けた方がよい場面を分けて考えます。
4.1 個人用マクロや小さなツールでは便利
個人用マクロでは、コード全体を自分で把握できることが多く、多少動的な書き方をしても保守できます。
たとえば、処理ステップをクラスとして分け、各クラスに同じ名前の Execute メソッドを持たせると、共通処理側は各ステップを順番に呼ぶだけで済みます。
このような場面では、インターフェースを作らずに、同じ名前のメソッドでそろえるだけでも十分なことがあります。
4.2 テンプレートパターン的に使える
この方法は、テンプレートパターン的な構成にも使えます。
共通の流れは一つのプロシージャに置き、差し替えたい処理だけを別クラスに持たせます。共通処理側は、そのクラスの特定メソッドを呼ぶだけにします。
これにより、共通処理を大きく変えずに、対象ごとの振る舞いを追加できます。
4.3 業務コードでは慎重に扱う
業務コードでは、動的な呼び出しは慎重に扱うべきです。
理由は、コンパイル時にメソッドの存在を確認しにくく、仕様変更やリネームの影響が見えづらくなるためです。
複数人で保守する場合や、長期的に使うマクロでは、インターフェースを使って契約を明示する方が安全です。
また、エラーを握りつぶす書き方は、障害調査を難しくします。業務コードで使うなら、失敗時にどのクラスで、どのメソッドが見つからなかったのかをログに残すべきです。
第5章(判断基準)
最後に、この方法を使うかどうかの判断基準を整理します。
5.1 使ってよい場面
使ってよいのは、対象範囲が小さく、呼び出すメソッド名が安定しており、失敗時の挙動を自分で確認できる場面です。
個人用ツール、実験コード、短命の補助マクロ、処理ステップを簡単に差し替えたい場面では、実用的な選択肢になります。
5.2 避けた方がよい場面
避けた方がよいのは、複数人で長期保守する業務マクロ、エラーが業務影響に直結する処理、メソッド名が頻繁に変わる処理、テストが十分にない処理です。
このような場面では、インターフェース、明示的な型、または分かりやすい分岐を使った方が、保守上の安心感があります。
5.3 使うならルールを決める
使う場合は、最低限のルールを決めておきます。
呼び出すメソッド名を固定する
エラー処理の範囲を狭くする
失敗時のログを残す
メソッド名を変更したときに確認するテストを用意する
業務本番処理では、安易にエラーを無視しない
これらを守れば、VBAでもダックタイピング的な設計を、過度に危険な裏技ではなく、限定的な設計手段として使えます。
まとめ
VBAでは継承はできませんが、クラス、インターフェース、Object、遅延バインディング的な呼び出しを組み合わせることで、簡単な多態性を表現できます。
同じ名前のメソッドを持つオブジェクトを受け取り、そのメソッドを呼び出す設計は、ダックタイピング的な考え方です。
この方法は、個人用マクロや小さなツールでは、シンプルで分かりやすいことがあります。特に、処理の流れは同じで、一部の振る舞いだけを差し替えたい場合には便利です。
一方で、業務コードでは、型の保証が弱く、エラーが実行時まで見つからない危険があります。業務で使うなら、インターフェースを使うか、少なくともエラー処理とログ、テストを用意するべきです。
結論として、VBAのダックタイピング的な書き方は、知っておく価値があります。ただし、便利だから常用するものではなく、範囲を限定して使う設計手段として扱うのがよいです。
元記事
この記事は、Dropbox Paperに残っていた過去記事「VBA クラスメソッドのダックタイピング」(2019年4月21日)を原型として、LWP公開記事用に再構成したものです。
元記事URL: https://www.dropbox.com/scl/fi/pl9cs00k8y1rz5twvvc6w/VBA.paper?rlkey=10mgj99ostxkwvu4ekwj89q8b&dl=0
