オブジェクトの広場はオージス総研グループのエンジニアによる技術発表サイトです

オブジェクト指向

オブジェクト指向技術の基本概念

1997年公開

  1. ひとことでいうと・・・
  2. クラスとインスタンス、アイデンティティ
  3. カプセル化(情報隠蔽)
  4. 継承(インヘリタンス)
  5. ポリモフィズム(多相性)

1. ひとことでいうと・・・

(a) 日常の仕事の仕方

会社の中での仕事の様子を考えてみましょう。Aさんが明日の会議でプレゼンする企画書を書いています。Aさんは新しい商品のアイデアを出すのは得意ですが、それをわかりやすい図にまとめるのは苦手です。そこである程度アイデアが出たところで、それを口頭でイラストレータのBさんに説明し、実際の図は彼女に描いてもらいます。Bさんは適切な図を数枚仕上げるとそれをAさんに戻します。Aさんは自分の書いた文章とBさんに描いてもらった図をまとめて文書に仕上げると、こんどはそれを上司のCさんに見せて承認をもらいます。するAさんはこの承認済みの企画書を明日の会議のコーディネータであるDさんに提出します。Dさんはそれを新人のEくんに手渡して参加人数分のコピーを依頼します。これで明日の会議の準備は整いました。

このように、世の中での様々な仕事は、複数の人がいっしょになって、共同でその解決に当たります。そのとき、次のような原則があることがわかるでしょう。自分にできることは自分でする。自分には無理なことや向いていない仕事は、それが得意な人や専門の人に依頼して代行してもらう。こうした処理の実行と依頼のチェインがより大きなより複雑な仕事の実行を可能にしていることがわかります。

こうしたさまざまな役割をもった仕事の実行主体が役割分担して問題解決に当たります。問題を意味のまとまりごとに分割し、その処理単位ごとに適切な実行主体を探してそこに実行を依頼するのです。依頼される側は、処理を実行してその結果を依頼者に返します。もしその実行主体の手に負えなければ、自分でできる分は実行し、残りの仕事をさらに別の実行主体に依頼してもよいわけです。こうして複数の実行主体が協調しながら、自分の解決できる部分を局所的に処理していき、結果的に全体として問題が解決されるというやり方です。

(b) オブジェクトどうしのメッセージのやり取り

オブジェクト指向では、今の比喩でいう仕事の実行主体のことをオブジェクトと呼んでいます。複数のオブジェクトが相互作用しながら問題解決を行うような形にソフトウェアの構造を作り上げていくのがオブジェクト指向の作法です。このようにオブジェクト指向は日常わたしたちが現実におこなっている仕事のやり方をモデルにして、ソフトウェアを組み立てていこうというものです。

このとき各オブジェクトはその業務の領域において1つ1つ明確に区別された主体として識別されています。そして各オブジェクトには、それぞれの仕事の責任分担(これをリスポンシビリティといいます)が割当てられています。いいかえると、この責任分担がそのオブジェクトのもつべき知識や作業能力を表しています。

ですから、オブジェクトは、自分自身の責任担当の仕事を実施するのに必要な知識や情報や(データ)と仕事の処理に必要な業務ルール(アルゴリズムや処理手順、ソフトウェアでいうところのアルゴリズム)を内部にもっています。オブジェクトは自身が保持しているこれらの知識やルールを使ってできる範囲のことを実行します。たとえば、イラストレータのBさんはペンや絵の具や定規といった絵を描くための様々な道具をリソースとしてもっていると同時にイラストの描き方のノウハウを業務ルールとして保持しているということになります。

またオブジェクトは、自分の責任分担で手に負えないことは、他のオブジェクトに依頼して実行してもらいますが、この依頼はメッセージを送信することによって行います。メッセージは、依頼したい仕事の種類(これをサービスと呼びます)とその仕事の実施に必要な情報(メッセージのパラメータ)からなります。さきほどの例で言うと、「文書をコピーする」がサービスの種類で、「オリジナルの文書」や「コピー部数」がメッセージのパラメータということになります。これはいってみれば作業の下請けへの依頼ということになります。

