Visual Basic 中級講座 |
Visual Basic 中学校 > 中級講座 >
オブジェクト指向の考え方を説明します。オブジェクト、カプセル化、ポリモーフィズム、継承という重要な考え方が登場します。
概要 ・ オブジェクト指向とは現実に存在する「物」に似せてプログラムを行い、「物」同士を連携させてシステム・アプリケーションを構築していくスタンス
・オブジェクトにはデータと振舞いがある。
・オブジェクトは内部の詳細を隠して外部に対しては使用方法と結果だけを定義すべきである。これをカプセル化という。 ・同じ振舞いをするオブジェクトは同じように呼び出せるようにすべきである。これをポリモーフィズムという。 ・継承とは既に存在するオブジェクトを拡張して新しいオブジェクトを作成することである。 |
オブジェクト指向プログラミングとはプログラムの作り方のスタンスの1つです。
世間では「オブジェクト指向」という言葉がいろいろなところで使われているので、この記事を読んでいる方も「オブジェクト指向」という言葉は聞いたことがあると思います。
そのためか「オブジェクト指向」というと小難しく身構えて考えてしまう方が多いようですが、単なる考え方の1つに過ぎません。みなさんが肩の力を抜いてこの記事を読むことを私は望んでいます。
今回はオブジェクト指向という言葉を聞いたことがない人でも読めるように初歩からオブジェクト指向について考えていきます。具体的なプログラム例は次回以降から登場します。
後ほどまた説明しますが、「オブジェクト指向」という言葉は、「オブジェクト指向プログラミング」の他にも、「オブジェクト指向分析」、「オブジェクト指向設計」などのように使われます。Visual Basic 中学校ではただ単に「オブジェクト指向」という場合は、「オブジェクト指向プログラミング」を意味していることが多いです。 |
オブジェクト指向とは、現実に存在する「物」に似せてプログラムを行い、「物」同士を連携させてシステム・アプリケーションを構築していくスタンスです。この「物」のことを「オブジェクト」と呼びます。
現実に存在する「物」に似せてプログラムを行うことを「コンポーネント指向」と呼びます。ですから、オブジェクト指向とは、コンポーネント指向 + オブジェクトの連携と考えることができます。この「連携」のさせ方にポイントがあるのですが、その点はすぐ少し後で説明します。
オブジェクト指向とは、 |
現実の「物」に着目 → 現実の「物」に似せてする。(=「コンポーネント指向」) |
「物」の連携 → 「物」同士が連携して動作するようにする。 |
考えてみれば、VBでよくでてくるButton(ボタン)は見かけも動作も現実のボタンにそっくりですよね。でっぱっていて、押すとひっこんで何かの機能が作動します。
Buttonなどはとてもわかりやすい方ですが、ファイル(書類)や日付など目に見えないオブジェクトもできるだけ現実に存在する「物」のようにデザインしていくのが特徴です。たとえば、File.Copyメソッドは書類をコピーするという現実世界の動作に対応しています。
オブジェクト指向で問題とする「物」とは、本文中でも説明したように、ボタンや書類などのように見える「もの」もありますし、日付や会議、財産、注文など見えない「物」もあります。 これら、見えない物は国語で言う「抽象名詞」に相当します。 一方、身長や速さ、赤、預金残高などは一個の独立した「もの」というよりは、「太郎君の身長」、「バラの色」のように何か別のものの性質ですので通常はオブジェクト扱いしません。 |
オブジェクト指向で言う「オブジェクト」についてもう少し詳しく見てみましょう。
オブジェクト指向では1つのオブジェクトには「データ」と「振舞い」という2種類の性質があると考えます。たとえば、TextBoxには現在の文字列や、TextBoxの位置、背景の色などのデータがありますし、内容をクリアしたり、文字を追加したりという振舞いがあります。
データ | 対応するプロパティ | 振舞い | 対応するメソッド |
現在の文字列 | Text | クリアする | Clear |
位置 | Location | 文字を追加する | AppendText |
背景の色 | BackColor | フォーカスを取得する | Focus |
オブジェクトをデータと振舞いに分けて考えるのは、オブジェクトの連携を前提としているからです。オブジェクトは自分自身に関する情報としてのデータを持つと同時に、外部のオブジェクトと連携するための振舞いを持つと考えるわけです。
振舞いの中には単にデータを公開するだけの振舞いもあります。
この時点で既に明らかではありますが、VBではクラスやそのインスタンスこそがオブジェクトにあたります。そして、VBにあてはめるとデータとはフィールドのことで、振舞いとはメソッドことです。プロパティはフィールドにアクセスするためのメソッドということになります。
■画像1:オブジェクトの構造。TextBoxの例。
発展学習では意欲的な方のために現段階では特に理解する必要はない項目を解説します。 ほとんどのフィールドはPrivateで宣言されているため、通常はどのようなフィールドが定義されているのか知ることができませんが、リフレクションを利用するとPrivateメンバも見ることができます。これを簡単に行う優れたツールがMSIL逆アセンブラ(ILDASM.exe)です。 MSIL逆アセンブラを使うと、Textプロパティはtextフィールドを読み書きしていることがわかります。また、Locationプロパティはxフィールドとyフィールドにアクセスしていることもわかります。 |
オブジェクトに関して重要なのは、どのようなプロパティであれメソッドであれプログラマはそれがどのような仕組みでプログラムされているか知らなくてもよいということです。
たとえば、AppendTextメソッドを使用して文字を追加しようとする場合は、マイクロソフトのプログラマがAppendTextメソッドの中身をどのようにプログラムしたかということは一切知る必要はありません。AppendTextメソッドを呼び出すと何が起きるのかという結果だけ知っていればいいのです。
特にAppendTextメソッドを使用すると文字列が変わるのですからデータであるTextプロパティ(を通じてアクセスされる フィールド)にも影響が及ぶはずですが、こういった内部の動きはまったく気にする必要がありません。
このように、内部の詳細を隠して外部に対しては使用方法と結果だけを定義することを「カプセル化」と呼びます。「隠蔽」(いんぺい)と呼ぶ場合もあります。
あまり経験がない方は「内部の詳細は知らなくていいなんて、あたりまえでしょ」と思われるかもしれませんが、このことが特に重要なのはオブジェクトを使うときよりも、オブジェクトを作る時なのです。アプリケーションを構築するときには当然無数のオブジェクトをその中でデザインすることになります。その時、あなたはきちんとカプセル化したオブジェクトをデザインしなければいけません。
たとえば、まったく無関係のObjectAとObjectBがあったとします。このとき、ObjectAのAPropプロパティの値によって、ObjectBのBMethodメソッドの結果が変わってしまうとしたらこれはカプセル化ができているとは言えません。
以上、ざざっとカプセル化の説明をしましたが、そもそもどこからカプセル化の話が出てきたかわかりますか?「現実世界の物に似せてプログラムを行うこと」(コンポーネント指向)と、「内部の詳細を隠して外部に対しては使用方法と結果だけを定義すること」(カプセル化)にはどのような関係があるでしょうか?
1つは、現実世界の物こそまさにカプセル化されているということです。テレビの仕組みを知らなくてもチャンネルを変えれば見たい番組が見られます。つまりテレビはカプセル化されています。
ですから、現実世界の物に似せれば似せるほど必然的にカプセル化の要素が備わってくるのです。
では、現実世界の物はなぜカプセル化されているのでしょうか?考えるまでもありませんね。便利だからです。テレビの仕組みを知らないとテレビが使えないなんてことは想像もできません。ここで言う「便利」の要素がそのままプログラムの世界にもよく馴染むのです。AppendTextメソッドの仕組みを知らないとAppendTextメソッドを使用できないなんて想像できませんよね。
つまり、現実世界でもプログラムの世界でも「仕組みを知らなくても使える」ということが非常に便利なのです。
次にオブジェクトの連携について考えていきましょう。オブジェクト指向ではオブジェクトの連携の仕組みとして「ポリモーフィズム」と「継承」というものが利用されています。
ポリモーフィズムとは「同じ振舞いをするオブジェクトは同じように呼び出せるようにすべきである」という考え方のことです。「多態性」と呼ばれる場合もあります。
たとえば、TextBoxやLabel, Buttonには文字列を表示する機能があり、実際に文字列を設定したり取得するためにはTextプロパティを使用します。これはもっともよく使うポリモーフィズムの例です。もし、Labelに文字列をセットするのはCaptionプロパティで、Buttonに文字列をセットするのがNoteプロパティというように、同じ機能なのに呼び出し方が異なるようならばどんなに不便か想像に難くありません。
なお、TextBoxやLabelなどの内部のプログラムでは文字列を表示する仕組みが異なる場合もあるのかもしれませんが、カプセル化のおかげでプログラマはそのようなことを意識しないで済みます。こう考えるとポリモーフィズムの成立にはカプセル化は不可欠と言えます。
ポリモーフィズムでは単に同じ名前のメソッドやプロパティを使おうということだけではなく、同じように呼び出せるようにしようという意味が込められています。ですから、メソッドやプロパティの名前以外にその呼び出しに使用する引数の型や数・順番にも気を配らなければいけません。
戻り値は呼び出しの結果であって、呼び出すために必要なものではないのでポリモーフィズムの観点からは特に気にする必要はありません。
となると、具体的にポリモーフィズムを実装するには同じ振舞いに対しては同じメソッド・プロパティ名、引数の型・数・順番を使用するということになります。この要素のことを「シグネチャ」と呼びます。
シグネチャが1つでも異なれば、名前が同じでも違うメソッド・プロパティということになります。
シグネチャの要素 |
メソッド・プロパティの名前 |
引数の型 |
引数の順番・数 |
例を挙げると、TextBoxにはPasteという名前のメソッドが2つ定義されています。1つは引数なしのPasteメソッドで、もう1つはString型の引数を1つ採るPasteメソッドです。このことを単純に次のように表記します。
Paste()
Paste(String)
この2つのシグネチャが異なることは見ればすぐにわかりますね。なお、引数の名前は呼び出し時には使用しないので重要ではありません。
さて、今度は「現実世界の物に似せてプログラムを行うこと」(コンポーネント指向)と、「同じ振舞いをするオブジェクトは同じように呼び出せるようにすべきであること」(ポリモーフィズム)にはどのような関係があるか考えてみましょう。
ここでもボタンの例が登場します。ボタンは身近にあって最もよく機能しているポリモーフィズムの例です。どのような装置であればボタンが付いていれば、そのボタンを押すことで機能が作動するということは3歳児にもわかることです。
テレビでも、電灯でも、自動販売機でも目的のボタンを押せば機能が発動するようになっています。このときのシグネチャは「押すという動作」です。
正確には、自動販売機の場合は、「お金を入れる」、「押す」というシグネチャになります。 |
もし、それぞれの装置でまったく機能の発動のさせ方が異なるとしたら社会的な非効率性は想像を絶するほどでしょう。現実世界ではこのようにポリモーフィズムが非常に役に立っているのです。そして、現実世界の物に似せてプログラムを行っていくのであれば当然、このポリモーフィズムを意識した方が上手くいくということです。
これでポリモーフィズムの考え方について一通り説明しましたが、VBで実際にポリモーフィズムを実装するには単にシグネチャを統一するだけでは足りません。シグネチャが同じであることを保証するためにインターフェースや継承という仕組みを使用する必要があります。このようなVBでの具体的な実現方法については次回以降から説明する予定です。
継承とは「既に存在するオブジェクトを拡張して新しいオブジェクトを作成すること」です。
前述のカプセル化やポリモーフィズムに比べると考え方としての重要性は低いのですが、実際のプログラムではとても便利で派手な機能で、オブジェクト指向の代名詞にもなっています。
たとえば、CheckedListBoxはListBoxとほとんど同じ機能のコントロールですから、ListBoxに機能を追加するだけで作成できるはずです。このような場合、実際のCheckedListBoxのプログラムでもListBoxのプログラムをベースにして変更がある部分だけを変えたり、機能を追加したりして作成します。
このような作業を継承を使わないで行うと、ListBoxのプログラムをコピーしてから貼り付けて手直ししていくということになりますが、継承を使用すればコピー&貼り付けすら必要ありません。また、コピー&貼り付けで作業した場合、万一元にしたプログラムにバグがあった場合、2か所を修正する必要がありますが、継承の場合は、元のプログラムの方を修正すれば継承先の機能も自動的に修正されます。
このように、継承は実際のプログラムでは非常に利点のある機能です。
継承の基になるクラスを基底クラスと呼びます。Javaなどの言語ではスーパークラスと呼ぶ場合もあります。継承して新しく作成されるクラスを派生クラスと呼びます。Javaなどの言語ではサブクラスと呼ぶ場合もあります。
通常は以上の機能が非常にクローズアップされていて、これが継承のすべてと思われる場合もあるようです。
しかし、継承にはもう1つの重要な機能があります。それは前述のポリモーフィズムにおいてシグネチャが同じであることを保証する機能です。たとえば、TextBoxのTextプロパティはControlクラスから継承したものです。また、LabelのTextプロパティもControlクラスから継承したものです。ですから、この2つのプロパティは同じシグネチャです。
正確にはTextBoxクラスはTextBoxBaseクラスを継承しており、このTextBoxBaseクラスがControlクラスを継承しています。 |
だから、次のようなプログラムを書くことができるのです。
Dim o As
Control If MsgBox("TextBoxにしますか?", MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then o = TextBox1 Else o = Label1 End If o.Text = "こんにちは!" |
■リスト1
これは継承によってポリモーフィズムが実装されている例です。この例では実行するまでoがTextBoxなのかLabelなのかわかりませんが、どちらも同じシグネチャなのでわかる必要がないのです。シグネチャが同じであれば呼び出しには成功し、呼び出しに成功すればカプセル化の恩恵で機能は発動するのです。どのような機械でもボタンを押せば機能が発動するのと同じ理屈になるように見事にモデル化されているのです。
上記は小さなくだらない事例ではありますが、オブジェクト指向が有効に動作している例です。
以上でオブジェクト指向の考え方については説明を終わりました。あとはこの考え方に基づいてどうやって実際にプログラムしていくかという実装の問題になります。この点については次回以降に扱います。
さて、結局オブジェクト指向とはなんだったのか、まとめの意味も含めて最後にもう一度全体を眺めてみましょう。
そもそもオブジェクト指向とは、「現実に存在する「物」に似せてプログラムを行い、「物」同士を連携させてシステム・アプリケーションを構築していくスタンス」のことでした。
つまり、オブジェクト指向とは2つの大きな考え方を結合したものと言えます。
オブジェクト指向の2つの大きな考え方 |
現実に存在する「物」に似せてプログラムを行う(=コンポーネント指向) |
「物」同士を連携させてシステム・アプリケーションを構築していく |
■オブジェクト指向の2つの大きな考え方
前段はコンポーネント指向とも呼ばれ、普通にVBでプログラムしていれば既にコンポーネント指向になっています。
ここでいう「物」こそが「オブジェクト」なのであり、VBではオブジェクトをクラスやそのインスタンスとして表現するのでした。
オブジェクト指向ではオブジェクトには2つの要素があると考えます。データと振舞いです。
VB用語に当てはめるとデータのことをフィールドと呼び、振舞いのことをメソッドと呼びます。プロパティはフィールドにアクセスするためのメソッドです。
コンポーネント指向の重要な仕組みのひとつが「カプセル化」でした。そして、後段の「物」同士の連携で重要になる仕組みが「ポリモーフィズム」と「継承」でした。この3つをオブジェクト指向の三大要素と呼ぶ場合もあります。
カプセル化 | 内部の詳細を隠して外部に対しては使用方法と結果だけを定義すること |
ポリモーフィズム | 同じ振舞いをするオブジェクトは同じように呼び出せるようにすること |
継承 | 既に存在するオブジェクトを拡張して新しいオブジェクトを作成すること |
■オブジェクト指向の三大要素
この3つの要素はバラバラに存在しているのではなく密接に関連しながら存在していることはすでに説明しました。カプセル化あってのポリモーフィズムであり、継承はただ単に便利であるという他にポリモーフィズムを補強する意味があるのです。
さて、ここまでの概要を理解できてもまだ安心してはいけません。そもそもオブジェクト指向とは何のためにあるのでしょうか?
究極的には「便利だから」と言えてしまいますが、そのような答えではたいていの物に当てはまってしまうので意味がありません。もう少しスケールダウンして考えてみてください。
私個人の考えですがオブジェクト指向の存在意義は次の2点にあると思います。
1つは、プログラムでのコードの量を少なくすることです。継承などはまさにこれですし、カプセル化が保障されていればメソッドの呼び出しも楽になります。コードの量が少なくなればプログラムの開発コストは下がりますし、バグも減ります。
もう1つは、静的な結合を行うためです。つまり、実行するまで何が起こるかわかりませんというのではなく、実行する前に何が起こるかわかっている状態にするということです。ポリモーフィズムはこの静的な結合のためにはとても重要なスタンスです。実行するまで何が起こるかわからない状態では、実行時にCPUに無駄な動作が多くなりますし、何より「私のパソコンでは動いていたのにあなたのパソコンでは動かない」という事態を発生させます。
また、実行する前にエラーを発見する確率が高くなり開発のリスクを低減する効果があります。聞いた話ではAda(エイダ)という言語は完全に静的な言語で開発時に動作したとおりに実行時に動作することが保障されていたということです。静的な方向というのはこのようなものです。
私としてはあまり静的な要素が多くなるとかえって不便な面もありますし、VBは伝統的に動的な動きを得意とする言語ですから、静的な側面はほどほどにしておきたいところですが、2000年代初頭くらいにはプログラム言語はできるだけ静的であるべきだという主張が強くなされていた印象があります。
話の順序ではまず、オブジェクト指向の説明をして、それからオブジェクト指向の存在意義を考えましたが、実際には必要があってオブジェクト指向が生まれてきたわけですから、私達の先進の苦労を軽んじることなくオブジェクト指向をメリットを享受していきましょう。
現代ではオブジェクト指向分析、オブジェクト指向設計などプログラミング以外の要素についてもオブジェクト指向という言葉を使う場合があります。 これらと区別してプログラミングのオブジェクト指向のことを「オブジェクト指向プログラミング」、略称OOP(Object Oriented Programming)と呼びます。 オブジェクト指向分析やオブジェクト指向設計とはオブジェクト指向プログラミングと同じ考え方に基づいたシステムの分析や設計を指しています。 オブジェクト指向分析やオブジェクト指向設計には直接プログラムに関連して、便利に活用できる側面もありますが、プログラムとは関係なく分析手法・設計手法として役に立つ側面もあります。 プログラム的な側面に限って考えてみると、システムの設計が旧来のものである場合、プログラミング作業だけオブジェクト指向で行うというのは効率的でない場合があります。ですから、真にオブジェクト指向の恩恵を受けたければ設計段階からオブジェクト指向で考えていくことが大切になります。 たとえば、システムの中で何がオブジェクトになるか考える必要があります。プログラミング時に技術的な都合からデザインされるオブジェクトも当然存在しますが、 システムで重要な観念は設計時からオブジェクトとして認識した方が当然オブジェクト指向に適合します。 例として大学の学生管理システムでは、「学生」が重要なオブジェクトになることは設計段階から明らかです。そうすると学生にはデータと振舞いがありますから、どのようなデータを持ってどのような振舞いをするのか考えていくことで設計作業が進んでいきます。 他にも「講義」や「教授」、「学校」などがオブジェクトになりそうですね。
それから、余談ですが世間では「オブジェクト指向プログラミング」というと反射的に「カプセル化、ポリモーフィズム、継承」と答える人がいるようですが、これらはオブジェクト指向のための手段であって目的ではないと私は解釈しており、この記事でもその線に沿って説明しています。 |
現在はオブジェクト指向全盛で、「オブジェクト指向でなければプログラムでない」くらいのことを言う人も見かけます。
現に、主流である言語でオブジェクト指向をサポートしていないものと言われるとC言語とVB6くらいしか思いつきませんし、主流と言ってもこの2つはちょっと古い言語です。JavaもDelphiもC++もC#も、当然私たちのVBもオブジェクト指向プログラミングをサポートしている言語です。
言語レベルでオブジェクト指向をサポートしているものをオブジェクト指向言語と呼びます。 |
このような状況ですから、オブジェクト指向でいけば絶対間違いがないんだと頭から思い込んでしまうのも無理はないと思いますが、これを読んでいる皆さんはオブジェクト指向が必ず正しいわけではないということは覚えておいてください。
時には昔ながらの泥臭い上から下へ流れていくだけのプログラムが有効な場合がありますし、妙にオブジェクト指向に拘泥しているよりは、多少非オブジェクト指向的な要素を取り入れた方が断然スムーズにプログラムできる場合も多いです。
重要なのはオブジェクト指向とは何か?どう便利なのか?ということをきちんと押さえて、場合によって自分の判断でいろいろなスタンスを使い分けできるようになることでしょう。
とはいえ、オブジェクト指向はプログラムの歴史上かつてないほど強力な考え方で、非常に大きな可能性を持っています。
次回からは具体例を交えてオブジェクト指向を説明していく予定です。どうぞ、みなさんオブジェクト指向で快適な生活を送りましょう。ご意見・つっこみなどはメール・掲示板にてお願いいたします。