2016/5/1オブジェクト指向プログラミングに関して、VBAで出来ないことここでは、オブジェクト指向プログラミングに関して、VBAで出来ないことについて説明します(※1)。VBAでオブジェクト指向プログラミングをするときは、設計段階でVBAで何が出来ないのかを把握しておく必要があります。出来ないことを他の方法で回避しようとすると、ソース行数が増えたり、デバッグが面倒なことになるということを覚えて欲しいです。(実装や単体テストに工数がかかるようになるということを覚えて欲しいです。)-----※1:出来ることを説明するのではなく、出来ないことを説明する理由は、GoogleやVBAのヘルプにて、出来ることを調べるのは意外と簡単なのですが、出来ないことを調べるのはとても難しいからです。(出来ないことはヘルプに載っていないことが多く、検索条件が悪かったのかの判断が難しいためです。)1.継承がサポートされていない2.インタフェースのみの継承に問題がある3.エラーオブジェクトに問題がある4.パッケージという概念が無い5.1つのモジュールにクラススコープのメソッド・属性とインスタンススコープのメソッド・属性を同時に定義できない6.コンストラクタに引数を持たせられない7.オーバーロードができない8.リフレクション機能がいまいち1.継承がサポートされていないVBAでは継承がサポートされていません。※1つだけ補足しますが、ここで述べているのは、新しく作成するクラスは別のクラスを継承できないという意味です。あるクラス(特にExcelライブラリやOfficeライブラリに含まれるクラス)は別のクラスを継承していたりします。(回避策1or2を使えば、設計に関しては特に意識する必要はなくなりますが、実装にかかる工数が増えます。)■問題点2:デザインパターン(例えばGofのデザインパターン)の適用が難しくなります。(デザインパターンの中には継承を使用したものがあるためです。?これも問題があったりする。これだと、コンパイル時に行ってくれるチェックが緩くなってしまうため、デバッグに工数がかかるようになります。このページのトップへ2.インタフェースのみの継承に問題があるVBAではインタフェースのみの継承について2点問題があります。VBAでは「Implements」ステートメントを使用することによりインタフェースのみの継承を行うことが出来ます。「Implements」ステートメントの詳細についてはVBAのヘルプを参照してください。■問題点1:「Implements」ステートメントはVBAのバージョンが6以上でないとサポートされてないです。macではインタフェースのみの継承は行うことが出来ません。■問題点2:実装したメソッドの呼び出し方法についてです。実装したメソッドを呼ぶ出すにはオブジェクトを一旦、インタフェースクラスにキャストする必要があります。クラス1がクラス2のインタフェースを実装したとします(図1)。クラス図この場合、クラス1はクラス2のaaaメソッドを実装することになります。Obj1.aaaが実行可能かというと、出来ない(コンパイルエラーになります)です。以下のようにする必要があります。Obj2.aaa■感想:面倒くさい・・・。(行数が長くなりソースが読みにくくなります。Subではないからです。Subとすれば良いです。行数は少し増えますが、クラスを使う側のソースは増えないので良いと思います。hogeさんへ:掲示板への記載(データNo:201)、ありがとうございました。このページのトップへ3.エラーオブジェクトに問題があるエラーオブジェクト(VBAライブラリのErrObjectクラス)に問題があります。■問題点1:エラーに含むことの出来る情報が少なすぎです。エラーオブジェクトをネストすることも出来ません。cause属性が定義されています。これに相当するものがVBAのエラーオブジェクトにはありません。)また、javaにはあるのですが、VBAのエラーオブジェクトには関数の呼び出し履歴と行数の一覧がない(スタックトレースがない)のも問題があります(どこでエラーが発生したのかがわかりにくい)。■問題点2:VBAではエラーの種類を番号(Err.Number)で識別します。エラーを番号で識別するのは問題があります(番号が重複してしまう恐れがある)。■回避方法1(問題点1に対する回避方法):この問題を回避するために、独自のエラー用のクラスを作成することも考えられます。スタックトレースは関数名までしか出来ないです(※1)。文が使用しにくくなります。■回避方法2(問題点2に対する回避方法):エラーを、番号でJavaではjava.lang.Exceptionクラスを継承した例外クラスを自作で出来ますが、VBAではそのようなことは出来ません。回避方法1とあわせ(エラー用のオブジェクトを自作し)、文字列、もしくはバイナリデータ(バイト配列)から、エラーの種類がわかるようにするか・・・。面倒だけど(工数がかかるけど)。■回避方法3(?):それでも、VBA標準のエラーオブジェクトを使用する。プログラムが小さい場合はこれでも問題は無いです。-----※1:行番号を記述しておいて、Erl関数を使用するということをすれば、行数も取得できますが、お勧めしないです。関数名についても、もちろん、自分で手で実装しないと駄目です。Setステートメントは使用できなくなります。このページのトップへ4.パッケージという概念が無いVBAではパッケージという概念がないです。それに変わるようなものは、無いと思います。(「ライブラリ」や「プロジェクト」はパッケージに相当するかもしれませんが、しかし、1つのエクセルブック内には1つのプロジェクトしか含められませんので、代わりにはなりません(※1)。)これは、大きいプログラムを組んだときに問題になります。■問題点1:クラスをカテゴリ化することが出来ない。そのため、クラス数が多くなってくると、混乱してくる。■問題点2:自作の共通クラス(例えば、文字列操作用のユーティリティクラス、数学計算ルーチンがあるユーティリティクラス、ファイル関連のクラスなど。他のエクセルのマクロやアドインでも使用できそうなクラス)とそうでないクラスの区別がわかりにくくなる。■問題点3:当然ですが、UML2.0のpackageに相当するような可視性(VBAではprivate,publicしかない(※2)。本来だったら、可視性がpackageで十分なメソッド、属性の可視性をpublicにしなければならなくなることがあります。その結果、デバッグが面倒になります。■回避策(問題点1,2の回避策):(1)クラスの名前を工夫する。Objects)を使用する。シングルトンクラスを作成したい場合には、Sheet1などを使用すれば良いと説明しています。同じようにグラフ(Graph1など)も同様に使用可能です。シートとグラフではアイコンが異なるので、カテゴリ分けに使うことが出来ると思います。hogeさんへ:掲示板への記載(データNo:202)、ありがとうございました。Editor)の[ツール]-[参照設定]にて他のプロジェクト(他のエクセルブックやアドインにあるプロジェクト)やVB6.0で作成したDLLを参照するのであれば「ライブラリ」や「プロジェクト」はパッケージの代わりになるのかもしれません。他のプロジェクトを参照するエクセルブックやアドインが開いている必要がありますし、あまり一般的ではないと思います。クラスモジュールについては属性の「Instancing」の値を2(PublicNotCreatable)にする必要がありますが(こうしないと参照先のプロジェクトでクラス解決できません)、この属性はVBAのバージョンが6以上でないとサポートされていません。macのVBAのバージョンは5です。)VB6.0で作成したDLLを参照するのは結構やる手ですが、Macでは出来ません(作成したマクロ、アドインがMacで動作しなくなります)。※2:正確には、Friendという可視性もあります。FriendはVBAのバージョンが6以上でないとサポートされていません。macのVBAのバージョンは5です。)このページのトップへ5.1つのモジュールにクラススコープのメソッド・属性とインスタンススコープのメソッド・属性を同時に定義できないVBAでは1つのモジュールにクラススコープのメソッド・属性とインスタンススコープのメソッド・属性を同時に定義できません。VBAではクラススコープのメソッド・属性は標準モジュールに定義する必要があり、インスタンススコープのメソッド・属性はクラスモジュールorユーザーフォーム内に定義しなければならないためです。■問題点1:例えば、本来であれば可視性がprivateで良いはずのクラススコープの属性をpublicにしなければならなくなります。これにより情報隠蔽・カプセル化がうまく出来なくなります。そのため、デバッグに工数がかかるようになります。■問題点2:問題点1により、シングルトンパターンの実装が行いにくいです。■回避策(問題点2の回避策):シートクラス(Sheet1など)を使えばシングルトンパターンを簡単に作成できます。いうか、初めから、シングルトンになっています。できない)様になっています。言っていることが難しいのですが、シートを後から挿入は出来ますが、それは別のクラスになります(分かりにくいのですが、「Sheet」は何個でも作れるが、「Sheet1」は1つしかない)。hogeさんへ:掲示板への記載(データNo:202)、ありがとうございました。このページのトップへ6.コンストラクタに引数を持たせられないVBAではコンストラクタに引数を持たせられないです。■回避方法:initメソッドを作成し、newした後に呼んでもらうしかないと思います。この場合、本当にnewの後にinitが呼ばれているかどうかのチェックはソースを目視で確認するか、単体テストにて確認するしかないです。(要するにデバッグに工数がかかります。)このページのトップへ7.オーバーロードが出来ないVBAではオーバーロードが出来ないです。■回避方法:1番目の方法としてOptionalキーワード(引数を省略可能にする)やParamArrayキーワード(任意の個数の引数を渡せる)を使用してオーバーロードのように見せかけることが挙げられます。やりすぎるとソースが読みにくくなるので注意してください。(ま、それはオーバロードもそうですが。)2番目の方法としては引数をVariant型やObject型にすることでしょうか。コンパイルチェックが甘くなるのであまりお勧めできません。3番目の方法としてはオーバーロードを使用しない設計にすることです。だるま個人の考えではこれが一番良いです。このページのトップへ8.リフレクション機能がいまいちVBAはリフレクション機能がいまいちです。一応、VBIDEを使えばVBAソースを取得できるので、がんばれば多少なんとかなりますが(※1)、自作のクラスに限られます。組み込みのクラス(※2)のメソッド一覧を実行時に取得するのは出来ないです。)一覧を実行時に取得するのは非常に厳しいと思ってください。それと、クラス名からクラスのオブジェクトを生成することは無理だと思います。ような感じで出来ると思うのだが(※3)。)VBAで出来るのは、以下の2点くらいしか思いつかなかったです。・TypeName関数を使用してオブジェクトのクラス名を取得する。・CallByName関数(※4)を使用してオブジェクトのメソッド名からメソッドを呼び出す。-----※1:ここで言っている、「がんばれば」というのは「VBAソースコードを構文解析すれば」という意味です。メソッド情報一覧の取得だけでよいのであれば、それほど大変ではないのですが(※Declare宣言されたメソッドは除く)、属性情報の一覧の取得は面倒です。※2:初めからVBAに搭載されているクラス。VBA.CollectionクラスやExcel.Workbookクラス。※3:このJavaの例はコンストラクタに引数がなかった場合の例です。コンストラクタに引数がある場合は、もう少し長いコードになります。※4:CallByName関数を使用するには、VBAのバージョンが6以上であることが必要となります。ExcelがWindows版でかつ2000以上であればVBAのバージョンは6以上です。このページのトップへこのページの利用によって発生した、いかなる損害について、このホームページの作成者は責任を負いません。このページの間違いや嘘を見つけた方、このページに書いて欲しい情報がある方はメールをお願いします。Corporationの米国およびその他の国における登録商標または商標です。VBAと表記する場合があります。Computer,Inc.の登録商標または商標です。Groupの商標または登録商標です。米国およびその他の国における商標または登録商標です。その他、社名および商品名、システム名称などは、一般に各社の商標または登録商標です。このホームページの作成者はこれらの会社とはいっさい関係がありません。