コンテンツにスキップ

「オブジェクト指向プログラミング」の版間の差分

出典: フリー百科事典『ウィキペディア(Wikipedia)』
削除された内容 追加された内容
編集の要約なし
(同じ利用者による、間の24版が非表示)
1行目: 1行目:
{{複数の問題
|独自研究=2018年2月
|正確性=2019年2月
|出典の明記=2019年2月
}}


{{プログラミング言語|index=おふしえくとしこうふろくらみんく}}
{{プログラミング言語|index=おふしえくとしこうふろくらみんく}}
[[ファイル:History of object-oriented programming languages.svg|サムネイル|OOP言語の系譜(水色がOOP)|代替文=|280x280ピクセル]]
[[ファイル:History of object-oriented programming languages.svg|サムネイル|OOP言語の系譜(水色がOOP)|代替文=|280x280ピクセル]]
'''オブジェクト指向プログラミング'''(オブジェクトしこうプログラミング、{{Lang-en-short|object-oriented programming}}; OOP)は、[[オブジェクト指向]]の[[プログラミングパラダイム|考え方]]<ref>コンピュータ・プログラミングのパラダイムについては『新しいプログラミング・パラダイム』などを参照: http://www.kyoritsu-pub.co.jp/bookdetail/9784320024939</ref>を取り入れた[[プログラミング (コンピュータ)|コンピュータ・プログラミング]]法である。プログラムを「[[オブジェクト (プログラミング)|オブジェクト]]」の集合体して構築し、オブジェクトはデータ(状態を主し、コード(動作を従として構成され、無数オブジェクトが相互に作用しうようにして任意の処理が実行される。オブジェクトとは対象物を意味し、CPU演算処理(コード)が参照ないし変動の対象とすデータ全般と考える事出来る。「どデータを扱うか」ではなく「データどの様に扱われか」を念頭置いてプログラムを組み立てる手法がOOPという事になる。
'''オブジェクト指向プログラミング'''(オブジェクトしこうプログラミング、{{Lang-en-short|''object-oriented programming''}}、略語:OOP)は、[[オブジェクト指向]]の[[プログラミングパラダイム|考え方]]<ref>コンピュータ・プログラミングのパラダイムについては『新しいプログラミング・パラダイム』などを参照: http://www.kyoritsu-pub.co.jp/bookdetail/9784320024939</ref>を取り入れた[[プログラミング (コンピュータ)|コンピュータ・プログラミング]]法である。[[オブジェクト (プログラミング)|オブジェクト]]とは大まかに言うとデータ([[変数 (プログラミング)|変数]]または[[プロパティ (プログラミング)|プロパティ]])とコード([[関数 (プログラミング)|関数]]または[[メソッド (計算機科学)|メソッド]])のを意味しるが、そ詳細ついて様々解釈存在す。OOP基づくプログラムはこの[[オブジェクト (プログラミング)|オブジェクト]]の集合中心にして組み立てられる事になるが、その実装スタイルもまた千差万別である。


OOPの成り立ちにもまた諸説ある。オブジェクト指向プログラミングという言葉自体は、計算機科学者[[アラン・ケイ]]が1972年に[[Smalltalk]]の言語設計を説明する中で初めて生み出されている。なお、データとコードの複合体である[[オブジェクト (プログラミング)|オブジェクト]]という用語を確立したのは1967年公開の[[Simula|Simula67]]であった。[[Simula|Simula67]]の[[クラス (コンピュータ)|クラス]]と[[オブジェクト (プログラミング)|オブジェクト]]の設計及び用法は[[手続き型プログラミング]]の機能拡張に近いものであったが、こちらもOOPの草分けと見なされるようになった。[[Smalltalk]]のプログラム内のあらゆる要素をオブジェクトとして扱い、[[メッセージパッシング]]で相互作用させる本来の意味でのオブジェクト指向設計は、哲学的かつ理論偏重のきらいがあったので実践面ではさほど広まらなかった。1983年に公開された[[C++]]が契機となり、OOPはまた違った角度から注目されるようになった。最終的にこの[[C++]]の設計スタイルが物議を醸しながらもOOPの主流となるに到り、同時にOOPの三大原則とされる[[カプセル化]]、[[継承 (プログラミング)|継承]]、[[ポリモーフィズム|多態性]]の[[プログラミングパラダイム]]が確立されている。
オブジェクト指向プログラミングは、ソフトウェアの大規模化に伴い、より効率的な開発手法が模索される中で1960年代から70年代にかけて確立された。OOP言語は歴史的にクラス仕様を中心とする[[Simula]]系統と、メッセージ仕様を主体とする[[Smalltalk]]系統に分けられるが、後に[[C++]]や[[Java]]などの言語の派生元となった前者が主流となった。この前者のオブジェクト指向が示す「[[カプセル化]]」「[[継承 (プログラミング)|継承]]」「[[ポリモーフィズム|多態性]]」の三大原則を備えていれば、OOP言語であると見なされる。


== 特徴 ==
== 特徴 ==
プログラミングパラダイムとしてのオブジェクト指向の確立は紆余曲折を経ており(後述)その詳細の解釈も様々であるが、一定の枠組みとなる三つの原則仕様(''fundamental principle'')が存在する。この三原則に従った言語仕様を総体的または部分的に備えたプログラミング言語がオブジェクト指向準拠と判別される。1~3はいわゆるOOPの三大原則とされるものであり、[[C++]]を契機にして確立された。4はオブジェクト指向プログラミングという用語の発端となった[[Smalltalk]]設計の主軸であるが、現在では亜流となっている。
OOPの基本原則(''fundamental principle'')仕様は以下の通りである。1~3は[[Simula]]系統であるクラス主体(''classes-principled'')OOPの三大原則となっている。4は[[Smalltalk]]系統であるメッセージ主体(''messaging-principled'')OOPの主要原則とされる。


#[[カプセル化]] (''encapsulation'') - <small>内部隠蔽</small>
#[[カプセル化]]''encapsulation''
#[[継承 (プログラミング)|継承]] (''inheritance'')
#[[継承 (プログラミング)|継承]]''inheritance''
#[[ポリモーフィズム|多態性]] (''polymorphism'')
#[[ポリモーフィズム|多態性]]''polymorphism''
#*アドホック多態性(''ad hoc polymorphism'')
#*[[多重定義]] (''overloading'') - <small>演算子オーバーローディングも含む</small>
#**[[多重定義|関数オーバーロード]](''function overloading'')
#*{{仮リンク|パラメータ多相|en|Parametric polymorphism}} (''parametric polymorphism'') - <small>[[ジェネリックプログラミング]]</small>
#**[[多重定義|演算子オーバーロード]](''operator overloading'')
#*[[派生型|派生型付け]] (''subtyping'') - <small>動的なもののみ</small>
#*パラメータ多態性(''parameter polymorphism'')
#*[[仮想関数]] (''virtual function'')
#**[[ジェネリックプログラミング|ジェネリック関数]](''generic function'')
#*[[多重ディスパッチ]] (''multiple dispatch'')
#[[メッセー (コンピュータ)|メセージパッシング]] (''message passing'')
#**[[ジェネリクプログラミング]]''generic programming''
#*[[派生型|サブタイプ多態性]](''subtyping'')
#**[[仮想関数]]''virtual function''
#**動的ディスパッチ(''dynamic dispatch'')
#**[[ダブルディスパッチ]](''double dispatch'')
#**[[多重ディスパッチ]]''multiple dispatch''
#[[メッセージ (コンピュータ)|メッセージパッシング]](''message passing'')


; カプセル化
; カプセル化


従来の手続き型プログラミング環境では、動作を定義する'''コード'''が、状態を表現する'''データ'''を扱う形でプログラムを記述するのが一般的だったが、開発規模の拡大に伴い解決の難しいソフトウェア・バグ原因の大半はデータの予期せぬ変動各データ間の不整合である事が経験則で知られるようになって来た。そこでデータを中心にしてプログラムを組み立てようとする考え方が生まれ、データそれに付随するコード群をまとめた'''オブジェクト'''というプログラミング概念編み出された。従来の「コードの為のデータ」と「データの為のコード」としは、データを変動させたコード位置を容易に特定出来るようにしてバグ発見に繋げる為であった。この目的沿って特定のデータを扱えるコード範囲を厳密に定めた'''内部隠蔽'''の機能は、アクセス範囲外からのデータ参照とコード呼出をコンパイルレベルで禁止して想定外のデータ変動をもたらす人的ミスを減らした。
従来のプログラミング環境では、動作を定義するコード(関数)が、状態を表現するデータ(変数)を扱う形でプログラムを記述するのが一般的だったが、開発規模の拡大に伴い解決の難しいソフトウェア・バグ原因の大半はデータの予期せぬ変動と各データ間の不整合である事が経験則で知られるようになって来た。そこでデータを中心にしてプログラムを組み立てようとする考え方が生まれ、その目的に沿うものとしてデータとコードの複合体であるオブジェクトに白羽の矢立てられた。オブジェクト内のデータは基本的同じオブジェクト内のコードだけが参照または変動可能とする仕組みが考案されこれがカプセル化と呼ばれた。データを変動させたコード位置の把握を容易にる為に、各データにアクセス出来るコード範囲を厳密に定めたカプセル化の機能は、範囲外アクセスをコンパイルレベルで禁止して想定外のデータ変動をもたらす人的ミスを減らした。また、データを変動させるコードを呼び出すそのまたコード位置の把握も芋蔓式に必要となったのでコードにもアクセス可能範囲が定められる様になり、更にアクセス範囲の広さにも段階が設けられたので特定の外部をも含んだ柔軟な内部隠蔽の設計が可能となった。


; 継承
; 継承


また、プログラム内の膨大な数のデータは通常グループ化(構造体)されて扱われていたが、構造体間にまたがる共通のデータ集合が多数出現し、その冗長と整合の頻雑さの問題が浮き彫りとなっていた。これを解決する為にOOPは、構造体=オブジェクトを複数の'''階層要素'''に分け、共通のデータ集合を親階層とし、派生する子階層に親階層の参照アドレスを持たせて連結する構造とした。A階層から成る親オブジェクトから派生した子オブジェクトはA+B階層として構成され、これが'''継承'''と呼ばれた。コードから参照されたデータがB階層に無い時は、次のA階層に有るか探す仕組みとなり、この連鎖によって傍からは一つのオブジェクトとして存在した。なお、派生クラスで任意のデータとメソッドを追加出来る継承の機能、工夫次第でコードの再利用性を高めるとも見なされたが、深い継承がもたらす構成把握の難化がネックとなってほぼ否定される様になり、クラスライブラリ使用の範疇内に留まっている。{{いつ範囲|date=2019年2月|現在}}では深い継承は回避される様になり、代わりに共通メソッド呼出テーブル(Javaのinterfaceなど)を実装させる横に長い継承が主流となっている。また、Simula系統のOOP言語では多態性を実現する為の重要な手段として用いられている。
プログラム内の膨大な数のデータは通常グループ化(構造体)されて扱われていたが、複数の構造体間にまたがる共通のデータ集合がく目に付くようになり、その冗長と整合の問題が浮き彫りとなっていた。これを解決する為に構造体=オブジェクトを複数の'''階層要素'''に分け、共通のデータ集合を親階層とし、派生する子階層に親階層の参照アドレスを持たせて連結する構造が考案された。A階層から成る親オブジェクトから派生した子オブジェクトはA+B階層として構成され、これが継承と呼ばれた。コードから参照されたデータがB階層に無い時は、次のA階層に有るか探す仕組みとなり、この連鎖によって傍からは一つのオブジェクトとして存在した。なお、派生クラスで任意の機能を追加出来る継承は従来コードの再利用性を高めるとも考えられたが、深い継承がもたらす構成把握の難化が問題視されてほぼ否定された。これはクラスライブラリ使用内に留まっている。{{いつ範囲|date=2019年2月|現在}}では深い継承は倦厭される様になり、代わりに仮想メソッドテーブル(Javaの''interface''など)を実装させる横に長い継承が重視されている。また、クラスメカニズムをベースとするOOP言語では多態性を実現する為の手段として用いられている。


