作成日 2008/9/15 最終更新日 2008/9/15
多態性
このページでは多態性のメリット、使用場面を説明します。
メリットですが、まずは、よく言われるif文が無くなるという点については
モジュールの凝集度と結合度 の点から、もう少し詳しく説明します。多態性を使用しなければ出来ないこと(例えばイベント処理)がありますが、それについても説明します。
多態性の使用場面としては
GoFのデザインパターン のうち多態性に関連するものを説明します。
※このページを読むには、少なくとも
モジュールの凝集度、結合度 について理解している必要があります。知らない人は先にそれらを理解してください。
なんか、訳語が「多態性」、「多相性」、「多様性」とたくさんあるのですが、特に理由は無いのですが、このページでは「多態性」で統一します。英語では「Polymorphism」なんだけど、こいつの読み方が、「ポリモーフィズム」と「ポリモルフィズム」といくつかあって面倒くさい。
多態性とはオブジェクトのメソッドを呼び出したときに、その動作がオブジェクトの型によって変化するようにする仕組みのことです。
とりあえず、下記のクラス図とサンプルソースを見てください。サンプルソースを実行するにはVBAのバージョンが6以上である必要があります。そのため、だるまのようにMacユーザの方やExcel
97を使用している方は実行できません。ごめんなさい。
図1
多態性のサンプルのクラス図
Class0(クラスモジュール)
Option Explicit
Public Sub printClassName()
End
Sub
Class1(クラスモジュール)
Option Explicit
Implements Class0
Private Sub Class0_printClassName()
Debug.Print "Class1"
End
Sub
Class2(クラスモジュール)
Option Explicit
Implements Class0
Private Sub Class0_printClassName()
Debug.Print "Class2"
End
Sub
Class3(クラスモジュール)
Option Explicit
Implements Class0
Private Sub Class0_printClassName()
Debug.Print "Class3"
End
Sub
Module1(標準モジュール)
Option Explicit
Public Sub
main()
Dim objs(1 To 3) As
Class0
Dim i As Integer
Set objs(1) = New
Class1
Set objs(2) = New
Class2
Set objs(3) = New Class3
For i = LBound (objs) To
UBound (objs)
Call
objs(i).printClassName
Next
End Sub
実行結果(イミディエイトウィンドウ)
Class1
Class2
Class3
標準モジュールの
call
objs(i).printClassName
のところで、objs配列の中身を意識せずにprintClassNameメソッドを呼び出しています。
そして、結果(標準モジュールのmainメソッドの結果)はそれぞれのクラスモジュールで定義したprintClassNameメソッドの実装に沿った結果となっています。
まずは、よく言われるif文がなくなる点について説明します。
VBAの場合、Object型を使用すると同じようなことが出来てしまうので説明がしにくい。
上記のソース(標準モジュール1)で、もし、多態性を使用しなかった場合はfor文の中で、
If
TypeName(objs(i)) = "Class1" Then
'...
Else If TypeName(objs(i)) =
"Class2" Then
'...
Else If TypeName(objs(i)) = "Class3"
Then
'...
End
If
のようなソースを書かないといけなくなる。
こうなると、そもそも、ソースの分量が増えるので読む気力がなくなってくる。さらに、分岐が増えるのでテスト工数が増えてしまう(わからない人は「ホワイトボックステスト 命令網羅 」でGoogleで調べてください。)。
さらに、モジュールの凝集度について思い出して欲しいのだけど、これは論理的凝集度という感じがしている。
(標準モジュール1の凝集度が悪くなってしまっていると思う。)
また、Class0のサブクラスであるClass1、Class2、Class3と結合している点もまずいです。
上記のIf文が1箇所しなかった場合はいいのだが、例えば1000箇所もあった場合にClass0のサブクラスが1つ増えたらどうしようか・・・。1000箇所修正するか・・・。そんなことをしたら、絶対修正漏れ起こすよ〜。
多態性のメリットをまとめると、「Class0を使用する側のソースの分量を減らすことが出来る」「If分岐が減ることによりテストにかかる工数を減らすことが出来る」、「モジュールの凝集度を上げ、結合度を下げることが出来る」となります。
次に、デメリットを説明する。
まず、本来ならClass1、Class2、Class3だけで良かったはずなのに、Class0が必要となっている点。要するに実装行数が増えてしまっているということ。
次。以下の状況を考えて欲しい。
後になってClass4が欲しくなりました。
Class4.printClassNameの設計していると、実は引数が必要であることがわかりました(※1)。でもClass0のprintClassNameは引数が無いんだけど、どうするの?Class0のprintClassNameに引数を増やしてもいいけど、そうするとClass1、Class2、Class3のprintClassNameメソッドを修正しなくちゃいけないし、さらに、既に使用されている箇所(1000箇所もあったら大変だ)も修正しないといけない・・・。
・・・と、後になって引数が足りないことに気がついたり、戻り値の型がおかしいことに気がついたりすることが多々ある・・・。マジで。
もちろん、そのようなことにならないよう、しっかり設計するのですが、しっかり設計するには工数がかかる。printClassNameの使用箇所が1000箇所もあれば、間違いなく多態性を使用したほうがいいのだけど、1箇所しかない場合は、多態性などしない方がよいです(※2)。
多態性のメリット・デメリットをまとめると表1のようになります。
表1 多態性のメリット・デメリットのまとめ
多態性のメリット
多態性のデメリット
■1.インタフェースを使用する側のソース量を減らすことが出来る。
■2.インタフェースを使用する側のIf文の数が減らせ、テストにかかる工数を減らすことが出来る。
■3.インタフェースを使用する側のモジュールの凝集度を高くできる。また結合度を下げることが出来る。
■4.インタフェースを使用する側は、インタフェースを実装するクラスが増えても修正する必要が無くなる。
■1.インタフェースの分だけソース量が増える。
■2.インタフェースの設計がいい加減だと、インタフェースを実装するクラスの種類が増えたときに対応できなくなる。
■3.2を回避するため、インタフェースの設計にとにかく工数がかかる。
-----
※1:より正確に言うと、「Class4.printClassNameの設計をしていると、実は
リスコフの置換原則
を守れなくなることがわかりました」。
※2:この内容は、「
実践UML 第3版 オブジェクト指向分析設計と反復型開発入門 」の下記の場所にも書かれています。
・p439(25.1
多相性パターン の 適用できない状況)
・p451(25.4 バリエーション防護壁(防護壁)パターン の 適用できない状況)
多態性を利用しないと出来ないことがある。その場合は多態性を使用するしかない。
多態性を利用しないといけない場合というのは、主にフレームワークを作成している場合、および、フレームワークを使用している場合です。
具体的にはイベント処理(一般的にはオブザーバパターン で実装。VBA(Ver.6以上)の場合は特別に構文がある)かな。
他にもあるかもしれませんが、これくらいで。
多態性の使用場面ですが、GoFのデザインパターンのうち、多態性が関係していると思われるものを紹介します。GoFのデザインパターンは23個しかないんだけど、多態性が関係しているものがかなりあり、紹介しきれません(※1)。申し訳ございません。
■オブザーバパターン
GUIなど(例えば、java.awtパッケージ)のイベント処理の部分で使用されるデザインパターンです。
フレームワーク側でイベント用のインタフェースを定義しておき、フレームワークを使用する側で、インタフェースを実装するクラスの作成をします。
■ストラテジーパターン
戦略がいくつかあり、それらを切り替えたりしたいときなどに使用するデザインパターンです。
たとえば、暗号化処理について考えてみてください。
暗号のアルゴリズム(ここでは共通鍵暗号方式 (※2)とします)にはDESやAESなどがありますが、いずれ、破られて使用できなくなる可能性があります。
その際、暗号化アルゴリズムを別の(まだ破られていない)アルゴリズムに簡単に切り替えられるようになっていれば修正が楽ですね。
実際、例えば、Java(java.securityパッケージ)ではどうだろうか・・・。他のデザインパターンも使用していそうだ。
■ステートパターン
オブジェクトの状態によってメソッドの動作が変わるようにしたいときに使用されるデザインパターンです。
1つの状態を1つのクラスとして定義しておきます。
■インタプリタパターン
あまり使うことは無いかもしれませんが、簡単に言うと構文解析をしたいときに使用するデザインパターンです。
■テンプレートメソッドパターン
■コマンドパターン
…関係あるっぽいよ。
-----
※1:だるまが調べきれないため。
※2:暗号化するときと、暗号化されたものを戻す(複合化)ときに同じキーを使用する暗号方式。
もっと勉強したい人は下記も読んでみて下さい。
このページの利用によって発生した、いかなる損害について、このホームページの作成者は責任を負いません。
このページの間違いや嘘を見つけた方、このページに書いて欲しい情報がある方は
メール をお願いします。
Microsoft 、Windows 、Visual Basic および Excel は米国Microsoft
Corporationの米国およびその他の国における登録商標または商標です。
ここではExcel® をエクセル、Visual Basic® for Applications をVBAと表記する場合があります。
Mac 、Mac OS 、Mac OS X は米国Apple Computer,Inc.の登録商標または商標です。
Sun、Sun Microsystems、サンのロゴマーク、Java、及び、Sun/Solaris/Java に関連するすべての商標およびロゴマークは米国 Sun Microsystems, Inc. の米国およびその他の国における商標または登録商標です。
その他、社名および商品名、システム名称などは、一般に各社の商標または登録商標です。
このホームページの作成者はこれらの会社とはいっさい関係がありません。