また「文書のコピー」サービスにおいては、新人のE君がサービスを提供する「サーバ」の役割を演じ、コピーを依頼している会議コーディネータのEさんがそのクライアントということになります。こうしていわゆるクライアント-サーバの関係がオブジェクト指向においても成り立つことになります。オブジェクト指向の場合、通常のクライアント-サーバの関係と異なる点は、同じオブジェクトが必要に応じてサーバになったりクライアントになったりして、役割を演じ分けることができるということです。会議コーディネータのEさんは会議実施に関してはサーバとして振る舞いますが、その際に必要な文書の準備のために文書のコピーを依頼するときには、クライアントとして振る舞っているのです。

いままでよくオブジェクトの説明をするにあたって、「オブジェクトとはその問題領域に登場するモノのことである」という言い方がされてきましたが、この言い方は少し不適切であることがわかります。仕事の一部を責任をもって遂行できる実行主体をオブジェクトというわけですから、モノそのものではなく、モノを管理する責任者とモノとをいっしょにしてオブジェクトと考えるわけです。つまり、コピーマシンそのものではなく、そのコピーマシンを使って原稿のコピーをこなす「原稿コピー係」をモデル化したオブジェクトを考えるのです。一種の擬人化といってもよいでしょう。

その際、仕事の性格に応じてオブジェクトを3つに分類して考えるのが普通です。外界とのやり取りに責任をもつインターフェースオブジェクト、仕事の割り振りをしたり複数のオブジェクトからの情報をもとに処理を実行したりするコントロールオブジェクト、問題領域に存在するモノや知識を資源として保持管理するためのドメインオブジェクトの3つが重要なオブジェクトのカテゴリです。たとえば、営業マンや受付係などはインターフェースオブジェクトとしてモデル化されますし、会議のコーディネータはコントロールオブジェクトの典型例です。イラストやデザインに関する知識をサービスしてくれるイラクトレータや企画マンはドメインオブジェクトということになります。現実の世界では、これらのオブジェクトのカテゴリはだぶって実現されることも多いので、オブジェクト指向でモデル化する際にはそのだぶりを解きほぐすことも分析フェイズにおける重要な仕事になります。同じAさんが企画も原稿コピーも会議のコーディネートも全部ひとりで行っている場合でも、企画オブジェクトと原稿作成オブジェクトと会議運営オブジェクトという具合に、Aさんの担当している役割や業務責任に着目したオブジェクトの抽出がオブジェクト指向モデルにもとづく分析設計のキーポイントになります。

(c) オブジェクト指向の定式化

それではここで、オブジェクト指向の考え方を定式化してみます。ニコラス・ヴィルトが、「プログラム = アルゴリズム + データ構造」といったのをもじっていえば、「オブジェクト = オブジェクトアイデンティティ + カプセル化 + クラス継承」といえるでしょう。そしてそれらのオブジェクトの集合から構成されたものがオブジェクト指向システムということになります。オブジェクト指向システムの内部では各オブジェクトがメッセージをやり取りしながら処理が進んでいきます。それではこれから、この定式化の意味するところをみていきましょう。

2. クラスとインスタンス、アイデンティティ

私たちは、なにかモノを認識するときに、「これは犬だ」というように「<具体的な対象>は<概念>だ」という形式を用いています。たとえば、家で飼っているポチやタロウを指して、それが猫でも狐でもなく「犬」というカテゴリに入るモノだというわけです。このようなとき、オブジェクト指向では、個々の具体的な対象(この場合ポチやタロウ)をインスタンスオブジェクト、それらの共通性をまとめあげた概念の方をクラスオブジェクトと呼んで区別します。ただし通常は、それぞれを簡単に、インスタンスとクラスといって済ませることが普通です。

このとき、各インスタンスはそれぞれ別々のオブジェクトとして確実に区別されていることに注意して下さい。つまりたとえすべての性質が同じオブジェクトが2つ存在してもきちんとそれらを区別するオブジェクト識別性が備わっているということです。この性質を、オブジェクトはアイデンティティをもつといったりします。これはデータベースの関係型(リレーショナル)モデルの考え方にはなかったものです。すべての属性項目が一致していればそのレコード(タプル)は同一物であるというのが関係データベースの世界ですが、オブジェクト指向の世界では全く同じ属性値をもつ複数のオブジェクトが存在できます。この性質を利用して、複数のオブジェクトを組み合わせてさらに複雑な構造のオブジェクトを作り出したり、あるオブジェクトのコピーを作って分散した各サイトに配置するといったことも簡単にできるようになります。
クラスは個々のオブジェクトに対するテンプレートすなわちひな形の役割をすると考えることができます。ですからクラスには、そのクラスに属する各インスタンスオブジェクトが共通にもつ属性項目の集合と手続きの集合がまとめて記述されます。