; 多態性
; 多態性


アドホック多態性は単にソースコードの記述を一部自動化するものである。'''関数オーバーロード'''は引数の並び方によって同じ名前のメソッドをコンパイル時に自動的に差別化する機能である。'''演算子オーバーロード'''は、扱う数値の型に従って宣言された演算記号を関数名と見なすようにし、単項演算子なら右の数値を第一引数とし、二項演算子なら左右の数値をそれぞれ第一第二引数として関数呼び出しのコードが生成されるという仕組みだった。これらは静的な多態性とされる。
様々なオブジェクトタイプ(型)が扱われるソースコード記述の頻雑性を回避する為に、呼出引数の型に応じたメソッドをコンパイル時に選択する'''多重定義'''と、型引数に応じたクラスorメソッドをコンパイル時に生成する'''パラメータ多相'''の機能が備えられた。これらは静的な多態性(''static polymorphism'')とも呼ばれる。動的な多態性(''dynamic polymorphism'')としては、特定の場面で扱われるオブジェクト群が共通して持つ階層要素の部分で一括りにして順次処理を行い、条件分岐をより柔軟かつ簡素にする目的で、特定の階層のコードの存在を抽象化して呼び出しポイントとし、オブジェクト共通の階層要素が持つ呼び出しポイントから、それぞれ異なる派生先階層の実装コードを呼び出すという'''仮想関数'''の仕組みが実装された。また、オブジェクトが特定の階層要素を持つかチェックした上でその階層に準じた動的な'''派生型付け'''を行い、その型変化させたオブジェクトを引数にした多重定義と仮想関数呼び出しの併せ技である'''多重ディスパッチ'''の機能も編み出され、データがコードを制御するというOOPの理念は明確に表現された。

パラメータ多態性もソースコードの記述を一部自動化するものである。関数内またはクラス内の型指定部分をワイルドカードにして宣言しておき、ソースコード内で関数またはクラスが実装記述されると、その具体的な型指定を関数内またはクラス内のワイルドカードに当てはめたコード部分がコンパイル時に自動生成されるという機能だった。前者は'''ジェネリック関数'''と呼ばれ、後者はより広い範囲を扱う事から'''ジェネリックプログラミング'''と名付けられた。これらも静的な多態性に位置付けられている。

サブタイプ多態性は動的なものである。最も初期のOOPであるSimula67は、シミュレーション内で扱う多種多様なオブジェクトを継承によって体系化したが、コード部分の細かな違いは共通スーパークラスに属する共通プロシージャ内の分岐フローで処理していた。サブクラスの数だけ分岐構文が増える頻雑さを解消する為に、共通プロシージャをただの住所テーブルにしてサブクラスの実装時に同名プロシージャのアドレスを収納させ、呼び出し時はそのアドレスへジャンプするという機能が考案され、これが'''仮想関数'''となった。'''動的ディスパッチ'''はSmalltalkのオブジェクト設計に由来するものであり、その実装の仕方は様々でやや曖昧な仕様でもある。メッセージを受け取ったレシーバーがオブジェクト内部で動的な状態に従い動的な処理を行って結果を返すというランタイム環境上のプロセスが後に動的ディスパッチのカテゴリで括られた。遠隔プロセスを扱うリモートメソッドのシステムも動的ディスパッチに該当するものである。'''多重ディスパッチ'''は動的な関数オーバーロードに近いものである。関数コール時または関数ブロック内で、それぞれの引数が動的に型審査されて型変化(''dynamic casting'')された後に、その引数パターンに対応した同名関数または分岐ルーチンに処理が移行されるという動的変化プロセスを指した。'''ダブルディスパッチ'''は多重ディスパッチの亜流またはその派生物であり、二通りの考え方がある。動的型審査および型変化されるBオブジェクトを単一引数にしてAオブジェクトの仮想関数メソッドを呼び出す形態と、多重ディスパッチに用いる引数を二つに限定した形態である。いずれも実行時状態に応じた動的変化プロセスとなった。これは主にデータ集合を対象にして分類、解析、作用といった処理を連続的または再帰的に行うアルゴリズムで用いられた。


; メッセージパッシング
; メッセージパッシング