3. カプセル化(情報隠蔽)

カプセル化とは、データとそれらを操作する手続き群とをひとまとめにしてパッケージとしたものです。外部からこれらのデータや手続きに直接アクセスすることはできません。それらを利用するための外部インターフェースとして定義された特別な操作を使ってのアクセスだけが許されているのです。ですから、データがかってに書き換えられたりすることはなくなります。必ず定められたインターフェースを通してでないと内部の情報に対する読み書きはできない仕組みです。これを情報隠蔽と呼ぶこともあります。ですから式で表すと「カプセル化 = データ + 手続き + 外部インターフェース」といった感じになります。

例えば、原稿コピー係オブジェクトは、「コピーする(元原稿,部数)」「給紙する(枚数)」いう外部インターフェース操作とともにカプセル化されます。このオブジェクトには、この2つの操作を通してしかアクセスできません。オブジェクト指向の場合、外部インターフェースとはそのオブジェクトが受付け可能なメッセージの集合に他なりません。そのオブジェクトに対してメッセージを送信することで、そのオブジェクトのもつデータや手続きが利用可能になるのです。カプセル化の利点は、クライアントの利用を制限する外部インターフェイス部とオブジェクトの内部処理用のデータと手続きという実装部を分けて考えることから生じています。これによって、利用者に影響を与えずに内部の実装を替えることができるようになります。利用者は外部インターフェースというプロトコルさえ守っていれば将来もそのオブジェクトの利用方法とその結果の同一性を保証されます。また、インターフェースをみるだけでそのオブジェクトの機能や役割を内部の実装に立ち入らずに理解できるという意味で、システムの理解容易性が高まるという利点もあります。たとえば、原稿コピー係オブジェクトの実現方式として、(a)ワープロで再度入力し印字出力する、(b)青焼きコピーをとる、(c)ゼロックスコピーをとる、(d)コピーサービス会社にさらに下請けに出す、等といろいろあります。そのどれで実現されるにせよ、このサービスを利用したい人(クライアント)は、この2つの基本操作さえ知っていれば済むわけです。もし実現手段が変更になったとしても基本的に利用者は影響を受けません。

4. 継承(インヘリタンス)

人間はものごとを理解するときに、それらを分類することによって体系化しようとします。たとえば動物をその姿形や生態、行動様式の差異と共通性にもとづいて、鳥類、爬虫類、哺乳類と分け、さらに哺乳類をヒト、サル、イヌ、等と細分していきます。こうすることで、動物全体の中での位置づけが明確になり、他の動物と何が共通で何が異なっているのかがはっきりします。

オブジェクト指向でもクラスを単位として、問題領域に登場するさまざまなオブジェクトを分類していきます。こうすると上位のクラスには共通の属性や手続きが集められ、下位のクラスにはそのクラスに独自の特殊な属性や手続きが記述されることになります。ある特定のクラスからみてその上位にあるクラスを「スーパークラス」、逆に下位にあるクラスを「サブクラス」と呼びます。これらは相対的な概念です。
ここで重要なことは、下位のクラスは上位のクラスから性質を受け継ぐ、つまり「継承」するということです。たとえば、「ヒト」クラスはその上位の「哺乳類」クラスから「哺乳のための乳房」という属性や「卵ではなく直接子を産み落とす」という行動パターンを継承します。ですからこれらの情報は「ヒト」クラスに書かなくとも済んでしまいます。さらに「ヒト」は「動物」という間接的な上位クラスからも「自律移動できる」といった性質を継承します。ですから「ヒト」クラスには人間に独自の「直立2足歩行する」「言語を話す」といった性質のみ新たに定義すればよいのです。こうしてクラス階層ができていきます。

このようなクラス階層を作ることの利点は2つあります。1つは、そのオブジェクトに本当に特徴的なことだけがそのクラスに記述されるということです。他のクラスのオブジェクトと共通の性質までそこに記載する必要が無くなります。これによって、プログラムコードの冗長性をなくし、簡潔なシステム記述が行えます。