上述の三つの機能と異なり、メッセージパッシングオブジェクト指向そものと肩を並べるパラダイムであり、オブジェクト指向が示す機能の大半を結果的に実現出来る「[[タデータ|メタ]]」的な土台となった。これは従来のサブルーチン形態変えので、基本はバイトデータ('''メッセージ''')の送受信でメソッド名とパラメータ値およびリターン値をやり取りする仕組みだった。オブジェクトのレシーバー関数引数として渡されたメッセージを読み込み、オブジェクト内部でそれに準じた処理を行い、結果をリターンした。レシの仕組みは結果的に「内部隠蔽」を実現出来オブジェクト内部での自由自在な処理実装るリターンはこれも結果的「多態性」実現出来た。この実装をサポートする為「[[リフレクション (情報工学)|リション]]」機能が備えられた。メッセージパッシングがもたらす変幻自在なメソッドの選択実行は、通信網介し他サバー上で実行れていビスプロセス自身のソフトウェアの一部として扱う様事も可能にした。あらゆる手法に応用出来るメッセージッシングは、適用範囲広げほど開発工数と実行負荷増大するので、決してオールマイティに用いられるのではないが、ソフトウェアモジュール化およびコンポーネント化をよりダイナミックに、そしてグローバル発展させる潜在力を秘めており、OOPがもたらす実装の拡張性を大きく広げた。
これ従来のパラメータ付きサブルーチン呼出とリタン値やり取り別の視点から再解釈したのであり、基本はバイトデータ(メッセージ)の送受信でセレクタ(メソッド名とパラメータ、そしてリターン値をやり取りする仕組みだった。オブジェクトのレシーバー(代表メソッド)が渡されたメッセージを解釈し、オブジェクト内部でそれに準じた処理を行い、結果をリターンした。パラメタとリタン値もまたオブジェクトだったの、リターン値をメッセージ内パラメータして後続のオブジェクトへ送事もでき、そのリターンまた別のメッセージ送る事も出来た。Smalltalk設計では真偽値、数値、コドブロックもオブジェクとなったので、真偽値または数値対して規定の予約語(セレクタ)と併せたコードブロックをメッセージとして送る事で条件分岐や反復処理といった制御ローを表現出来た。オブジェトを次々とメッセージとして送るは順次処理となった。メッセージパッシングは、オブジェクトにオブジェクト引き合わせ、双方に関連した手続きまたは制御フロを発生チンワーク意味しており、この連鎖的かつ再帰的繰り返がプログラム全体の流れとなった。この斬新なラダイムを実際のプロラムの形にする為に比較的緻密な設計が要求されまたパターン化されたプロセスにおいても柔軟さを維持する為ワンステップ必要とすので開発工数とCPU負荷増大する欠点もあった。これが倦厭されてOOP設計本質ありなが広くは支持さず、OOPに対す関心焦点は本来は枝葉の部分あるのクラスのメカニズム移る事なった。

Smalltalkの影響を受けた後継言語においても、あらゆるプログラム要素をメッセージで表現しようとする理論の追求は避けられ、例えば制御フローは制御構文で扱われている。またメッセージングに対する認識もシフトし、オブジェクトの代表となるメソッドがパラメータを受け取り多様な処理と移譲を行ういわゆるレシーバーの仕組み自体がメッセージングそのものと見なされるようになった。そのメカニズムを応用したリモートメソッドを実現するバイトデータの送受信もメッセージパッシングの代表例となった。この様にメッセージパッシング設計の本質は見失われながらも、その側面的仕様は数々のOOP言語に導入されてもいる。


== 歴史 ==
== 歴史 ==
58行目: 65行目:


== OOP言語一覧 ==
== OOP言語一覧 ==
オブジェクト指向を総体的または部分的にサポートする機能を備えたプログラミング言語の公開は、1980年代後半から顕著となった。オブジェクト指向プログラミングスタイルは、[[クラス (コンピュータ)|クラス仕様]]中心とする'''Simula系統'''と、[[メッセージパッシング|メッセージ仕様]]を主体とする'''Smalltalk系統'''二通りに分けられる。前者には「C++」「Java」「C#」などが、後者には「Smalltalk」「Objective-C」「Swift」などが挙げられる。
オブジェクト指向を総体的または部分的にサポートする機能を備えたプログラミング言語の公開は、1980年代後半から顕著となった。OOP言語分類法複数あるがSmalltalkルーツとするメッセージパッシングの構文が重視さか否かで大別される事が多いそうでないものがOOP言語の主流となっており「C++」「Java」「C#」「Swift」などがその代表とされる。メッセージパッシングを重視するOOP言語には「Smalltalk」「Objective-C」「Self」などがある。言語仕様の中でオブジェクト指向の存在感が比較的高い代表的なプログラミング言語を以下に列る。

オブジェクト指向プログラミング言語の形態には、オブジェクト指向と他の[[プログラミングパラダイム|パラダイム]]が同居している'''ハイブリッドOOP'''と、オブジェクト指向に基づくコードプロセスだけが許容される'''ピュアOOP'''の双方が存在する。前者には「C++」「Objective-C」「Java」「C#」「Swift」などが、後者には「Smalltalk」「Eiffel」「Self」などがある。

言語仕様の中でオブジェクト指向の存在感が比較的高い代表的なプログラミング言語を以下に列挙する。


[[Simula|'''Simula 67''']] 1967年
[[Simula|'''Simula 67''']] 1967年
: 1962年に公開された[[Simula]]の後継版であり、[[クラス (コンピュータ)|クラス仕様]]を導入した最初の言語。現実世界の擬似モデルを観測するシミュレーション・プログラム記述用に開発され、された。C++、Java、C#に代表される'''クラス主OOP'''のルーツされる
: 1962年に公開された[[Simula]]の後継版であり、[[クラス (コンピュータ)|クラス]]のプログラミング概念を導入した最初の言語である。現実世界の擬似モデルを観測するシミュレーション・プログラム制作用に開発されたものでクラスを実メモリに展開したオブジェクトは、その観測対象要素となった。simulaのクラスは、サブルーチン変数と補助プロシージャを加えた機能的小型モジュールに近いものであったが、継承と仮想関数という先進的な設計を備えていた事でOOPの草分けと見なされるようになった。C++、Java、C#の設計母体となった


'''[[Smalltalk]]''' 1980
'''[[Smalltalk]]''' 1972
: オブジェクト間の相互作用を担う[[メッセージパッシング|メッセージ仕様]]を導入した最初の言語。オブジェクト指向という言葉は、Smalltalk開発者がその言語仕様を説明する中で生み出された。'''メッセー主体OOP'''元祖であり、高度に柔軟なオブジェクトの連携動作を表現できた。また、後に仮想マシンと呼ばれる専用ランタイム環境も用意された。
:[[メッセージパッシング]]のプログラミング概念を導入した最初の言語。数値、真偽値から変数、構造体、コードブロック、メタデータまでのあらゆる要素をオブジェクトとする概念を編み出した最初の言語でもある。オブジェクト指向という言葉は、Smalltalk開発者がその言語設計を説明する中で生み出された。オブェクト基礎的振る舞いを規定する限られた予約語の他は、オブジェクトとメッセージやり取りで制御構造を含めたあらゆるプロセスを表現出来た。また、専用のランタイム環境上でプログラムを実行する設計を応用して動的な多態性とセキュリティに繋がるモニタリングも実現した。これは後に仮想マシンと呼ばれるとなり、JavaやC#に踏襲された。


'''[[C++]]''' 1983年
'''[[C++]]''' 1983年
: [[C言語]]にクラス主体OOPに基づく言語仕様を追加したもの。Simulaの影響を強く受けている。クラス仕様によってカプセル化、継承、多態性を表現している。また[[ジェネリックプログラミング|ジェネリクス]]に相当する[[テンプレート (プログラミング)|テンプレート機能]]も備えていた。元がC言語であるため、OOPから逸脱したコーディングも多用できる点が物議を醸したが、その是非はプログラマ次第であるという結論に落ち着いた。
: [[C言語]]にOOPデザインを追加したもの。Simulaの影響を受けている。[[クラス (コンピュータ)|クラス]]のメカニズムが備えられカプセル化、継承、多態性といったOOP仕様実装している。[[テンプレート (プログラミング)|テンプレート機能]]や[[例外処理]]、演算子オーバーロードを応用した関数オブジェクトなど様々なプログラミングパラダイム導入された。元がC言語であるため、OOPから逸脱したコーディングも多用できる点が物議を醸したが、その是非はプログラマ次第であるという結論に落ち着いた。


'''[[Objective-C]]''' 1984年
'''[[Objective-C]]''' 1984年
:[[C言語]]にメッセージ主体OOPに基づく言語仕様追加したもの。Smalltalkの影響を強く受けている。OOP的には前述のC++よりも正統であると見なされた。プログラミング・パラダイム共存しており、Smalltalkよりもコーディングし易くなった。
:[[C言語]]をOOPデザイン化したもの。こちらはSmalltalkの影響を受けており、それに準じた[[メッセージパッシング]]と[[リフレクション (情報工学)|リフレクション]]のメカニズムが備えられた。OOP的には前述のC++よりも正統であると見なされた。制御構文が追加され、メッセージ仕様やや簡素化されるなど実践上の便宜が図られており、Smalltalkよりもコーディングし易くなった。


'''[[Object Pascal]]''' 1986年
'''[[Object Pascal]]''' 1986年
:[[Pascal]]にクラス主体OOPに基づく言語仕様追加したもの。
:[[Pascal]]をOOPデザイン化したもの。


'''[[Eiffel]]''' 1986年
'''[[Eiffel]]''' 1986年
:[[Pascal]]をベースにしてクラス主体OOPに基づく言語仕様と[[ジェネリックプログラミング|ジェネリクス]]のパラダイムを追加した。型付けは静的に限られ、非参照データを自動解放する[[ガーベジコレクション|ガーベジコレクタ]]を持ち、多重継承時の問題を回避する仕組みや例外処理など高い堅牢性を備えた。これらは後のJavaやC#の手本となった。
:[[Pascal]]をベースにしてOOPデザイン化し、また[[ジェネリックプログラミング]]を追加した。型付けは静的に限られ、非参照データを自動解放する[[ガーベジコレクション|ガーベジコレクタ]]を持ち、多重継承時の問題を回避する仕組みや例外処理など高い堅牢性を備えた。これらは後のJavaやC#の手本となった。


'''[[Self]]''' 1987年
'''[[Self]]''' 1987年
: メッセージ主体OOPに分類される。従来の[[クラスベース]]に対して、システム側が用意したオブジェクトを複製して任意の拡張を施す[[プロトタイプベース]]を実装した。[[実行時コンパイラ]](just-in-time compiler)を備える専用ランタイム環境で実行された。
: メッセージパッシングの構文が中心となっている。動的な多態性を重視した言語であり、従来の[[クラスベース]]のオブジェクト設計に対して、システム側が用意したオブジェクトを複製して任意の拡張を施す[[プロトタイプベース]]のデザイン初めて実装した。Smalltalkと同様に専用のランタイム環境で実行されたが、これも実用面では初となる[[実行時コンパイラ]](''just-in-time compiler'')の機能が備えられて速度面でも画期的なもとなった。


'''[[CLOS]]''' 1988年
'''[[CLOS]]''' 1988年
:[[Common Lisp]]にメッセージ主体OOPに基づく言語仕様追加したもの。
:[[Common Lisp]]をOOPデザイン化したもの。


'''[[Python]]''' 1990年
'''[[Python]]''' 1990年
: クラス主体OOPに分類される。[[インタプリタ]]式で動作する。言語仕様を簡素化し自動メモリ管理機能を実装して扱いやすく理解しやすいOOPを目指している。後のOOPスクリプト言語の手本となった。
:[[インタプリタ]]式で動作する。言語仕様を簡素化し自動メモリ管理機能を実装して扱いやすく理解しやすいOOPを目指している。後のOOPスクリプト言語の手本となった。


'''[[Java]]''' 1995年
'''[[Java]]''' 1995年
:堅牢性と安全性を重視したクラス主体OOPである。仮想マシン上の実行、ガーベジコレクタ、例外処理などを採用し、多重継承ジェネリックを破棄したその埋め合わせの仕様も備えられているメッセージ主体OOP的設計を表現出来るシリアイゼーショやリフレション様も導入され。非常に整えられたハイブリッドOOP言語である。
:堅牢性と安全性を重視したOOP言語その二つの理念を実現する為に、仮想マシン上の実行、ガーベジコレクタ、例外処理などを採用し、ポインタと直アドレス変数、多重継承ジェネリックプログラミング、演算子オーバーロードなどを破棄した。破棄部分についてはその埋め合わせの設計も備えられ[[クス (コピュータ)|ラス]]メカニズムを中心にしたOOPであるが、々なプログラミングパラダイム追加されている。非常に整えられたハイブリッドOOP言語である。


'''[[Delphi]]''' 1995年
'''[[Delphi]]''' 1995年
:クラス主体OOPに分類される。[[Object Pascal]]を発展させたもので、データベースの操作プログラム開発などを主な用途とした。一時期Javaの対抗馬となった。
:[[Object Pascal]]を発展させたもので、データベースの操作プログラム開発などを主な用途とした。一時期Javaの対抗馬となった。


'''[[Ruby]]''' 1995年
'''[[Ruby]]''' 1995年
: クラス主体OOPに分類されスクリプト言語である。[[インタプリタ]]式で動作する。スクリプトでありながら、クラス、マルチスレッド、例外処理、そして[[ソフトウェアコンポーネント]](モジュール)を扱える[[Mixin]]といった利便性と汎用性の高い機能も備えている。
: OOPデザインされスクリプト言語である。[[インタプリタ]]式で動作する。スクリプトでありながら、クラス、マルチスレッド、例外処理、そして[[ソフトウェアコンポーネント]](モジュール)を扱える[[Mixin]]といった利便性の高い機能も備えている。


'''[[JavaScript]]''' 1996年
'''[[JavaScript]]''' 1996年
:[[ウェブアプリケーション]]開発を主な目的とするOOPスクリプト言語。主に[[プロトタイプベース]]でオブジェクトを扱う事でコーディングを簡便にしている。クラス主体OOPに分類される。[[ECMAScript]]として標準化されている。ECMAScript 2015ではクラス構文をサポートするようになった。
:[[ウェブアプリケーション]]開発を主な目的とするOOPスクリプト言語。主に[[プロトタイプベース]]でオブジェクトを扱う事でコーディングを簡便にしている。[[ECMAScript]]として標準化されている。ECMAScript 2015ではクラス構文をサポートするようになった。


[[C Sharp|'''C#''']] 2000年
[[C Sharp|'''C#''']] 2000年
:[[Java]]を強く意識して開発されたクラス主体OOP言語。[[.NET Framework]]などの[[共通言語基盤]]上で実行される。Javaと同等または部分的に拡張させた仕様を持ち、こちらもよく整えられたハイブリッドOOP言語として知られる。
:[[Java]]を強く意識して開発されたOOP言語。[[.NET Framework]]などの[[共通言語基盤]]上で実行される。Javaと同等または部分的に拡張させたスタイルを持ち、こちらもよく整えられたハイブリッドOOP言語として知られる。
[[Microsoft Visual Basic .NET|'''Visual Basic.NET''']] 2002年
[[Microsoft Visual Basic .NET|'''Visual Basic.NET''']] 2002年
: 従来の[[Microsoft Visual Basic|Visual Basic]]の構文ベースにつつ、クラス主体OOPに基づいて言語仕様が改変されている。[[.NET Framework]]などの[[共通言語基盤]]上で実行される。
:[[Microsoft Visual Basic|Visual Basic]]をOOPデザイン化たもの。[[.NET Framework]]などの[[共通言語基盤]]上で実行される。


'''[[Ceylon]]''' 2011年
'''[[Ceylon]]''' 2011年
:[[Java]]を元に開発され、その長所と短所を見直しつつ再設計されたOOP言語。Javaの改造版である。また[[JavaScript]]にもコンバートできる。
:[[Java]]を元に開発され、その長所と短所を見直しつつ再設計されたOOP言語。Javaの改造版である。また[[JavaScript]]にもコンバートできる。

'''[[Kotlin]]''' 2011年
:[[Javaバイトコード]]を出力し、[[Java仮想マシン]]上で動作するJVM互換のOOP言語。関数型プログラミング言語由来の機能を持つ。
[[Swift (プログラミング言語)|'''Swift''']] 2014年
[[Swift (プログラミング言語)|'''Swift''']] 2014年

:[[Objective-C]]の後継言語。クラス主体OOPとメッセージ主体OOPの中間に位置する。関数型プログラミング言語由来の機能など他のパラダイムも併せ持っている。
:高度に整えられたマルチパラダイムプログラミング言語。クラスのメカニズムをベースにしたオブジェクト指向的言語仕様も導入されている。


== OOP言語の仕組み ==
== OOP言語の仕組み ==
130行目: 133行目:
これらをどのように言語の要素として提供し、どのような[[機械語]]コードで実現するかによって様々な[[オブジェクト指向プログラミング言語]]のバリエーションが生まれる。以下、オブジェクト指向プログラミング言語が提供する様々な要素が上記の仕組みをどのように実現しているかについて概観する。
これらをどのように言語の要素として提供し、どのような[[機械語]]コードで実現するかによって様々な[[オブジェクト指向プログラミング言語]]のバリエーションが生まれる。以下、オブジェクト指向プログラミング言語が提供する様々な要素が上記の仕組みをどのように実現しているかについて概観する。


=== オブジェクトの概念と実 ===
=== オブジェクトの概念と実 ===
'''[[オブジェクト (プログラミング)|オブジェクト]]''' ({{Lang|en|object}}) はオブジェクト指向プログラミングの中心となる'''概念'''であり、この概念を'''実際'''にどう実現するかは[[オブジェクト指向プログラミング言語]]により異なる。
'''[[オブジェクト (プログラミング)|オブジェクト]]''' ({{Lang|en|object}}) はオブジェクト指向プログラミングの中心となる'''概念'''であり、この概念を'''実際'''にどう実現するかは[[オブジェクト指向プログラミング言語]]により異なる。


139行目: 142行目:
* 概念的には コードとデータが一つになっている。
* 概念的には コードとデータが一つになっている。


==== オブジェクト概念の実現方法 ====
==== オブジェクトの実装構造 ====
実際のプログラムでは、全てのオブジェクトが互いに全く異なった存在ではなくオブジェクトは種類に分けることが出来る。
実際のプログラムでは、全てのオブジェクトが互いに全く異なった存在ではなくオブジェクトは種類に分けることが出来る。


154行目: 157行目:
もう一つは同一種類のオブジェクトでもそれぞれ異なる部分、典型的には各オブジェクトが保持するデータ群である。
もう一つは同一種類のオブジェクトでもそれぞれ異なる部分、典型的には各オブジェクトが保持するデータ群である。


==== クラスオブジェクト ====
==== クラスオブジェクト ====
動的型付けを採用するオブジェクト指向言語の多くは、クラスより生成するインスタンスの他に[[メタクラス]]という機能を持ちクラス自体をオブジェクトとして扱うことが出来る。このためオブジェクトには、インスタンスオブジェクトとクラスオブジェクトという2種類のオブジェクトが存在する。Java等クラスオブジェクトを持たない言語の文化圏では、インスタンスオブジェクトとオブジェクトを混同して説明される事があるが、Objective-CやPython、Ruby等、インスタンスオブジェクトとクラスオブジェクトが別であるオブジェクト指向言語では区別して説明される。<ref>Objective-Cプログラミ
動的型付けを採用するオブジェクト指向言語の多くは、クラスより生成するインスタンスの他に[[メタクラス]]という機能を持ちクラス自体をオブジェクトとして扱うことが出来る。このためオブジェクトには、インスタンスオブジェクトとクラスオブジェクトという2種類のオブジェクトが存在する。Java等クラスオブジェクトを持たない言語の文化圏では、インスタンスオブジェクトとオブジェクトを混同して説明される事があるが、Objective-CやPython、Ruby等、インスタンスオブジェクトとクラスオブジェクトが別であるオブジェクト指向言語では区別して説明される。<ref>Objective-Cプログラミ
ング言語[https://developer.apple.com/jp/devcenter/ios/library/documentation/ObjC.pdf]</ref>
ング言語[https://developer.apple.com/jp/devcenter/ios/library/documentation/ObjC.pdf]</ref>
160行目: 163行目:
<ref>クラス/メソッドの定義 (Ruby manual) [http://www.ruby-lang.org/ja/old-man/html/_A5AFA5E9A5B9A1BFA5E1A5BDA5C3A5C9A4CEC4EAB5C1.html]</ref>元々はSmalltalkから始まった用語である。
<ref>クラス/メソッドの定義 (Ruby manual) [http://www.ruby-lang.org/ja/old-man/html/_A5AFA5E9A5B9A1BFA5E1A5BDA5C3A5C9A4CEC4EAB5C1.html]</ref>元々はSmalltalkから始まった用語である。


==== メッセ処理 ====
==== プライベトデータ扱い方 ====
{{seealso|this (プログラミング)}}
{{seealso|this (プログラミング)}}
そしてあるオブジェクトOにメッセージを配送し適切なメッセージ処理コード(振る舞い)を呼び出す際には、まず対象となるオブジェクトOについて共通部分の格納場所を見つけて適切なコードを選び出し、次にそのコードに対して処理対象となるオブジェクトO固有のデータの所在を示す'''オブジェクトID'''を渡すようになっている。
そしてあるオブジェクトOにメッセージを配送し適切なメッセージ処理コード(振る舞い)を呼び出す際には、まず対象となるオブジェクトOについて共通部分の格納場所を見つけて適切なコードを選び出し、次にそのコードに対して処理対象となるオブジェクトO固有のデータの所在を示す'''オブジェクトID'''を渡すようになっている。
179行目: 182行目:


最も普及している[[クラスベース]]の言語では、共通部分はオブジェクトの種類を表現するクラスに保持され、各オブジェクトは固有データと共にそのクラスのIDを保持する。そしてオブジェクトに送られるメッセージはその送り先オブジェクトにあるクラスのIDからクラスを見つけ、その中からメッセージを処理するコードを見つけ出し、処理対象となっているオブジェクトのIDを付してそのコードを呼び出す仕組みになっている。
最も普及している[[クラスベース]]の言語では、共通部分はオブジェクトの種類を表現するクラスに保持され、各オブジェクトは固有データと共にそのクラスのIDを保持する。そしてオブジェクトに送られるメッセージはその送り先オブジェクトにあるクラスのIDからクラスを見つけ、その中からメッセージを処理するコードを見つけ出し、処理対象となっているオブジェクトのIDを付してそのコードを呼び出す仕組みになっている。

==== コンポジション ====
'''コンポジション'''は、複数のオブジェクトがある一つのオブジェクトの構成要素となっている巨大なオブジェクト群をいう。コンポジションのもとにあるオブジェクトは同一の生存期間を持ち、一つの巨大な仮想オブジェクトの構成部品として機能する。


=== メッセージ ===
=== メッセージ ===
207行目: 213行目:
多くの言語でクラスは言語の要素として直接実現されているが、これは実行効率のためであり、そのように実現することが必須というわけではない。実際、各クラスをそれぞれオブジェクトとして提供する言語も存在する(例:[[Smalltalk]])。このような言語ではある種の'''[[リフレクション (情報工学)|リフレクション]]''' (reflection) が可能となる。即ち必要があればプログラムで実行時にクラスの動作を変更することが可能である。これは非常に大きな柔軟性を提供するが、[[処理系|言語処理系]]による最適化が難しいため実行効率は低下することが多い。{{いつ範囲|date=2019年2月|近年}}では柔軟性と効率性を両立させるために基本的に言語要素としてクラスを提供した上で、リフレクション機能が必要なプログラムに対しては必要に応じて各クラスに対応するクラス・オブジェクトをプログラムが獲得できるようにしている言語が現れてきている。(例:JavaのリフレクションAPI)
多くの言語でクラスは言語の要素として直接実現されているが、これは実行効率のためであり、そのように実現することが必須というわけではない。実際、各クラスをそれぞれオブジェクトとして提供する言語も存在する(例:[[Smalltalk]])。このような言語ではある種の'''[[リフレクション (情報工学)|リフレクション]]''' (reflection) が可能となる。即ち必要があればプログラムで実行時にクラスの動作を変更することが可能である。これは非常に大きな柔軟性を提供するが、[[処理系|言語処理系]]による最適化が難しいため実行効率は低下することが多い。{{いつ範囲|date=2019年2月|近年}}では柔軟性と効率性を両立させるために基本的に言語要素としてクラスを提供した上で、リフレクション機能が必要なプログラムに対しては必要に応じて各クラスに対応するクラス・オブジェクトをプログラムが獲得できるようにしている言語が現れてきている。(例:JavaのリフレクションAPI)


==== インスンスベース ====
==== プロトイプベース ====
{{main|プロトタイプベース}}
{{main|プロトタイプベース}}
クラスは非常に多くのオブジェクト指向プログラミング言語で提供されている機能ではあるが、オブジェクト指向プログラミング言語に必須の機能というわけではない。実際にオブジェクトの管理や、データ・メンバや[[メソッド (計算機科学)|メソッド]]の記述、[[継承 (プログラミング)|継承]]に際してクラスという仕組みに依存せずに、もしくはクラスという仕組み自体を持たずに別の手段でこれらを実現している言語も存在する。このような言語を'''[[プロトタイプベース|インスタンスベース]]''' (''{{Lang|en|instance-based}}'')、'''オブジェクトベース''' (''object-based'') あるいは'''プロトタイプベース''' (''{{Lang|en|prototype-based}}'') のオブジェクト指向プログラミング言語と呼ぶ。インスタンスベースまたはそれに類するオブジェクト指向プログラミング言語には以下のようなものがある:
クラスは非常に多くのオブジェクト指向プログラミング言語で提供されている機能ではあるが、オブジェクト指向プログラミング言語に必須の機能というわけではない。実際にオブジェクトの管理や、データ・メンバや[[メソッド (計算機科学)|メソッド]]の記述、[[継承 (プログラミング)|継承]]に際してクラスという仕組みに依存せずに、もしくはクラスという仕組み自体を持たずに別の手段でこれらを実現している言語も存在する。このような言語を'''[[プロトタイプベース|インスタンスベース]]''' (''{{Lang|en|instance-based}}'')、'''オブジェクトベース''' (''object-based'') あるいは'''プロトタイプベース''' (''{{Lang|en|prototype-based}}'') のオブジェクト指向プログラミング言語と呼ぶ。インスタンスベースまたはそれに類するオブジェクト指向プログラミング言語には以下のようなものがある:
245行目: 251行目:
'''[[メソッド (計算機科学)|メソッド]]''' ({{Lang|en|method}}) は特定の種類のメッセージの処理方法を記述したものである。メソッドも[[メソッド (計算機科学)#インスタンスメソッド|インスタンス・メソッド]]と[[メソッド (計算機科学)#静的メソッド|クラス・メソッド]]の2種にできる。インスタンス・メソッドはそのクラスの各インスタンスオブジェクトを操作し、クラス・メソッドはクラスオブジェクトを操作する。メソッドとの集まりはそのクラスのオブジェクトが処理可能なメッセージのカタログの機能を果たす。
'''[[メソッド (計算機科学)|メソッド]]''' ({{Lang|en|method}}) は特定の種類のメッセージの処理方法を記述したものである。メソッドも[[メソッド (計算機科学)#インスタンスメソッド|インスタンス・メソッド]]と[[メソッド (計算機科学)#静的メソッド|クラス・メソッド]]の2種にできる。インスタンス・メソッドはそのクラスの各インスタンスオブジェクトを操作し、クラス・メソッドはクラスオブジェクトを操作する。メソッドとの集まりはそのクラスのオブジェクトが処理可能なメッセージのカタログの機能を果たす。


一例として、[[C++]]ではメソッドは'''メンバ関数''' ({{Lang|en|member function}}) や'''関数メンバ''' (function member) と呼ばれる。これはC++が[[グローバル関数]]との区別をつけることと、クラスを[[抽象データ型]]の拡張と位置づけ、非メッセージメタファな言語思想を持っている為である。これら言語ではメソッドをオブジェクト(=クラスやインスタンス)の持ち物として捉えず、クラスに定義された機能要素であると考える。メッセージメタファを否定するため、同時にメッセージを実行するメソッド(手法)ではありえない。
==== 他言語と比較したC++のメソッド ====
[[C++]]ではメソッドは'''メンバ関数''' ({{Lang|en|member function}}) や'''関数メンバ''' (function member) と呼ばれる。これはC++が[[グローバル関数]]との区別をつけることと、クラスを[[抽象データ型]]の拡張と位置づけ、非メッセージメタファな言語思想を持っている為である。これら言語ではメソッドをオブジェクト(=クラスやインスタンス)の持ち物として捉えず、クラスに定義された機能要素であると考える。メッセージメタファを否定するため、同時にメッセージを実行するメソッド(手法)ではありえない。


==== クラスメソッド ====
==== クラスメソッド ====
279行目: 284行目:
C++やJavaなどでは、コンストラクタはクラスと同じ名前を持ち、戻り値を持たないメソッドとして定義される。C++では一部のコンストラクタは[[型変換演算子]]として、また[[暗黙の型変換]]にも利用される。
C++やJavaなどでは、コンストラクタはクラスと同じ名前を持ち、戻り値を持たないメソッドとして定義される。C++では一部のコンストラクタは[[型変換演算子]]として、また[[暗黙の型変換]]にも利用される。


=== ガベージコレクションとメモリコンパクション ===
=== ガベージコレクション ===
オブジェクト指向プログラミング言語では、オブジェクトへの[[参照 (情報工学)|参照]]や[[ポインタ (プログラミング)|ポインタ]]が多用される。そのため、オブジェクトの[[メモリ]]への割り当てと破棄に関して、[[ガベージコレクション]]による自動管理機能を備えているものが多い。ただし、すべての言語が備えているわけではない。例えば、C++はガベージコレクションを備えていない。
オブジェクト指向プログラミング言語では、オブジェクトへの[[参照 (情報工学)|参照]]や[[ポインタ (プログラミング)|ポインタ]]が多用される。そのため、オブジェクトの[[メモリ]]への割り当てと破棄に関して、[[ガベージコレクション]]による自動管理機能を備えているものが多い。ただし、すべての言語が備えているわけではない。例えば、C++はガベージコレクションを備えていない。


=== アクセス ===
=== アクセスコントロール ===
オブジェクト指向プログラミングにおいて、[[オブジェクト (プログラミング)|オブジェクト]]は、[[カプセル化]]されておりブラックボックスである。したがって、処理するメッセージのカタログ、つまりインタフェースだけが利用者に公開され、内部の詳細は隠されるのが基本である。しかし、あるクラスのインスタンスの内部だけで利用されるメソッドまで公開してしまうと、利用者にとって煩雑である。また、定数データ・メンバのようなものは一々メソッドでアクセスするようにせず公開してしまっても、カプセル化の利点は失われず効率的でもある。そこで、オブジェクトを定義するプログラマが各データ・メンバやメソッドについて公開・非公開を設定できる機能を用意している言語は多い。
オブジェクト指向プログラミングにおいて、[[オブジェクト (プログラミング)|オブジェクト]]は、[[カプセル化]]されておりブラックボックスである。したがって、処理するメッセージのカタログ、つまりインタフェースだけが利用者に公開され、内部の詳細は隠されるのが基本である。しかし、あるクラスのインスタンスの内部だけで利用されるメソッドまで公開してしまうと、利用者にとって煩雑である。また、定数データ・メンバのようなものは一々メソッドでアクセスするようにせず公開してしまっても、カプセル化の利点は失われず効率的でもある。そこで、オブジェクトを定義するプログラマが各データ・メンバやメソッドについて公開・非公開を設定できる機能を用意している言語は多い。


291行目: 296行目:
なお、public、private、protectedというキーワードは、多くのプログラミング言語で用いられているが、その示す意味は言語ごとに差異があるため、注意が必要である。
なお、public、private、protectedというキーワードは、多くのプログラミング言語で用いられているが、その示す意味は言語ごとに差異があるため、注意が必要である。


=== コンポジション ===
=== 脚注 ===
'''コンポジション'''は、複数のオブジェクトがある一つのオブジェクトの構成要素となっている巨大なオブジェクト群をいう。コンポジションのもとにあるオブジェクトは同一の生存期間を持ち、一つの巨大な仮想オブジェクトの構成部品として機能する。

== 脚注 ==
{{脚注ヘルプ}}
{{脚注ヘルプ}}
{{Reflist}}
{{Reflist}}

2019年4月13日 (土) 04:56時点における版

OOP言語の系譜(水色がOOP)

オブジェクト指向プログラミング(オブジェクトしこうプログラミング、: object-oriented programming、略語:OOP)は、オブジェクト指向考え方[1]を取り入れたコンピュータ・プログラミング手法である。オブジェクトとは大まかに言うとデータ(変数またはプロパティ)とコード(関数またはメソッド)の複合体を意味してるが、その詳細については様々な解釈が存在する。OOPに基づくプログラムはこのオブジェクトの集合を中心にして組み立てられる事になるが、その実装スタイルもまた千差万別である。

OOPの成り立ちにもまた諸説ある。オブジェクト指向プログラミングという言葉自体は、計算機科学者アラン・ケイが1972年にSmalltalkの言語設計を説明する中で初めて生み出されている。なお、データとコードの複合体であるオブジェクトという用語を確立したのは1967年公開のSimula67であった。Simula67クラスオブジェクトの設計及び用法は手続き型プログラミングの機能拡張に近いものであったが、こちらもOOPの草分けと見なされるようになった。Smalltalkのプログラム内のあらゆる要素をオブジェクトとして扱い、メッセージパッシングで相互作用させる本来の意味でのオブジェクト指向設計は、哲学的かつ理論偏重のきらいがあったので実践面ではさほど広まらなかった。1983年に公開されたC++が契機となり、OOPはまた違った角度から注目されるようになった。最終的にこのC++の設計スタイルが物議を醸しながらもOOPの主流となるに到り、同時にOOPの三大原則とされるカプセル化継承多態性プログラミングパラダイムが確立されている。

特徴

プログラミングパラダイムとしてのオブジェクト指向の確立は紆余曲折を経ており(後述)その詳細の解釈も様々であるが、一定の枠組みとなる三つの原則仕様(fundamental principle)が存在する。この三原則に従った言語仕様を総体的または部分的に備えたプログラミング言語がオブジェクト指向準拠と判別される。1~3はいわゆるOOPの三大原則とされるものであり、C++を契機にして確立された。4はオブジェクト指向プログラミングという用語の発端となったSmalltalk設計の主軸であるが、現在では亜流となっている。

  1. カプセル化encapsulation
  2. 継承inheritance
  3. 多態性polymorphism
  4. メッセージパッシングmessage passing
カプセル化

従来のプログラミング環境では、動作を定義するコード(関数)が、状態を表現するデータ(変数)を扱う形でプログラムを記述するのが一般的だったが、開発規模の拡大に伴い解決の難しいソフトウェア・バグ原因の大半は、データの予期せぬ変動と各データ間の不整合である事が経験則で知られるようになって来た。そこでデータを中心にしてプログラムを組み立てようとする考え方が生まれ、その目的に沿うものとしてデータとコードの複合体であるオブジェクトに白羽の矢が立てられた。オブジェクト内のデータは基本的に同じオブジェクト内のコードだけが参照または変動可能とする仕組みが考案され、これがカプセル化と呼ばれた。データを変動させたコード位置の把握を容易にする為に、各データにアクセス出来るコードの範囲を厳密に定めたカプセル化の機能は、範囲外アクセスをコンパイルレベルで禁止して想定外のデータ変動をもたらす人的ミスを減らした。また、データを変動させるコードを呼び出すそのまたコード位置の把握も芋蔓式に必要となったのでコードにもアクセス可能範囲が定められる様になり、更にアクセス範囲の広さにも段階が設けられたので特定の外部をも含んだ柔軟な内部隠蔽の設計が可能となった。

継承

プログラム内の膨大な数のデータは通常グループ化(構造体)されて扱われていたが、複数の構造体間にまたがる共通のデータ集合が数多く目に付くようになり、その冗長さと整合性の問題が浮き彫りとなっていた。これを解決する為に構造体=オブジェクトを複数の階層要素に分け、共通のデータ集合を親階層とし、派生する子階層に親階層の参照アドレスを持たせて連結する構造が考案された。A階層から成る親オブジェクトから派生した子オブジェクトはA+B階層として構成され、これが継承と呼ばれた。コードから参照されたデータがB階層に無い時は、次のA階層に有るか探す仕組みとなり、この連鎖によって傍からは一つのオブジェクトとして存在した。なお、派生クラスで任意の機能を追加出来る継承は従来コードの再利用性を高めるとも考えられたが、深い継承がもたらす構成把握の難化が問題視されてほぼ否定された。これはクラスライブラリの使用内に留まっている。現在[いつ?]では深い継承は倦厭される様になり、代わりに仮想メソッドテーブル(Javaのinterfaceなど)を実装させる横に長い継承が重視されている。また、クラスのメカニズムをベースとするOOP言語では多態性を実現する為の手段として用いられている。

多態性

アドホック多態性は単にソースコードの記述を一部自動化するものである。関数オーバーロードは引数の並び方によって同じ名前のメソッドをコンパイル時に自動的に差別化する機能である。演算子オーバーロードは、扱う数値の型に従って宣言された演算記号を関数名と見なすようにし、単項演算子なら右の数値を第一引数とし、二項演算子なら左右の数値をそれぞれ第一第二引数として関数呼び出しのコードが生成されるという仕組みだった。これらは静的な多態性とされる。

パラメータ多態性もソースコードの記述を一部自動化するものである。関数内またはクラス内の型指定部分をワイルドカードにして宣言しておき、ソースコード内で関数またはクラスが実装記述されると、その具体的な型指定を関数内またはクラス内のワイルドカードに当てはめたコード部分がコンパイル時に自動生成されるという機能だった。前者はジェネリック関数と呼ばれ、後者はより広い範囲を扱う事からジェネリックプログラミングと名付けられた。これらも静的な多態性に位置付けられている。

サブタイプ多態性は動的なものである。最も初期のOOPであるSimula67は、シミュレーション内で扱う多種多様なオブジェクトを継承によって体系化したが、コード部分の細かな違いは共通スーパークラスに属する共通プロシージャ内の分岐フローで処理していた。サブクラスの数だけ分岐構文が増える頻雑さを解消する為に、共通プロシージャをただの住所テーブルにしてサブクラスの実装時に同名プロシージャのアドレスを収納させ、呼び出し時はそのアドレスへジャンプするという機能が考案され、これが仮想関数となった。動的ディスパッチはSmalltalkのオブジェクト設計に由来するものであり、その実装の仕方は様々でやや曖昧な仕様でもある。メッセージを受け取ったレシーバーがオブジェクト内部で動的な状態に従い動的な処理を行って結果を返すというランタイム環境上のプロセスが後に動的ディスパッチのカテゴリで括られた。遠隔プロセスを扱うリモートメソッドのシステムも動的ディスパッチに該当するものである。多重ディスパッチは動的な関数オーバーロードに近いものである。関数コール時または関数ブロック内で、それぞれの引数が動的に型審査されて型変化(dynamic casting)された後に、その引数パターンに対応した同名関数または分岐ルーチンに処理が移行されるという動的変化プロセスを指した。ダブルディスパッチは多重ディスパッチの亜流またはその派生物であり、二通りの考え方がある。動的型審査および型変化されるBオブジェクトを単一引数にしてAオブジェクトの仮想関数メソッドを呼び出す形態と、多重ディスパッチに用いる引数を二つに限定した形態である。いずれも実行時状態に応じた動的変化プロセスとなった。これは主にデータ集合を対象にして分類、解析、作用といった処理を連続的または再帰的に行うアルゴリズムで用いられた。

メッセージパッシング

これは従来のパラメータ付きサブルーチン呼出とリターン値のやり取りを別の視点から再解釈したのであり、基本はバイトデータ(メッセージ)の送受信でセレクタ(メソッド名)とパラメータ、そしてリターン値をやり取りする仕組みだった。オブジェクトのレシーバー(代表メソッド)が渡されたメッセージを解釈し、オブジェクト内部でそれに準じた処理を行い、結果をリターンした。パラメータとリターン値もまたオブジェクトだったので、リターン値をメッセージ内のパラメータにして後続のオブジェクトへ送る事もでき、そのリターン値にまた別のメッセージを送る事も出来た。Smalltalk設計では真偽値、数値、コードブロックもオブジェクトとなったので、真偽値または数値に対して規定の予約語(セレクタ)と併せたコードブロックをメッセージとして送る事で条件分岐や反復処理といった制御フローを表現出来た。オブジェクトを次々とメッセージとして送るのは順次処理となった。メッセージパッシングとは、オブジェクトにオブジェクトを引き合わせて、双方に関連した手続きまたは制御フローを発生させるルーチンワークを意味しており、この連鎖的かつ再帰的な繰り返しがプログラム全体の流れとなった。この斬新なパラダイムを実際のプログラムの形にする為には比較的緻密な設計が要求され、またパターン化されたプロセスにおいても柔軟さを維持する為のワンステップを必要とするので開発工数とCPU負荷が増大する欠点もあった。これが倦厭されてOOP設計の本質でありながら広くは支持されず、OOPに対する関心の焦点は本来は枝葉の部分であるはずのクラスのメカニズムに移る事になった。

Smalltalkの影響を受けた後継言語においても、あらゆるプログラム要素をメッセージで表現しようとする理論の追求は避けられ、例えば制御フローは制御構文で扱われている。またメッセージングに対する認識もシフトし、オブジェクトの代表となるメソッドがパラメータを受け取り多様な処理と移譲を行ういわゆるレシーバーの仕組み自体がメッセージングそのものと見なされるようになった。そのメカニズムを応用したリモートメソッドを実現するバイトデータの送受信もメッセージパッシングの代表例となった。この様にメッセージパッシング設計の本質は見失われながらも、その側面的仕様は数々のOOP言語に導入されてもいる。

歴史

オブジェクト指向プログラミングという考え方が生まれた背景には、計算機の性能向上によって従来より大規模なソフトウェアが書かれるようになってきたということが挙げられる。大規模なソフトウェアが書かれコードも複雑化してゆくにつれ、ソフトウェア開発コストが上昇し、1960年代には「ソフトウェア危機 (software crisis)」といったようなことも危惧されるようになってきた。そこでソフトウェアの再利用部品化といったようなことを意識した仕組みの開発や、ソフトウェア開発工程の体系化(ソフトウェア工学 (software engineering) の誕生)などが行われるようになった。

このような流れの中で、プログラムを構成するコードとデータのうち、コードについては手続き関数といった仕組みを基礎に整理され、その構成単位をブラックボックスとすることで再利用性を向上し、部品化を推進する仕組みが提唱され構造化プログラミング (structured programming) として1967年[要出典]エドガー・ダイクストラ (Edsger Wybe Dijkstra) らによってまとめあげられた(プログラミング言語の例としてはPascal 1971年)。なお、それに続けて「しかしデータについては相変わらず主記憶上の記憶場所に置かれている限られた種類の基本データ型の値という比較的低レベルの抽象化から抜け出せなかった。これはコードはそれ自身で意味的なまとまりを持つがデータはそれを処理するコードと組み合わせないと十分に意味が表現できないという性質があるためであった。」といったように、ほぼ間違いなく説明されている。

そこでデータを構造化し、ブラックボックス化するために考え出されたのが、データ形式の定義とそれを処理する手続きや関数をまとめて一個の構成単位とするという考え方でモジュール (module) と呼ばれる概念である(プログラミング言語の例としてはModula-2 1979年)。しかし定義とプログラム内の実体が一対一に対応する手続きや関数とは異なり、データはその形式の定義に対して値となる実体(インスタンスと呼ばれる)が複数存在し、各々様々な寿命を持つのが通例であるため、そのような複数の実体をうまく管理する枠組みも必要であることがわかってきた。そこで単なるモジュールではなく、それらのインスタンスを整理して管理する仕組み(例えばクラスとその継承など)まで考慮して生まれたのがオブジェクトという概念である(プログラミング言語の例としては1967年のSimula 67)。

Simulaのオブジェクトとクラスというアイデアは異なる二つの概念に継承される。一つはシステム全てをオブジェクトの集合と捉え、オブジェクトの相互作用をメッセージに喩えた「オブジェクト指向」である。オブジェクト間の相互作用をメッセージの送受と捉えることで、オブジェクトは受信したメッセージに見合った手続き単位(≒関数)を自身で起動すると考える。結果オブジェクトは自身の持つ手続きのカプセル化を行うことができ、メッセージが同じでもレシーバオブジェクトによって行われる手続きは異なる――多相性(ポリモーフィズム)を実現した(このメッセージを受け実行される手続き単位は、メッセージで依頼されたことを行うための「手法」の意味でメソッドと呼ばれる)。この思想に基づき作られたのがSmalltalk1972年)であり、オブジェクト指向という言葉はこのときアラン・ケイによって作られた。

一方、Smalltalkとは別にSimulaの影響を受け作られたC++1979年)は抽象データ型のスーパーセットとしてのクラス、オブジェクトに注目し、オブジェクト指向をカプセル化、継承、多相性をサポートするものと再定義した(その際、実行時速度重視及びコンパイラ設計上の制約により、変数メタファである動的束縛の特徴は除外された)。これらは当初抽象データ型派生仮想関数と呼ばれ、オブジェクトのメンバ関数を実体ではなくポインタとすることで、継承関係にあるクラスのメンバ関数のオーバーライド(上書き)を可能にしたことで、多相性を実現した(この流儀ではメッセージメタファはオブジェクト指向に必須ではないものと定義し、オブジェクトの持つ手続きをメソッドとは呼ばずメンバ関数と呼ぶ)。この他、Smalltalkにある動的束縛の類似的な機能としてオーバーロード(多重定義)が実装されている。

Smalltalkはこの「全てをオブジェクトとその相互作用で表現する」というデザインに立ち設計されたため、全てをファイルと捉えるファイル指向オペレーティングシステムからの脱却と、プログラムをフロー制御された手続きと捉える手続き型言語からの脱却が行われた。そのためSmalltalkは自身がオブジェクト指向オペレーティングシステムでもあること、メッセージ・パッシングなどの特徴を持った。これは当時のプログラム言語としては特異的であり、ガベージコレクタを必要としインタプリタ形式で実行される処理の重さも手伝って先進的ではありながら普及しがたいものであると捉えられた。また、メッセージでの多相性は、変数へのオブジェクトの動的束縛が前提となるため、静的型チェックが必要な実行時性能重視のコンパイラ言語にとって、実装から除外すべき特徴となった。

C++の創始者ビャーネ・ストロヴストルップは、Smalltalkのような論理美の追求よりも、現用としての実用性を重視した。そのため、C++の再定義した「オブジェクト指向」はこれらの問題を全てクリアにし、既存言語の拡張としてオブジェクト指向機能を実装できることでブレイクスルーを迎え急速に普及する。これによりメッセージ送信という考え方はやや軽視されるようになり、オブジェクト指向とはC++の再定義したものと広く認知されるようになった。

1980年代後半に次々と生まれたオブジェクト指向分析・設計論は、Smalltalkを源流とするオブジェクト指向を基に組み立てられた。このときSmalltalkは健在であったが広く普及しているとは言えずC++実装する機会が多かったが、C++はSmalltalkとは思想的にやや異なることと、仕様の複雑さが問題とされた。このニーズを受けC++の提示した現実解と、Smalltalk的理想論を融合するものとして[要出典]、構文面ではシンプル化しながらも強くC++の影響を受けつつ、一方で用語や思想面でSmalltalk色を濃くした[要出典]Javaが作られた(1995年に初期リリース)。バランス感覚に長けたJavaの登場によってオブジェクト指向開発に必要な要素が全てそろい、1990年代後半からオブジェクト指向は広く普及するようになった。

OOP言語一覧

オブジェクト指向を総体的または部分的にサポートする機能を備えたプログラミング言語の公開は、1980年代後半から顕著となった。OOP言語の分類法は複数あるが、Smalltalkをルーツとするメッセージパッシングの構文が重視されてるか否かで大別される事が多い。そうでないものがOOP言語の主流となっており「C++」「Java」「C#」「Swift」などがその代表とされる。メッセージパッシングを重視するOOP言語には「Smalltalk」「Objective-C」「Self」などがある。言語仕様の中でオブジェクト指向の存在感が比較的高い代表的なプログラミング言語を以下に列挙する。

Simula 67 1967年

1962年に公開されたSimulaの後継版であり、クラスのプログラミング概念を導入した最初の言語である。現実世界の擬似モデルを観測するシミュレーション・プログラム制作用に開発されたもので、クラスを実メモリに展開したオブジェクトは、その観測対象要素となった。simulaのクラスは、サブルーチンに専用変数と補助プロシージャを加えた機能的小型モジュールに近いものであったが、継承と仮想関数という先進的な設計を備えていた事でOOPの草分けと見なされるようになった。C++、Java、C#の設計母体となった。

Smalltalk 1972年

メッセージパッシングのプログラミング概念を導入した最初の言語。数値、真偽値から変数、構造体、コードブロック、メタデータまでのあらゆる要素をオブジェクトとする概念を編み出した最初の言語でもある。オブジェクト指向という言葉は、Smalltalk開発者がその言語設計を説明する中で生み出された。オブジェクトの基礎的な振る舞いを規定する限られた予約語の他は、オブジェクトとメッセージのやり取りで制御構造を含めたあらゆるプロセスを表現出来た。また、専用のランタイム環境上でプログラムを実行する設計を応用して動的な多態性とセキュリティに繋がるモニタリングも実現した。これは後に仮想マシンと呼ばれるものとなり、JavaやC#に踏襲された。

C++ 1983年

C言語にOOPデザインを追加したもの。Simulaの影響を受けている。クラスのメカニズムが備えられて、カプセル化、継承、多態性といったOOP仕様を実装している。テンプレート機能例外処理、演算子オーバーロードを応用した関数オブジェクトなど様々なプログラミングパラダイムも導入された。元がC言語であるため、OOPから逸脱したコーディングも多用できる点が物議を醸したが、その是非はプログラマ次第であるという結論に落ち着いた。

Objective-C 1984年

C言語をOOPデザイン化したもの。こちらはSmalltalkの影響を受けており、それに準じたメッセージパッシングリフレクションのメカニズムが備えられた。OOP的には前述のC++よりも正統であると見なされた。制御構文が追加され、メッセージの仕様もやや簡素化されるなど実践上の便宜が図られており、Smalltalkよりもコーディングし易くなった。

Object Pascal 1986年

PascalをOOPデザイン化したもの。

Eiffel 1986年

PascalをベースにしてOOPデザイン化し、またジェネリックプログラミングを追加した。型付けは静的に限られ、非参照データを自動解放するガーベジコレクタを持ち、多重継承時の問題を回避する仕組みや例外処理など高い堅牢性を備えた。これらは後のJavaやC#の手本となった。

Self 1987年

メッセージパッシングの構文が中心となっている。動的な多態性を重視した言語であり、従来のクラスベースのオブジェクト設計に対して、システム側が用意したオブジェクトを複製して任意の拡張を施すプロトタイプベースのデザインを初めて実装した。Smalltalkと同様に専用のランタイム環境で実行されたが、これも実用面では初となる実行時コンパイラjust-in-time compiler)の機能が備えられて速度面でも画期的なものとなった。

CLOS 1988年

Common LispをOOPデザイン化したもの。

Python 1990年

インタプリタ式で動作する。言語仕様を簡素化し自動メモリ管理機能を実装して扱いやすく理解しやすいOOPを目指している。後のOOPスクリプト言語の手本となった。

Java 1995年

堅牢性と安全性を重視したOOP言語。その二つの理念を実現する為に、仮想マシン上の実行、ガーベジコレクタ、例外処理などを採用し、ポインタと直アドレス変数、多重継承、ジェネリックプログラミング、演算子オーバーロードなどを破棄した。破棄部分についてはその埋め合わせの設計も備えられた。クラスのメカニズムを中心にしたOOPであるが、様々なプログラミングパラダイムも追加されている。非常に整えられたハイブリッドOOP言語である。

Delphi 1995年

Object Pascalを発展させたもので、データベースの操作プログラム開発などを主な用途とした。一時期Javaの対抗馬となった。

Ruby 1995年

OOPデザインされたスクリプト言語である。インタプリタ式で動作する。スクリプトでありながら、クラス、マルチスレッド、例外処理、そしてソフトウェアコンポーネント(モジュール)を扱えるMixinといった利便性の高い機能も備えている。

JavaScript 1996年

ウェブアプリケーション開発を主な目的とするOOPスクリプト言語。主にプロトタイプベースでオブジェクトを扱う事でコーディングを簡便にしている。ECMAScriptとして標準化されている。ECMAScript 2015ではクラス構文をサポートするようになった。

C# 2000年

Javaを強く意識して開発されたOOP言語。.NET Frameworkなどの共通言語基盤上で実行される。Javaと同等または部分的に拡張させたスタイルを持ち、こちらもよく整えられたハイブリッドOOP言語として知られる。

Visual Basic.NET 2002年

Visual BasicをOOPデザイン化したもの。.NET Frameworkなどの共通言語基盤上で実行される。

Ceylon 2011年

Javaを元に開発され、その長所と短所を見直しつつ再設計されたOOP言語。Javaの改造版である。またJavaScriptにもコンバートできる。

Swift 2014年

高度に整えられたマルチパラダイムプログラミング言語。クラスのメカニズムをベースにしたオブジェクト指向的言語仕様も導入されている。

OOP言語の仕組み

オブジェクト指向プログラミング言語は、相互にメッセージを送りあうオブジェクトの集まりとしてプログラムを構成することができる仕組みを持つ。 そのために、少なくともオブジェクトについての3つの仕組みと、オブジェクトの管理についての3つの仕組みが必要となる。

オブジェクトの仕組み
  • オブジェクトに蓄えられる情報、データを表現する仕組み。
  • 他のオブジェクトにメッセージを配送する仕組み。
  • 受け入れ可能な各種メッセージに対応して、処理する事柄を記述する仕組み(メソッド)。
オブジェクトを管理する仕組み
  • オブジェクト間の関係を整理分類して系統立てる仕組み。
  • 必要なオブジェクトを作成・準備する仕組み。
  • 不要なオブジェクトを安全に破棄する仕組み。

これらをどのように言語の要素として提供し、どのような機械語コードで実現するかによって様々なオブジェクト指向プログラミング言語のバリエーションが生まれる。以下、オブジェクト指向プログラミング言語が提供する様々な要素が上記の仕組みをどのように実現しているかについて概観する。

オブジェクトの概念と実装

オブジェクト (object) はオブジェクト指向プログラミングの中心となる概念であり、この概念を実際にどう実現するかはオブジェクト指向プログラミング言語により異なる。

以下、概念と実際がどう対応しているかについて説明する。

  • 概念的には各々のオブジェクトは、プログラムが表現する情報システムの中で能動的な役割を持った存在を表現している。
  • 概念的には メッセージを受け取り、その処理の過程で内部に蓄えたデータを書き換え、必要に応じて他のオブジェクトにメッセージを送るといった動作をしている。
  • 概念的には コードとデータが一つになっている。

オブジェクトの実装構造

実際のプログラムでは、全てのオブジェクトが互いに全く異なった存在ではなくオブジェクトは種類に分けることが出来る。

例えば勤怠管理のシステムであれば、氏名や年齢、累積勤務時間などのデータは異なっても社員は皆、出勤し退勤するという処理(振る舞い)は同じだろう。このように複数の異なるオブジェクトが同じ種類のメッセージを受け取り共通の処理をするのが普通である。

このような場合、各オブジェクトがそれぞれメッセージ処理のコード(前述の「振る舞い」に当たる)を独自に備えていては無駄である。そこでオブジェクト指向プログラミング言語がオブジェクトを実現する際には多くの場合、内部的にはオブジェクトを2つの部分に分けている。

同一種類のオブジェクトの間で変わらない共通部分

一つは同一種類のオブジェクトに共有される部分、例えばメッセージ処理のコード(振る舞い)や定数(どのオブジェクトでも異ならないデータ)の類である。

同一種類のオブジェクトの間で変わる個々の部分

もう一つは同一種類のオブジェクトでもそれぞれ異なる部分、典型的には各オブジェクトが保持するデータ群である。

クラスのオブジェクト化

動的型付けを採用するオブジェクト指向言語の多くは、クラスより生成するインスタンスの他にメタクラスという機能を持ちクラス自体をオブジェクトとして扱うことが出来る。このためオブジェクトには、インスタンスオブジェクトとクラスオブジェクトという2種類のオブジェクトが存在する。Java等クラスオブジェクトを持たない言語の文化圏では、インスタンスオブジェクトとオブジェクトを混同して説明される事があるが、Objective-CやPython、Ruby等、インスタンスオブジェクトとクラスオブジェクトが別であるオブジェクト指向言語では区別して説明される。[2] [3] [4]元々はSmalltalkから始まった用語である。

プライベートデータの扱い方

そしてあるオブジェクトOにメッセージを配送し適切なメッセージ処理コード(振る舞い)を呼び出す際には、まず対象となるオブジェクトOについて共通部分の格納場所を見つけて適切なコードを選び出し、次にそのコードに対して処理対象となるオブジェクトO固有のデータの所在を示すオブジェクトIDを渡すようになっている。

各オブジェクトの固有データを識別するオブジェクトIDを表現する方法も様々で、オブジェクトのIDとしては名前、番号なども用いられることがあるが、オブジェクトの固有データを記憶している主記憶上のアドレスがそのまま用いられることもある。アドレスを直接利用することは非常に実行効率の向上に寄与するが、プログラム間でのオブジェクトの受け渡し、セッション間(プログラムが終了して再度起動された時など)でのオブジェクトの受け渡しにはそのまま利用することができない。

また各オブジェクトの固有データから共通部分の格納場所を見つける方法もまた各言語により異なり、その言語の開発目的に応じて実に多種多様である。

JavaScriptの場合

例えばJavaScriptの場合、各オブジェクトは連想配列であり、名前で表現されたメッセージのIDからメッセージ処理コードである関数への参照を直接見つけ出す。各オブジェクトの固有データもその連想配列に格納されていて、メッセージを処理する関数には連想配列のアドレスが渡される。

Selfの場合

Selfのようなインスタンスベースのオブジェクト指向プログラミング言語では、プロトタイプとなるオブジェクトがメッセージを処理するコードも保持しており、オブジェクトがクローンされて作成されるときにそのプロトタイプのありかを示す情報もコピーされ、メッセージは受け取ったオブジェクトのIDを添えてプロトタイプに送られて処理される(Selfでは実行効率上の問題から後に内部的にクラスを作って利用するようになっている)。

クラスベースの言語の場合

最も普及しているクラスベースの言語では、共通部分はオブジェクトの種類を表現するクラスに保持され、各オブジェクトは固有データと共にそのクラスのIDを保持する。そしてオブジェクトに送られるメッセージはその送り先オブジェクトにあるクラスのIDからクラスを見つけ、その中からメッセージを処理するコードを見つけ出し、処理対象となっているオブジェクトのIDを付してそのコードを呼び出す仕組みになっている。

コンポジション

コンポジションは、複数のオブジェクトがある一つのオブジェクトの構成要素となっている巨大なオブジェクト群をいう。コンポジションのもとにあるオブジェクトは同一の生存期間を持ち、一つの巨大な仮想オブジェクトの構成部品として機能する。

メッセージ

メッセージ (message) はオブジェクト間の通信でやりとりされる情報である。メッセージはメッセージ種別を示すIDとメッセージの種別に応じた追加の情報からなる定まった形式を持つ。追加の情報はそれ自身が何らかのオブジェクトやオブジェクトのIDである場合もある。メッセージの配送には大別して2つの方式がある:

同期式
オブジェクトがメッセージの送信を依頼すると相手が受信、処理して結果を返すまでそのオブジェクトは処理を中断して待つ。
非同期式
オブジェクトがメッセージの送信を依頼した後、相手の応答を待たずにオブジェクトは処理を続行する。処理結果は別のメッセージとして返される。

両者とも一長一短がありどちらがすぐれているとは言えない。また並列・並行処理が可能な環境では一方の仕組みがあれば、それを利用してもう一方も実現可能である。一般的な傾向としては、メッセージの伝送や処理に時間が掛かる場合は非同期式の方が効率は良く、そうでない場合には同期式の方が挙動が分かりやすく利用しやすい。

並列処理並行処理システムを記述する言語や分散システムを記述する言語ではOSなどが提供するメッセージ機能や自前の配送メカニズムを使って非同期式でメッセージが配送される場合もあるが、一般にオブジェクト指向プログラミング言語ではその多くが同一のプログラム内の通信であるので同期式のメッセージ配送が利用される。特にコンパイルされるタイプのオブジェクト指向プログラミング言語では、しばしば特別なメッセージ配送の仕組みを用意せず、特別な形式の関数の呼び出しでメッセージの配送を直接に表現する。即ち、各メソッドを内部的には関数として実現し、メッセージIDはメソッド名で表し、関数の第一引数としてオブジェクトIDを渡し(この第一引数は多くの言語で特別な記法で表される)、追加の引数としてメッセージの追加部分の情報を渡すのである。こうするとメッセージ送信は直接的なメソッドの関数呼び出しとして表せる。ただし、プログラムで継承の仕組みが利用されている場合はプログラムのテキストからだけでは呼び出すべきメソッドが決定できない場合があるので、実行時にメソッドを決定するためにメソッド・サーチ仮想関数テーブルといった仕組みが必要となる。

多くのプログラミング言語においてメッセージは、メソッド呼び出しの比喩でしかないことが多い。SmalltalkやObjective-Cの様な言語では、メッセージはメソッド呼び出しとは独立した機構として存在している。メッセージが機構として存在する言語では、メッセージをオブジェクトに送信した際、宛先のオブジェクトにメッセージで指定したメソッドが存在しない場合でもメッセージを処理することが出来る。これを利用し、メッセージの配送先を別のオブジェクトに指定したり、メッセージを一時保存したり、不要なメッセージを無視する等といったメッセージ処理が行われる。

クラス

クラスベース

クラス (class) は大多数のオブジェクト指向プログラミング言語で提供されている仕組みであり、上記の機能の殆ど全てに関わりがある。概念的にはクラスはオブジェクトの種類を表す。このためオブジェクトはクラスに属するという言い方をする。あるクラスに属するオブジェクトのことをそのクラスのインスタンス (instance) と呼ぶ。データ型の理論から見た場合クラスは型を定義する手段の一つである。クラスによってオブジェクトを記述する言語をクラスベース (class-based) のオブジェクト指向プログラミング言語と呼ぶ。

ハイブリッド型オブジェクト指向プログラミング言語では在来のレコード型(Cでは構造体)の構文を拡張してクラスの定義を行うようにしたものが多い。

多くのオブジェクト指向プログラミング言語ではクラスをデータメンバメソッドの集まりとして記述する。平たく言えばデータ・メンバの集まりはオブジェクトが保持するデータの形式を定め、各メソッドはそれぞれオブジェクトが処理する特定のメッセージの処理方法を定める。しばしばデータ・メンバとメソッドには個別にアクセス権が設定できるようになっていて、そのクラスに属するオブジェクトが内部的に利用するものと他のクラスに属するオブジェクトに公開するものを分類できるようになっている。多くの場合、公開されたメソッドの集まりは全体として処理可能なメッセージのカタログの機能、即ちインタフェースを提供する。各言語によって異なるが特定の名前のメソッドを定めて、オブジェクトの生成や初期化時の処理、廃棄時の処理などを記述できるようにすることも多い。

多くの言語でクラスは言語の要素として直接実現されているが、これは実行効率のためであり、そのように実現することが必須というわけではない。実際、各クラスをそれぞれオブジェクトとして提供する言語も存在する(例:Smalltalk)。このような言語ではある種のリフレクション (reflection) が可能となる。即ち必要があればプログラムで実行時にクラスの動作を変更することが可能である。これは非常に大きな柔軟性を提供するが、言語処理系による最適化が難しいため実行効率は低下することが多い。近年[いつ?]では柔軟性と効率性を両立させるために基本的に言語要素としてクラスを提供した上で、リフレクション機能が必要なプログラムに対しては必要に応じて各クラスに対応するクラス・オブジェクトをプログラムが獲得できるようにしている言語が現れてきている。(例:JavaのリフレクションAPI)

プロトタイプベース

クラスは非常に多くのオブジェクト指向プログラミング言語で提供されている機能ではあるが、オブジェクト指向プログラミング言語に必須の機能というわけではない。実際にオブジェクトの管理や、データ・メンバやメソッドの記述、継承に際してクラスという仕組みに依存せずに、もしくはクラスという仕組み自体を持たずに別の手段でこれらを実現している言語も存在する。このような言語をインスタンスベース (instance-based)、オブジェクトベース (object-based) あるいはプロトタイプベース (prototype-based) のオブジェクト指向プログラミング言語と呼ぶ。インスタンスベースまたはそれに類するオブジェクト指向プログラミング言語には以下のようなものがある:

なお、クラスベースの言語とインスタンス・ベースの言語との間には明確な境界線はない。たとえば、インスタンス・ベースの代表格ともいえる Self には、traits と呼ばれるクラスのような仕組みが追加されているし、JavaScript、NewtonScript に至っては traits 類似の仕組みを「クラス」と呼称している。また逆に、クラスベースの言語でもクローンを行うメソッドを備え、委譲の仕組みを記述すればある程度はインスタンス・ベースのスタイルでプログラムを記述できる。

インスタンス・ベースの言語ではオブジェクトの生成は既存のオブジェクト、特にプロトタイプprototype、原型)と呼ばれるオブジェクトからのクローンによって行われる。当然、一群のクローンはその親、ひいてはプロトタイプと同一の種類のオブジェクトと見なされる。メソッドはプロトタイプ・オブジェクトに属し、メッセージは委譲によってそのオブジェクトが覚えているコピー元へ向かってプロトタイプまで順にメッセージが中継されてから処理される。新しい種類のオブジェクトが必要な場合は適当なオブジェクトをクローンした後で必要なデータ・メンバやメソッドを追加あるいは削除し新たなプロトタイプとすることで行われる。追加されたのでないメソッドに対応するメッセージについてはコピー元のオブジェクトに処理を委譲する。

クラスベースの言語との関係について考えてみると、クローンはプロトタイプと同一の「クラス」に属すると見なし、データ・メンバやメソッドが追加・削除されてあらたなプロトタイプが作られると別の「クラス」が内部的に生成されると考えることができる。ここでデータ・メンバやメソッドの追加のみを許して削除を許さないよう制限すればクローンの「クラス」がその親の「クラス」を継承した場合と同等になる。このためメッセージが委譲の連鎖をたどって配送されるという効率上の問題を無視すれば理論上、インスタンス・ベースの言語の記述能力はクラス・ベースの言語を包含していると言える。ただ、インスタンス・ベースの言語でも実行効率上の問題からなんらかのクラスに似た仕組みを備えている場合が多い。

データメンバ

データメンバ (data member) は、他のオブジェクトに対する参照ポインタであるか、他のオブジェクトそのものである。参照かポインタである場合にはそのデータメンバの参照するのはデータメンバが記述されているクラスそのもののインスタンスに対する参照であっても良い。

一般にデータメンバはインスタンスデータメンバインスタンスフィールド)とクラスデータメンバ静的変数)の2種類に大別できる。効率上の観点から言語が提供する基本オブジェクトの定数を表すデータメンバは特別扱いされる。そのような定数を表すデータメンバを特に定数データメンバ (constant data member) と呼ぶ。データメンバはC++などの言語ではメンバ変数 (member variable)、Javaなどではフィールドと呼ばれることがあり、UMLでは属性と呼ばれる。

インスタンスデータメンバ

インスタンスデータメンバ(一般に単にデータメンバと言われる場合はこちら)はそのクラスのインスタンス各々に保持される。インスタンスデータメンバの集まりはそのクラスのインスタンスが保持するデータの形式を定める。インスタンスデータメンバは単にデータメンバと呼ばれることも多い。

Smalltalkではインスタンス変数 (instance variable) と呼ばれる。

クラスデータメンバ

クラスデータメンバはそのクラスオブジェクトとインスタンスオブジェクトの間で共有されるデータである。

Smalltalkではクラスデータメンバはクラス変数 (class variable) と呼ばれる。また、C++Javaでは歴史的事情によりクラスデータメンバは静的データメンバ (static data member)、静的変数 (static variable)、静的フィールド (static field) と呼ばれる。

ただし、Smalltalkのクラス変数はC++やJavaのクラス変数とは異なる。Smalltalkにおいて、C++やJavaのクラス変数と同等となる変数はプール辞書 (pool dictionary) と呼ばれる。

メソッド

メソッド (method) は特定の種類のメッセージの処理方法を記述したものである。メソッドもインスタンス・メソッドクラス・メソッドの2種にできる。インスタンス・メソッドはそのクラスの各インスタンスオブジェクトを操作し、クラス・メソッドはクラスオブジェクトを操作する。メソッドとの集まりはそのクラスのオブジェクトが処理可能なメッセージのカタログの機能を果たす。

一例として、C++では、メソッドはメンバ関数 (member function) や関数メンバ (function member) と呼ばれる。これはC++がグローバル関数との区別をつけることと、クラスを抽象データ型の拡張と位置づけ、非メッセージメタファな言語思想を持っている為である。これら言語ではメソッドをオブジェクト(=クラスやインスタンス)の持ち物として捉えず、クラスに定義された機能要素であると考える。メッセージメタファを否定するため、同時にメッセージを実行するメソッド(手法)ではありえない。

クラスメソッド

クラス・メソッドだが、オブジェクト指向の本義に立ち返れば、クラス・メソッドがあるということはクラスがメッセージをレシーブできるという事になる。

クラスがメソッドを持つことは便利だが、クラスをオブジェクトとすると実行効率に劣るため、双方の利点を享受できるこのような折中的仕様を取る言語は多い。

C++ではクラスはオブジェクトでは無いが、一方でクラスに属するメソッドは存在する。

Eiffelではクラスはオブジェクトでは無いためクラスのメソッドであるクラス・メソッドは存在しない。

Smalltalkではクラスもオブジェクトの一種であるため当然クラスはメソッドをもつ。

各言語におけるクラスメソッドの呼称

クラス・メソッドは、C++では静的メンバ関数 (static member function) と呼ばれる。これはクラスがオブジェクトでない言語にとってはクラス・メソッドより正確な表現であり適切である。("static" とはCのstatic変数に由来しauto変数の対語である。関数コールによりスタック上に生成される関数インスタンスに依存しない変数と、インスタンス生成有無にかかわらず実行できる関数の類似による。)

Javaではクラス・メソッドは静的メソッド (static method) とも呼ばれることもある。

システムメソッド

言語によっては特定の名前のインスタンス・メソッドやクラス・メソッドにオブジェクトの生成、初期化、複製、廃棄といった機能を固定的に割り当てている。

コンストラクタとデストラクタ

初期化に利用されるメソッドをコンストラクタあるいは構築子 (constructor)、廃棄時に利用されるメソッドをデストラクタあるいは消滅子[要出典] (destructor) と呼んで特別に扱うことが多い。

コンストラクタが初期化だけを担う場合はイニシャライザあるいは初期化子 (initializer) と呼ばれることもある。

Javaはオブジェクトの寿命管理にガベージコレクションを用いるため、デストラクタをサポートしない。ただし、オブジェクトがガベージコレクションによって破棄されるときに呼び出されるファイナライザ (finalizer) をサポートし、Object#finalize()メソッドがその役割を果たす。ただし、ファイナライザはC++のデストラクタと違ってユーザーコードで明示的に呼び出すことはできない。ファイナライザが呼び出されるタイミングをプログラマが制御することはできず、最終防壁(フェイルセーフ)としての役割しか持たないため、Javaにおけるファイナライザは本当に必要でない限り使用するべきではない。C#もファイナライザをサポートする(構文はC++のデストラクタに似ており、かつてはデストラクタと呼ばれていたが、役割はJavaのファイナライザと同じである)。

データ型の理論においては保持されるデータが必ずその型で認められる正しい値の範囲に収まることを保証するため、生成されるオブジェクトのデータ・メンバが必ず適切なコンストラクタによって初期化されるように求める。またオブジェクトが入出力機器やファイルや通信、プロセススレッドウィンドウウィジェットなどハードウェアオペレーティングシステム (OS) が提供する資源を管理するために利用される場合に、コンストラクタやデストラクタでそれらの資源の使用開始(オープン処理)や使用終了(クローズ処理)をそれぞれ管理し、通常のメソッドでそれらにまつわる各種サービスを提供するようにすることで、それらのリソースがあたかもプログラム中のオブジェクトであるかのように自然に取り扱うことができるようになる(RAII)。

C++やJavaなどでは、コンストラクタはクラスと同じ名前を持ち、戻り値を持たないメソッドとして定義される。C++では一部のコンストラクタは型変換演算子として、また暗黙の型変換にも利用される。

ガベージコレクション

オブジェクト指向プログラミング言語では、オブジェクトへの参照ポインタが多用される。そのため、オブジェクトのメモリへの割り当てと破棄に関して、ガベージコレクションによる自動管理機能を備えているものが多い。ただし、すべての言語が備えているわけではない。例えば、C++はガベージコレクションを備えていない。

アクセスコントロール

オブジェクト指向プログラミングにおいて、オブジェクトは、カプセル化されておりブラックボックスである。したがって、処理するメッセージのカタログ、つまりインタフェースだけが利用者に公開され、内部の詳細は隠されるのが基本である。しかし、あるクラスのインスタンスの内部だけで利用されるメソッドまで公開してしまうと、利用者にとって煩雑である。また、定数データ・メンバのようなものは一々メソッドでアクセスするようにせず公開してしまっても、カプセル化の利点は失われず効率的でもある。そこで、オブジェクトを定義するプログラマが各データ・メンバやメソッドについて公開・非公開を設定できる機能を用意している言語は多い。

例えば、Javaでは、データ・メンバやメソッドの宣言にpublicと指定すれば、他オブジェクトから自由に利用でき(公開と呼ばれる)、privateと指定すればオブジェクト内だけで利用できるようになる(非公開と呼ばれる)。

しかし、ある機能を提供するのに、一個ではなく一群のクラスに属するオブジェクトでそれを記述するのが相応しい事例がある。そのような場合、関係する一群のオブジェクト間でだけデータ・メンバやメソッドを利用できれば便利である。それを可能にするための拡張がいくつか存在する。例えば、継承を利用しているときに、あるクラスが子孫にだけ利用を許可したいデータ・メンバやメソッドがある場合、Javaではprotectedを指定することでそれを実現できる(限定公開と呼ばれる)。また、ある一群の機能を実現するクラスのライブラリで、その実現に関連するクラスに属するオブジェクトだけがデータ・メンバやメソッド利用できるようにしたい場合も考えられる。また、Javaでは、ライブラリを構成するクラス群を表現するパッケージ (package) という仕組みがあり、特に指定がない場合は同一パッケージに属するクラスのオブジェクト間でのみデータ・メンバやメソッドを相互に利用可能である。その他にも、デザインパターンの一つであるFacade パターンでは、この仕組みがテクニックとして応用されている。また、C++ではフレンド宣言という仕組みがあり、あるクラスで外部非公開に指定されているデータ・メンバやメソッドについて、その利用を許可するクラスや関数のリストをクラス内に列挙することができる。

なお、public、private、protectedというキーワードは、多くのプログラミング言語で用いられているが、その示す意味は言語ごとに差異があるため、注意が必要である。

脚注

  1. ^ コンピュータ・プログラミングのパラダイムについては『新しいプログラミング・パラダイム』などを参照: http://www.kyoritsu-pub.co.jp/bookdetail/9784320024939
  2. ^ Objective-Cプログラミ ング言語[1]
  3. ^ Classes ― Python v2.7.3 documentation[2]
  4. ^ クラス/メソッドの定義 (Ruby manual) [3]

関連項目