さらに、既に定義済みのクラスを用いてそれに新しい属性や手続きを追加定義することでサブクラスとして新たなクラスを作ることができます。既に定義済みの属性や手続きは継承して流用できるからです。こうのように、継承の概念は、ソフトウェアの再利用性を確保する役目を果たし、生産性の向上に役立ちます。標準的なクラス群を部品として用意しておき、利用する側ではそのサブクラスを独自に定義することで柔軟に再利用が可能になるからです。

また、これは特に分析において大事なことですが、ある一定の問題領域をそこに登場するクラスの体系として示すことができるので、あるシステムを構築する際の大局的な問題把握に役立ちます。ですから、分析モデル自体の再利用を促す上でもクラス階層は重要です。

5. ポリモフィズム(多相性)

このポリモルフィズムという言葉は聞き慣れないと思いますが、私たちが言語表現を使いこなす上で普通に行っている比喩の概念に相当します。例えば、「イヌが走る」というのと「ヒトが走る」というのでは、一方は4本足を使い一方は2本足で走りますから、いわゆる走る行為の実現手段(ソフトウェアでいう実装のこと)は異なります。しかし私たちはそれら2つの「走る」に同一の意味を見いだして同一の表現を比喩的に用いているわけです。このとき、「走る」という表現はポリモルフィズム(多相性、多義性)をもつといいます。

このポリモルフィズムをソフトウェアに導入すると、人間にとってわかりやすいシステム記述が行えます。実装はさまざま異なる複数のオブジェクトに対して同一のメッセージで対応できることになるからです。同じメッセージを受け取った各オブジェクトの側でそのメッセージを解釈し自分に合った手続きを選択して実行することになります。結果としてオブジェクトによって異なる手続きが実行されることになるわけですが、呼び出す側は同一のプロトコル(メッセージ)を通して利用することができるわけです。

このポリモルフィズムがうまく働くようにシステムの構成要素である各クラスのプロトコルを定義しておくと、新たなクラスを追加してもその影響を吸収できるシステム拡張性のあるソフトウェア構成ができます。たとえば、マルチメディアを表現するためのいくつかのデータを考えてみましょう。テキストやイメージ、構造化グラフィクスといったデータがあります。これらのデータを画面上で移動するmoveという処理を例にします。

通常の手続き指向の記述では、1つmoveという手続きを作って、その中で入力データの型を判別するcase文かif文で分岐し、それぞれのブロックで各データ専用の手続きを呼び出します。例えば、テキストとわかればテキスト用の移動ルーチンtext-moveを起動するといった具合です。

これをオブジェクト指向で記述すると、各マルチメディアデータごとにクラスが独立して定義され、さきほどカプセル化のところで説明したように、そのデータに関連する手続きも併せてこのオブジェクトの中に封じ込められます。ですから手続き指向の記述でひとつのルーチンにまとめられていた各手続きが、対応するデータごとに分散配置された状態といってよいでしょう。こうしておくと、これらの異なるマルチメディアオブジェクトを利用するクライアント側では、どのオブジェクトに対しても同一のメッセージmoveを送れば、その実装にわずらわされることなく移動を行えるわけです。

この利点は、新たにデータを追加する際に顕著になります。ここで、新しく「ビデオ」データを導入しましょう。すると手続き指向では、既存のプログラムに手を入れる必要が出てきます。if文を追加してビデオデータ用のルーチン呼び出しを挿入しなければなりません。この際にバグが混入する可能性は否定できませんから、システム拡張に関する信頼性は落ちます。またモジュールの再コンパイル・リンクといった手間も発生し簡単に部品を追加するようにはいきません。それに対して、オブジェクト指向では、ビデオオブジェクト用のクラスを定義してやって、そこにビデオデータもビデオ操作用の手続きもカプセル化します。ですから、1個オブジェクトがシステムに追加になるだけで既存のコードには一切触れません。さらに重要なことは、こうして追加するオブジェクトのプロトコルを既存のマルチメディアオブジェクトと共通にしておけば、ポリモルフィズムが働いて、これらのオブジェクトは共通のメッセージで操作できますから、クライアント側のプログラムも一切変更せずに済むという点です。このような柔軟なシステム拡張性をポリモルフィズムは提供してくれます。このことからソフトウェアの部品化とそれを柔軟に組み合わせる方式を提供するコンポーネントウェアにとっていかにこのポリモルフィズムが重要かがおわかりいただけたと思います。