「オブジェクト指向プログラミング」の版間の差分
タグ: Refタグつき記述の除去 |
Goldensundown2 (会話 | 投稿記録) |
||
(同じ利用者による、間の24版が非表示) | |||
1行目: | 1行目: | ||
{{独自研究|date=2018年2月}} |
{{独自研究|date=2018年2月}} |
||
{{プログラミング言語|index=おふしえくとしこうふろくらみんく}} |
{{プログラミング言語|index=おふしえくとしこうふろくらみんく}} |
||
[[ファイル: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という事になる。 |
||
オブジェクト指向プログラミングは、ソフトウェアの大規模化に伴い、より効率的な開発手法が模索される中で1960年代に考案された。OOP言語は歴史的にクラス構造を中心とする''[[Simula]]''系統と、メッセージ機能を主体とする''[[Smalltalk]]''系統に分けられるが、後に''[[C++]]''や''[[Java]]''などの言語が派生した前者が主流となった。この前者のオブジェクト指向が示す「[[カプセル化|内部隠蔽]]」「[[継承 (プログラミング)|継承]]」「[[ポリモーフィズム|多態性]]」の三仕様を備えていれば、OOP言語であると見なされる。 |
|||
⚫ | |||
オブジェクト指向プログラミングで用いられる代表的な概念および技法として、以下のものが挙げられる。 |
|||
*[[カプセル化]](振る舞いの隠蔽とデータ隠蔽) |
|||
*[[インヘリタンス]]([[継承 (プログラミング)|継承]]) -- [[クラスベース]] |
|||
⚫ | |||
*[[ダイナミックバインディング]]([[動的束縛]]) -- [[動的型付け]] |
|||
⚫ | |||
上記を含むオブジェクト指向プログラミングにおいて有用な機能(の一部)を言語仕様として備えたプログラミング言語を、'''オブジェクト指向プログラミング言語''' ({{Lang|en|'''OOPL'''; object-oriented programming language}}) という。 |
|||
OOP環境の主な仕様は以下の通りである。括弧内の名称はやや意訳したものである。 |
|||
#[[カプセル化|エンカプセレーション(内部隠蔽)]] |
|||
ただし、上述の特徴はオブジェクト指向言語に固有の概念というわけではなく、非オブジェクト指向言語の中にもこの性質を備えるものもある。 |
|||
#[[インヘリタンス|インヘリタンス(継承)]] |
|||
⚫ | |||
#*[[多重定義|オーバーローディング(多重定義)]] |
|||
#*[[ジェネリックプログラミング|パラメティックポリモーフィズム(型準拠生成)]] |
|||
#*[[仮想関数|バーチャルファンクション(仮想関数)]] |
|||
#*[[多重ディスパッチ|マルチプルディスパッチ(多択展開)]] |
|||
#[[メッセージパッシング|メッセージパッシング(伝言送受)]] |
|||
#''etc..'' |
|||
'''エンカプセレーション''' |
|||
⚫ | |||
従来のプログラミング環境では、「動作」を定義する'''コード'''が、「状態」を表現する'''データ'''を扱う形でプログラムを記述するのが一般的だったが、開発規模の拡大に伴い、解決の難しいソフトウェア・バグ原因の大半は「データの予期せぬ変動」と「各データ間の予期せぬ不整合」である事が経験則で知られるようになって来た。そこでデータを中心にしてプログラムを組み立てようとする考え方が生まれ、データ群とそれに付随するコード群をまとめた'''オブジェクト'''というプログラミング概念が編み出された。従来の「コードの為のデータ」とは逆に「データの為のコード」としたのは、データを変動させたコード位置を容易に特定出来るようにしてバグ発見に繋げる為であった。この目的に沿って特定のデータを扱えるコード範囲を厳密に定めた'''内部隠蔽'''の仕様は、アクセス許可外のコード記述をエラー警告して、想定外のデータ変動をもたらす人的ミスを減らした。 |
|||
'''インヘリタンス''' |
|||
また、プログラム内の膨大な数のデータは通常グループ化(構造体)されて扱われていたが、各構造体間にまたがる共通のデータ集合が多数出現し、その冗長性と整合の頻雑さの問題が浮き彫りとなっていた。これを解決する為にOOPは、構造体=オブジェクトを複数の'''階層要素'''に分け、共通のデータ集合を親階層とし、派生する子階層に親階層の参照アドレスを持たせて連結する構造とした。A階層から成る親オブジェクトから派生した子オブジェクトはA+B階層として構成され、これが'''継承'''と呼ばれた。コードから参照されたデータがB階層に無い時は、次のA階層に有るか探す仕組みとなり、この連鎖によって傍からは一つのオブジェクトとして存在した。なお、派生クラスで任意のデータとメソッドを追加出来る継承の機能は、同時にコードの再利用性を高めるとも見なされたが、深い継承がもたらす構成把握の難化がネックとなってほぼ否定されるに到った。現在では深い継承は回避される様になり、代わりに共通メソッド呼出テーブル(''Java''のinterfaceなど)を実装させる横に長い継承が主流となっている。また、''simula''系統のOOP言語では多態性を実現する為の重要な手段として用いられている。 |
|||
'''ポリモーフィズム''' |
|||
様々なオブジェクトタイプ(型式)が扱われるソースコード記述の頻雑性を回避する為に、呼出引数に応じたメソッドをコンパイル時に選択する'''多重定義'''と、引数型に合わせたクラスorメソッドをコンパイル時に全生成する'''型準拠生成'''の書式が備えられた。これらはスタティック(静的)な多態性とされる。ダイナミック(動的)な'''多態性'''としては、特定の場面で扱われるオブジェクト群が共通して持つ階層要素の部分で一括りにして順次処理を行い、条件分岐をより柔軟かつ簡素にする目的で、特定の階層のコードの存在を抽象化して呼び出しポイントとし、オブジェクト共通の階層要素が持つ呼び出しポイントから、それぞれ異なる派生先階層の実装コードを呼び出すという'''仮想関数'''の仕組みが実装された。また、オブジェクトが特定の階層要素を持つかチェックした上でその階層に準じた型変化を行い、そのオブジェクトを引数にした多重定義と仮想関数呼び出しの併せ技である'''多択展開'''の機能も編み出され、データがコードを制御するというOOPの理念は明確に表現された。 |
|||
'''メッセージパッシング''' |
|||
上述の三つの仕様と異なり、メッセージパッシングはオブジェクト指向そのものと肩を並べるパラダイムであり、オブジェクト指向が示す機能の大半を結果的に実現出来る「[[メタデータ|メタ]]」的な土台となった。これは従来のサブルーチンコールの形態を変えたもので、基本はバイトデータ('''メッセージ''')の送受信でメソッド名とパラメータ値およびリターン値をやり取りする仕組みだった。オブジェクトのレシーバー関数が引数として渡されたメッセージを読み込み、オブジェクト内部でそれに準じた処理を行い、結果をリターンした。レシーバーの仕組みは結果的に「内部隠蔽」を実現出来た。オブジェクト内部での自由自在な処理実装によるリターンはこれも結果的に「多態性」を実現出来た。メッセージパッシングがもたらす変幻自在なメソッドの選択実行は、通信網を介して他サーバー上で実行されてるサービスプロセスを自身のソフトウェアの一部として扱う様な事も可能にした。あらゆる手法に応用出来るメッセージパッシングは、その適用範囲を広げるほど開発工数と実行負荷も増大するので、決してオールマイティに用いられるものではないが、ソフトウェアのモジュール化およびコンポーネント化をよりダイナミックに、そしてグローバルに発展させる潜在力を秘めており、OOPがもたらす実装の拡張性を大きく広げた。 |
|||
⚫ | |||
オブジェクト指向プログラミングという考え方が生まれた背景には、計算機の性能向上によって従来より大規模な[[ソフトウェア]]が書かれるようになってきたということが挙げられる。大規模なソフトウェアが書かれコードも複雑化してゆくにつれ、ソフトウェア開発コストが上昇し、[[1960年代]]には「[[ソフトウェア危機]] ({{Lang|en|software crisis}})」といったようなことも危惧されるようになってきた。そこでソフトウェアの[[再利用 (プログラミング)|再利用]]、[[部品化 (プログラミング)|部品化]]といったようなことを意識した仕組みの開発や、[[ソフトウェア開発工程]]の体系化('''[[ソフトウェア工学]]''' ({{Lang|en|software engineering}}) の誕生)などが行われるようになった。 |
オブジェクト指向プログラミングという考え方が生まれた背景には、計算機の性能向上によって従来より大規模な[[ソフトウェア]]が書かれるようになってきたということが挙げられる。大規模なソフトウェアが書かれコードも複雑化してゆくにつれ、ソフトウェア開発コストが上昇し、[[1960年代]]には「[[ソフトウェア危機]] ({{Lang|en|software crisis}})」といったようなことも危惧されるようになってきた。そこでソフトウェアの[[再利用 (プログラミング)|再利用]]、[[部品化 (プログラミング)|部品化]]といったようなことを意識した仕組みの開発や、[[ソフトウェア開発工程]]の体系化('''[[ソフトウェア工学]]''' ({{Lang|en|software engineering}}) の誕生)などが行われるようになった。 |
||
31行目: | 52行目: | ||
[[1980年]]代後半に次々と生まれたオブジェクト指向分析・設計論は、[[Smalltalk]]を源流とするオブジェクト指向を基に組み立てられた。このときSmalltalkは健在であったが広く普及しているとは言えず[[C++]]で[[実装]]する機会が多かったが、C++はSmalltalkとは思想的にやや異なることと、仕様の複雑さが問題とされた。このニーズを受けC++の提示した現実解と、Smalltalk的理想論を融合するものとして、構文面ではシンプル化しながらも強くC++の影響を受けつつ、一方で用語や思想面でSmalltalk色を濃くした{{Citation needed|date=2018年2月}}[[Java]]([[1991年]])が作られた。バランス感覚に長けたJavaの登場によって[[オブジェクト指向開発]]に必要な要素が全てそろい、[[1990年代]]後半からオブジェクト指向は広く普及するようになった。 |
[[1980年]]代後半に次々と生まれたオブジェクト指向分析・設計論は、[[Smalltalk]]を源流とするオブジェクト指向を基に組み立てられた。このときSmalltalkは健在であったが広く普及しているとは言えず[[C++]]で[[実装]]する機会が多かったが、C++はSmalltalkとは思想的にやや異なることと、仕様の複雑さが問題とされた。このニーズを受けC++の提示した現実解と、Smalltalk的理想論を融合するものとして、構文面ではシンプル化しながらも強くC++の影響を受けつつ、一方で用語や思想面でSmalltalk色を濃くした{{Citation needed|date=2018年2月}}[[Java]]([[1991年]])が作られた。バランス感覚に長けたJavaの登場によって[[オブジェクト指向開発]]に必要な要素が全てそろい、[[1990年代]]後半からオブジェクト指向は広く普及するようになった。 |
||
== |
== OOP言語一覧 == |
||
オブジェクト指向プログラミングをサポートする機能を備える代表的な[[プログラミング言語]]([[オブジェクト指向プログラミング言語]])としては以下のようなものが挙げられる: |
オブジェクト指向プログラミングをサポートする機能を備える代表的な[[プログラミング言語]]([[オブジェクト指向プログラミング言語]])としては以下のようなものが挙げられる: |
||
88行目: | 109行目: | ||
: 「純粋」という用語をどう定義するかによるが、たとえば[[Smalltalk]]や[[Ruby]]のように「あらゆる対象がオブジェクトである」という言語(たとえば、Rubyではnilも、ヌルポインタのようなものではなく、NilClassのインスタンスというオブジェクトである。この場合対照例としては、通常の数値型などがオブジェクトでない[[Java]]であろう)を「純粋だ」と言う者もいれば、「当初からオブジェクト指向プログラミング言語として設計された」言語を「純粋だ」と言う者もいる(こちらの定義ではJavaも含まれるだろうか)。例としては、以上のどちらの基準か、それとも別の定義によるものかは知らないが、[[Smalltalk]]、[[Eiffel]]、[[Self]]、[[Ruby]]、[[Swift (プログラミング言語)|Swift]]などを挙げる者がいる。 |
: 「純粋」という用語をどう定義するかによるが、たとえば[[Smalltalk]]や[[Ruby]]のように「あらゆる対象がオブジェクトである」という言語(たとえば、Rubyではnilも、ヌルポインタのようなものではなく、NilClassのインスタンスというオブジェクトである。この場合対照例としては、通常の数値型などがオブジェクトでない[[Java]]であろう)を「純粋だ」と言う者もいれば、「当初からオブジェクト指向プログラミング言語として設計された」言語を「純粋だ」と言う者もいる(こちらの定義ではJavaも含まれるだろうか)。例としては、以上のどちらの基準か、それとも別の定義によるものかは知らないが、[[Smalltalk]]、[[Eiffel]]、[[Self]]、[[Ruby]]、[[Swift (プログラミング言語)|Swift]]などを挙げる者がいる。 |
||
== OOP言語の仕組み == |
|||
⚫ | |||
== オブジェクト指向の応用 == |
|||
プログラミング(ただし、ごく狭義の。普通は以下のようなものもプログラミングの工程に含まれる)にとどまらず、以下のようなより広い範囲にオブジェクト指向は応用されている。 |
|||
* [[オブジェクト指向分析設計]] |
|||
* [[オブジェクト指向モデリング]] |
|||
* [[オブジェクトモデル化技法]] |
|||
また、当初はJavaないしC++における[[デザインパターン (ソフトウェア)|デザインパターン]]を表記・記述するための(図形を含む)言語として設計が始まった[[統一モデリング言語]]は、ツールベンダ等の意向により以上のような応用をサポートするためのものとして、仕様は大幅に膨れている。 |
|||
== オブジェクト指向プログラミング言語の仕組み == |
|||
オブジェクト指向プログラミング言語は、相互に'''[[#メッセージ|メッセージ]]'''を送りあう'''[[オブジェクト (プログラミング)|オブジェクト]]'''の集まりとして[[プログラム (コンピュータ)|プログラム]]を構成することができる仕組みを持つ。 |
オブジェクト指向プログラミング言語は、相互に'''[[#メッセージ|メッセージ]]'''を送りあう'''[[オブジェクト (プログラミング)|オブジェクト]]'''の集まりとして[[プログラム (コンピュータ)|プログラム]]を構成することができる仕組みを持つ。 |
||
そのために、少なくともオブジェクトについての3つの仕組みと、オブジェクトの管理についての3つの仕組みが必要となる。 |
そのために、少なくともオブジェクトについての3つの仕組みと、オブジェクトの管理についての3つの仕組みが必要となる。 |
||
;オブジェクトの仕組み |
;オブジェクトの仕組み |
||
:* オブジェクトに蓄えられる情報、データを表現する仕組み。 |
:* オブジェクトに蓄えられる情報、データを表現する仕組み。 |
||
120行目: | 133行目: | ||
* 概念的には コードとデータが一つになっている。 |
* 概念的には コードとデータが一つになっている。 |
||
==== オブジェクト |
==== オブジェクト概念の実現方法 ==== |
||
実際のプログラムでは、全てのオブジェクトが互いに全く異なった存在ではなくオブジェクトは種類に分けることが出来る。 |
実際のプログラムでは、全てのオブジェクトが互いに全く異なった存在ではなくオブジェクトは種類に分けることが出来る。 |
||
127行目: | 140行目: | ||
このような場合、各オブジェクトがそれぞれメッセージ処理のコード(前述の「振る舞い」に当たる)を独自に備えていては無駄である。そこでオブジェクト指向プログラミング言語がオブジェクトを実現する際には多くの場合、内部的にはオブジェクトを2つの部分に分けている。 |
このような場合、各オブジェクトがそれぞれメッセージ処理のコード(前述の「振る舞い」に当たる)を独自に備えていては無駄である。そこでオブジェクト指向プログラミング言語がオブジェクトを実現する際には多くの場合、内部的にはオブジェクトを2つの部分に分けている。 |
||
'''同一種類のオブジェクトの間で変わらない共通部分''' |
|||
一つは同一種類のオブジェクトに共有される部分、例えばメッセージ処理のコード(振る舞い)や定数(どのオブジェクトでも異ならないデータ)の類である。 |
一つは同一種類のオブジェクトに共有される部分、例えばメッセージ処理のコード(振る舞い)や定数(どのオブジェクトでも異ならないデータ)の類である。 |
||
'''同一種類のオブジェクトの間で変わる個々の部分''' |
|||
もう一つは同一種類のオブジェクトでもそれぞれ異なる部分、典型的には各オブジェクトが保持するデータ群である。 |
もう一つは同一種類のオブジェクトでもそれぞれ異なる部分、典型的には各オブジェクトが保持するデータ群である。 |
||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
{{seealso|this (プログラミング)}} |
{{seealso|this (プログラミング)}} |
||
そしてあるオブジェクトOにメッセージを配送し適切なメッセージ処理コード(振る舞い)を呼び出す際には、まず対象となるオブジェクトOについて共通部分の格納場所を見つけて適切なコードを選び出し、次にそのコードに対して処理対象となるオブジェクトO固有のデータの所在を示す'''オブジェクトID'''を渡すようになっている。 |
そしてあるオブジェクトOにメッセージを配送し適切なメッセージ処理コード(振る舞い)を呼び出す際には、まず対象となるオブジェクトOについて共通部分の格納場所を見つけて適切なコードを選び出し、次にそのコードに対して処理対象となるオブジェクトO固有のデータの所在を示す'''オブジェクトID'''を渡すようになっている。 |
||
141行目: | 162行目: | ||
また各オブジェクトの固有データから共通部分の格納場所を見つける方法もまた各言語により異なり、その言語の開発目的に応じて実に多種多様である。 |
また各オブジェクトの固有データから共通部分の格納場所を見つける方法もまた各言語により異なり、その言語の開発目的に応じて実に多種多様である。 |
||
'''JavaScriptの場合''' |
|||
例えば[[JavaScript]]の場合、各オブジェクトは[[連想配列]]であり、名前で表現されたメッセージのIDからメッセージ処理コードである関数への参照を直接見つけ出す。各オブジェクトの固有データもその連想配列に格納されていて、メッセージを処理する関数には連想配列のアドレスが渡される。 |
例えば[[JavaScript]]の場合、各オブジェクトは[[連想配列]]であり、名前で表現されたメッセージのIDからメッセージ処理コードである関数への参照を直接見つけ出す。各オブジェクトの固有データもその連想配列に格納されていて、メッセージを処理する関数には連想配列のアドレスが渡される。 |
||
'''Selfの場合''' |
|||
[[Self]]のような[[プロトタイプベース|インスタンスベース]]のオブジェクト指向プログラミング言語では、プロトタイプとなるオブジェクトがメッセージを処理するコードも保持しており、オブジェクトがクローンされて作成されるときにそのプロトタイプのありかを示す情報もコピーされ、メッセージは受け取ったオブジェクトのIDを添えてプロトタイプに送られて処理される(Selfでは実行効率上の問題から後に内部的にクラスを作って利用するようになっている)。 |
[[Self]]のような[[プロトタイプベース|インスタンスベース]]のオブジェクト指向プログラミング言語では、プロトタイプとなるオブジェクトがメッセージを処理するコードも保持しており、オブジェクトがクローンされて作成されるときにそのプロトタイプのありかを示す情報もコピーされ、メッセージは受け取ったオブジェクトのIDを添えてプロトタイプに送られて処理される(Selfでは実行効率上の問題から後に内部的にクラスを作って利用するようになっている)。 |
||
'''クラスベースの言語の場合''' |
|||
最も普及している[[クラスベース]]の言語では、共通部分はオブジェクトの種類を表現するクラスに保持され、各オブジェクトは固有データと共にそのクラスのIDを保持する。そしてオブジェクトに送られるメッセージはその送り先オブジェクトにあるクラスのIDからクラスを見つけ、その中からメッセージを処理するコードを見つけ出し、処理対象となっているオブジェクトのIDを付してそのコードを呼び出す仕組みになっている。 |
最も普及している[[クラスベース]]の言語では、共通部分はオブジェクトの種類を表現するクラスに保持され、各オブジェクトは固有データと共にそのクラスのIDを保持する。そしてオブジェクトに送られるメッセージはその送り先オブジェクトにあるクラスのIDからクラスを見つけ、その中からメッセージを処理するコードを見つけ出し、処理対象となっているオブジェクトのIDを付してそのコードを呼び出す仕組みになっている。 |
||
168行目: | 192行目: | ||
=== クラス === |
=== クラス === |
||
{{main|クラス (コンピュータ)}} |
{{main|クラス (コンピュータ)}} |
||
==== クラスベース ==== |
|||
'''クラス''' (class) は大多数のオブジェクト指向プログラミング言語で提供されている仕組みであり、上記の機能の殆ど全てに関わりがある。概念的にはクラスはオブジェクトの種類を表す。このためオブジェクトはクラスに'''属する'''という言い方をする。あるクラスに属するオブジェクトのことをそのクラスの'''インスタンス''' (instance) と呼ぶ。[[データ型]]の理論から見た場合クラスは型を定義する手段の一つである。クラスによってオブジェクトを記述する言語を'''[[クラスベース]]''' ({{Lang|en|class-based}}) のオブジェクト指向プログラミング言語と呼ぶ。 |
'''クラス''' (class) は大多数のオブジェクト指向プログラミング言語で提供されている仕組みであり、上記の機能の殆ど全てに関わりがある。概念的にはクラスはオブジェクトの種類を表す。このためオブジェクトはクラスに'''属する'''という言い方をする。あるクラスに属するオブジェクトのことをそのクラスの'''インスタンス''' (instance) と呼ぶ。[[データ型]]の理論から見た場合クラスは型を定義する手段の一つである。クラスによってオブジェクトを記述する言語を'''[[クラスベース]]''' ({{Lang|en|class-based}}) のオブジェクト指向プログラミング言語と呼ぶ。 |
||
180行目: | 205行目: | ||
クラスは非常に多くのオブジェクト指向プログラミング言語で提供されている機能ではあるが、オブジェクト指向プログラミング言語に必須の機能というわけではない。実際にオブジェクトの管理や、データ・メンバや[[メソッド (計算機科学)|メソッド]]の記述、[[継承 (プログラミング)|継承]]に際してクラスという仕組みに依存せずに、もしくはクラスという仕組み自体を持たずに別の手段でこれらを実現している言語も存在する。このような言語を'''[[プロトタイプベース|インスタンスベース]]''' (''{{Lang|en|instance-based}}'')、'''オブジェクトベース''' (''object-based'') あるいは'''プロトタイプベース''' (''{{Lang|en|prototype-based}}'') のオブジェクト指向プログラミング言語と呼ぶ。インスタンスベースまたはそれに類するのオブジェクト指向プログラミング言語には以下のようなものがある: |
クラスは非常に多くのオブジェクト指向プログラミング言語で提供されている機能ではあるが、オブジェクト指向プログラミング言語に必須の機能というわけではない。実際にオブジェクトの管理や、データ・メンバや[[メソッド (計算機科学)|メソッド]]の記述、[[継承 (プログラミング)|継承]]に際してクラスという仕組みに依存せずに、もしくはクラスという仕組み自体を持たずに別の手段でこれらを実現している言語も存在する。このような言語を'''[[プロトタイプベース|インスタンスベース]]''' (''{{Lang|en|instance-based}}'')、'''オブジェクトベース''' (''object-based'') あるいは'''プロトタイプベース''' (''{{Lang|en|prototype-based}}'') のオブジェクト指向プログラミング言語と呼ぶ。インスタンスベースまたはそれに類するのオブジェクト指向プログラミング言語には以下のようなものがある: |
||
* |
*[[Self]] |
||
* |
*[[JavaScript]] |
||
* |
*[[NewtonScript]] |
||
* |
*[[ドリトル (プログラミング言語)|ドリトル]] |
||
* Squeak [[Etoys|eToys]]([[Squeak]]の非開発者向けビジュアルスクリプト言語。SqueakToys とも) |
* Squeak [[Etoys|eToys]]([[Squeak]]の非開発者向けビジュアルスクリプト言語。SqueakToys とも) |
||
210行目: | 235行目: | ||
ただし、Smalltalkのクラス変数はC++やJavaのクラス変数とは異なる。Smalltalkにおいて、C++やJavaのクラス変数と同等となる変数は'''プール辞書''' ({{Lang|en|pool dictionary}}) と呼ばれる。 |
ただし、Smalltalkのクラス変数はC++やJavaのクラス変数とは異なる。Smalltalkにおいて、C++やJavaのクラス変数と同等となる変数は'''プール辞書''' ({{Lang|en|pool dictionary}}) と呼ばれる。 |
||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
=== メソッド === |
=== メソッド === |
||
223行目: | 242行目: | ||
[[C++]]ではメソッドは'''メンバ関数''' ({{Lang|en|member function}}) や'''関数メンバ''' (function member) と呼ばれる。これはC++が[[グローバル関数]]との区別をつけることと、クラスを[[抽象データ型]]の拡張と位置づけ、非メッセージメタファな言語思想を持っている為である。これら言語ではメソッドをオブジェクト(=クラスやインスタンス)の持ち物として捉えず、クラスに定義された機能要素であると考える。メッセージメタファを否定するため、同時にメッセージを実行するメソッド(手法)ではありえない。 |
[[C++]]ではメソッドは'''メンバ関数''' ({{Lang|en|member function}}) や'''関数メンバ''' (function member) と呼ばれる。これはC++が[[グローバル関数]]との区別をつけることと、クラスを[[抽象データ型]]の拡張と位置づけ、非メッセージメタファな言語思想を持っている為である。これら言語ではメソッドをオブジェクト(=クラスやインスタンス)の持ち物として捉えず、クラスに定義された機能要素であると考える。メッセージメタファを否定するため、同時にメッセージを実行するメソッド(手法)ではありえない。 |
||
==== クラスメソッド |
==== クラスメソッド ==== |
||
[[メソッド (計算機科学)#静的メソッド|クラス・メソッド]]だが、オブジェクト指向の本義に立ち返れば、クラス・メソッドがあるということはクラスがメッセージをレシーブできるという事になる。 |
[[メソッド (計算機科学)#静的メソッド|クラス・メソッド]]だが、オブジェクト指向の本義に立ち返れば、クラス・メソッドがあるということはクラスがメッセージをレシーブできるという事になる。 |
||
234行目: | 253行目: | ||
[[Smalltalk]]ではクラスもオブジェクトの一種であるため当然クラスはメソッドをもつ。 |
[[Smalltalk]]ではクラスもオブジェクトの一種であるため当然クラスはメソッドをもつ。 |
||
'''各言語におけるクラスメソッドの呼称''' |
|||
クラス・メソッドは、C++では'''静的メンバ関数''' ({{Lang|en|static member function}}) と呼ばれる。これはクラスがオブジェクトでない言語にとってはクラス・メソッドより正確な表現であり適切である。("static" とはCの'''static変数'''に由来し'''auto変数'''の対語である。関数コールによりスタック上に生成される関数インスタンスに依存しない変数と、インスタンス生成有無にかかわらず実行できる関数の類似による。) |
クラス・メソッドは、C++では'''静的メンバ関数''' ({{Lang|en|static member function}}) と呼ばれる。これはクラスがオブジェクトでない言語にとってはクラス・メソッドより正確な表現であり適切である。("static" とはCの'''static変数'''に由来し'''auto変数'''の対語である。関数コールによりスタック上に生成される関数インスタンスに依存しない変数と、インスタンス生成有無にかかわらず実行できる関数の類似による。) |
||
Javaではクラス・メソッドは'''静的メソッド''' ({{Lang|en|static method}}) とも呼ばれることもある。 |
Javaではクラス・メソッドは'''静的メソッド''' ({{Lang|en|static method}}) とも呼ばれることもある。 |
||
==== システムメソッド ==== |
|||
==== 特定の機能の割り当てについて ==== |
|||
言語によっては特定の名前のインスタンス・メソッドやクラス・メソッドにオブジェクトの生成、初期化、複製、廃棄といった機能を固定的に割り当てている。 |
言語によっては特定の名前のインスタンス・メソッドやクラス・メソッドにオブジェクトの生成、初期化、複製、廃棄といった機能を固定的に割り当てている。 |
||
==== コンストラクタとデストラクタ ==== |
|||
===== 初期化と廃棄時に利用されるメソッド ===== |
|||
初期化に利用されるメソッドを'''[[コンストラクタ]]'''あるいは'''構築子''' ({{Lang|en|constructor}})、廃棄時に利用されるメソッドを'''[[デストラクタ]]'''あるいは'''消滅子''' ({{Lang|en|destructor}}) と呼んで特別に扱うことが多い。 |
初期化に利用されるメソッドを'''[[コンストラクタ]]'''あるいは'''構築子''' ({{Lang|en|constructor}})、廃棄時に利用されるメソッドを'''[[デストラクタ]]'''あるいは'''消滅子''' ({{Lang|en|destructor}}) と呼んで特別に扱うことが多い。 |
||
252行目: | 272行目: | ||
C++とJavaの場合、各クラスの名前がコンストラクタの名前として使用される。C++では一部のコンストラクタは[[型変換演算子]]として、また[[暗黙の型変換]]にも利用される。 |
C++とJavaの場合、各クラスの名前がコンストラクタの名前として使用される。C++では一部のコンストラクタは[[型変換演算子]]として、また[[暗黙の型変換]]にも利用される。 |
||
=== ガーベジコレクション === |
|||
⚫ | |||
=== アクセス権 === |
=== アクセス権 === |
2019年1月28日 (月) 02:28時点における版
この記事には独自研究が含まれているおそれがあります。 |
オブジェクト指向プログラミング(オブジェクトしこうプログラミング|英: object-oriented programming/OOP)は、オブジェクト指向の考え方[1]を取り入れたコンピュータ・プログラミング技術である。プログラムを「オブジェクト」の集合体として構築し、オブジェクトはデータ(状態)を主とし、コード(動作)を従として構成され、無数のオブジェクトが相互に作用し合うようにして任意の処理が実行される。オブジェクトとは対象物を意味し、CPU演算処理(コード)が参照ないし変動の対象とするデータ全般と考える事が出来る。「どの様にデータを扱うか」ではなく「データがどの様に扱われるか」を念頭に置いてプログラムを組み立てる手法がOOPという事になる。
オブジェクト指向プログラミングは、ソフトウェアの大規模化に伴い、より効率的な開発手法が模索される中で1960年代に考案された。OOP言語は歴史的にクラス構造を中心とするSimula系統と、メッセージ機能を主体とするSmalltalk系統に分けられるが、後にC++やJavaなどの言語が派生した前者が主流となった。この前者のオブジェクト指向が示す「内部隠蔽」「継承」「多態性」の三仕様を備えていれば、OOP言語であると見なされる。
仕様
OOP環境の主な仕様は以下の通りである。括弧内の名称はやや意訳したものである。
エンカプセレーション
従来のプログラミング環境では、「動作」を定義するコードが、「状態」を表現するデータを扱う形でプログラムを記述するのが一般的だったが、開発規模の拡大に伴い、解決の難しいソフトウェア・バグ原因の大半は「データの予期せぬ変動」と「各データ間の予期せぬ不整合」である事が経験則で知られるようになって来た。そこでデータを中心にしてプログラムを組み立てようとする考え方が生まれ、データ群とそれに付随するコード群をまとめたオブジェクトというプログラミング概念が編み出された。従来の「コードの為のデータ」とは逆に「データの為のコード」としたのは、データを変動させたコード位置を容易に特定出来るようにしてバグ発見に繋げる為であった。この目的に沿って特定のデータを扱えるコード範囲を厳密に定めた内部隠蔽の仕様は、アクセス許可外のコード記述をエラー警告して、想定外のデータ変動をもたらす人的ミスを減らした。
インヘリタンス
また、プログラム内の膨大な数のデータは通常グループ化(構造体)されて扱われていたが、各構造体間にまたがる共通のデータ集合が多数出現し、その冗長性と整合の頻雑さの問題が浮き彫りとなっていた。これを解決する為にOOPは、構造体=オブジェクトを複数の階層要素に分け、共通のデータ集合を親階層とし、派生する子階層に親階層の参照アドレスを持たせて連結する構造とした。A階層から成る親オブジェクトから派生した子オブジェクトはA+B階層として構成され、これが継承と呼ばれた。コードから参照されたデータがB階層に無い時は、次のA階層に有るか探す仕組みとなり、この連鎖によって傍からは一つのオブジェクトとして存在した。なお、派生クラスで任意のデータとメソッドを追加出来る継承の機能は、同時にコードの再利用性を高めるとも見なされたが、深い継承がもたらす構成把握の難化がネックとなってほぼ否定されるに到った。現在では深い継承は回避される様になり、代わりに共通メソッド呼出テーブル(Javaのinterfaceなど)を実装させる横に長い継承が主流となっている。また、simula系統のOOP言語では多態性を実現する為の重要な手段として用いられている。
ポリモーフィズム
様々なオブジェクトタイプ(型式)が扱われるソースコード記述の頻雑性を回避する為に、呼出引数に応じたメソッドをコンパイル時に選択する多重定義と、引数型に合わせたクラスorメソッドをコンパイル時に全生成する型準拠生成の書式が備えられた。これらはスタティック(静的)な多態性とされる。ダイナミック(動的)な多態性としては、特定の場面で扱われるオブジェクト群が共通して持つ階層要素の部分で一括りにして順次処理を行い、条件分岐をより柔軟かつ簡素にする目的で、特定の階層のコードの存在を抽象化して呼び出しポイントとし、オブジェクト共通の階層要素が持つ呼び出しポイントから、それぞれ異なる派生先階層の実装コードを呼び出すという仮想関数の仕組みが実装された。また、オブジェクトが特定の階層要素を持つかチェックした上でその階層に準じた型変化を行い、そのオブジェクトを引数にした多重定義と仮想関数呼び出しの併せ技である多択展開の機能も編み出され、データがコードを制御するというOOPの理念は明確に表現された。
メッセージパッシング
上述の三つの仕様と異なり、メッセージパッシングはオブジェクト指向そのものと肩を並べるパラダイムであり、オブジェクト指向が示す機能の大半を結果的に実現出来る「メタ」的な土台となった。これは従来のサブルーチンコールの形態を変えたもので、基本はバイトデータ(メッセージ)の送受信でメソッド名とパラメータ値およびリターン値をやり取りする仕組みだった。オブジェクトのレシーバー関数が引数として渡されたメッセージを読み込み、オブジェクト内部でそれに準じた処理を行い、結果をリターンした。レシーバーの仕組みは結果的に「内部隠蔽」を実現出来た。オブジェクト内部での自由自在な処理実装によるリターンはこれも結果的に「多態性」を実現出来た。メッセージパッシングがもたらす変幻自在なメソッドの選択実行は、通信網を介して他サーバー上で実行されてるサービスプロセスを自身のソフトウェアの一部として扱う様な事も可能にした。あらゆる手法に応用出来るメッセージパッシングは、その適用範囲を広げるほど開発工数と実行負荷も増大するので、決してオールマイティに用いられるものではないが、ソフトウェアのモジュール化およびコンポーネント化をよりダイナミックに、そしてグローバルに発展させる潜在力を秘めており、OOPがもたらす実装の拡張性を大きく広げた。
歴史
オブジェクト指向プログラミングという考え方が生まれた背景には、計算機の性能向上によって従来より大規模なソフトウェアが書かれるようになってきたということが挙げられる。大規模なソフトウェアが書かれコードも複雑化してゆくにつれ、ソフトウェア開発コストが上昇し、1960年代には「ソフトウェア危機 (software crisis)」といったようなことも危惧されるようになってきた。そこでソフトウェアの再利用、部品化といったようなことを意識した仕組みの開発や、ソフトウェア開発工程の体系化(ソフトウェア工学 (software engineering) の誕生)などが行われるようになった。
このような流れの中で、プログラムを構成するコードとデータのうちコードについては手続きや関数といった仕組みを基礎に整理され、その構成単位をブラックボックスとすることで再利用性を向上し、部品化を推進する仕組みが提唱され構造化プログラミング (structured programming) として1967年にエドガー・ダイクストラ (Edsger Wybe Dijkstra) らによってまとめあげられた(プログラミング言語の例としてはPascal 1971年)。なお、それに続けて「しかしデータについては相変わらず主記憶上の記憶場所に置かれている限られた種類の基本データ型の値という比較的低レベルの抽象化から抜け出せなかった。これはコードはそれ自身で意味的なまとまりを持つがデータはそれを処理するコードと組み合わせないと十分に意味が表現できないという性質があるためであった。」といったように、ほぼ間違いなく説明されている。
そこでデータを構造化し、ブラックボックス化するために考え出されたのが、データ形式の定義とそれを処理する手続きや関数をまとめて一個の構成単位とするという考え方でモジュール (module) と呼ばれる概念である(プログラミング言語の例としてはModula-2 1979年)。しかし定義とプログラム内の実体が一対一に対応する手続きや関数とは異なり、データはその形式の定義に対して値となる実体(インスタンスと呼ばれる)が複数存在し、各々様々な寿命を持つのが通例であるため、そのような複数の実体をうまく管理する枠組みも必要であることがわかってきた。そこで単なるモジュールではなく、それらのインスタンスを整理して管理する仕組み(例えばクラスとその継承など)まで考慮して生まれたのがオブジェクトという概念である(プログラミング言語の例としてはSimula 1962年)
Simulaのオブジェクトとクラスというアイデアは異なる二つの概念に継承される。一つはシステム全てをオブジェクトの集合と捉え、オブジェクトの相互作用をメッセージに喩えた「オブジェクト指向」である。オブジェクト間の相互作用をメッセージの送受と捉えることで、オブジェクトは受信したメッセージに見合った手続き単位(≒関数)を自身で起動すると考える。結果オブジェクトは自身の持つ手続きのカプセル化を行うことができ、メッセージが同じでもレシーバオブジェクトによって行われる手続きは異なる――多相性(ポリモーフィズム)を実現した(このメッセージを受け実行される手続き単位は、メッセージで依頼されたことを行うための「手法」の意味でメソッドと呼ばれる)。この思想に基づき作られたのがSmalltalk(1972年)であり、オブジェクト指向という言葉はこのとき作られた。
一方、Smalltalkとは別にSimulaの影響を受け作られたC++(1979年)は抽象データ型のスーパーセットとしてのクラス、オブジェクトに注目し、オブジェクト指向をカプセル化、継承、多相性をサポートするものと再定義した(その際、実行時速度重視及びコンパイラ設計上の制約により、変数メタファである動的束縛の特徴は除外された)。これらは当初抽象データ型、派生、仮想関数と呼ばれ、オブジェクトのメンバ関数を実体ではなくポインタとすることで、継承関係にあるクラスのメンバ関数のオーバーライド(上書き)を可能にしたことで、多相性を実現した(この流儀ではメッセージメタファはオブジェクト指向に必須ではないものと定義し、オブジェクトの持つ手続きをメソッドとは呼ばずメンバ関数と呼ぶ)。この他、Smalltalkにある動的束縛の類似的な機能としてオーバーロード(多重定義)が実装されている。
Smalltalkはこの「全てをオブジェクトとその相互作用で表現する」というデザインに立ち設計されたため、全てをファイルと捉えるファイル指向オペレーティングシステムからの脱却と、プログラムをフロー制御された手続きと捉える手続き型言語からの脱却が行われた。そのためSmalltalkは自身がオブジェクト指向オペレーティングシステムでもあること、メッセージ・パッシングなどの特徴を持った。これは当時のプログラム言語としては特異的であり、ガベージコレクタを必要としインタプリタ形式で実行される処理の重さも手伝って先進的ではありながら普及しがたいものであると捉えられた。また、メッセージでの多相性は、変数へのオブジェクトの動的束縛が前提となるため、静的型チェックが必要な実行時性能重視のコンパイラ言語にとって、実装から除外すべき特徴となった。
C++の創始者ビャーネ・ストロヴストルップは、Smalltalkのような論理美の追求よりも、現用としての実用性を重視した。そのため、C++の再定義した「オブジェクト指向」はこれらの問題を全てクリアにし、既存言語の拡張としてオブジェクト指向機能を実装できることでブレイクスルーを迎え急速に普及する。これによりメッセージ送信という考え方はやや軽視されるようになり、オブジェクト指向とはC++の再定義したものと広く認知されるようになった。
1980年代後半に次々と生まれたオブジェクト指向分析・設計論は、Smalltalkを源流とするオブジェクト指向を基に組み立てられた。このときSmalltalkは健在であったが広く普及しているとは言えずC++で実装する機会が多かったが、C++はSmalltalkとは思想的にやや異なることと、仕様の複雑さが問題とされた。このニーズを受けC++の提示した現実解と、Smalltalk的理想論を融合するものとして、構文面ではシンプル化しながらも強くC++の影響を受けつつ、一方で用語や思想面でSmalltalk色を濃くした[要出典]Java(1991年)が作られた。バランス感覚に長けたJavaの登場によってオブジェクト指向開発に必要な要素が全てそろい、1990年代後半からオブジェクト指向は広く普及するようになった。
OOP言語一覧
オブジェクト指向プログラミングをサポートする機能を備える代表的なプログラミング言語(オブジェクト指向プログラミング言語)としては以下のようなものが挙げられる:
- Simula 1962年
- オブジェクトとクラスを導入した最初の言語。また、C++が再定義したオブジェクト指向プログラミング言語の要件を満たすことから、最初のオブジェクト指向プログラミング言語とされることもある。クラスベース。当初はシミュレーション記述用に開発されたものであったが、後に汎用化された。
- Smalltalk 1972年
- オブジェクト間の相互作用にメッセージという機構を導入した最初の言語。オブジェクト指向と言う言葉は、Smalltalkのプログラミングスタイルを説明するために作られた。動的な型付けにより高い柔軟性を持つ。
- C++ 1979年
- C言語のオブジェクト指向拡張。Simula言語風にクラスを定義でき、C言語の型システムを強化している。当初はC言語に対して上位互換であった。また、強い型付けによりC言語譲りの実行効率の高いコードが生成できる。
- Objective-C 1983年
- C言語のオブジェクト指向拡張。C言語のコードとSmalltalk型のオブジェクトシステムを混在させたもの。オブジェクトシステム自体はCで書かれたランタイム。
- Eiffel 1986年
- 強い型付けを行う型システムや表明など堅牢性を強く意識して設計されたオブジェクト指向プログラミング言語。C++にテンプレートが導入される前に総称型を導入していた。
- Self 1987年
- 最初のインスタンスベースのオブジェクト指向プログラミング言語。
- CLOS 1988年
- 関数型言語Common Lispのオブジェクト指向仕様。
- Modula-3 1988年
- モジュールを実現していたModula-2言語のオブジェクト指向拡張。
- Python 1990年
- 最初のオブジェクト指向スクリプト言語。
- Sather 1990年
- Eiffelを拡張したもの。実行効率面での工夫が見られる。
- NewtonScript 1993年
- PDA環境用に修正されたSelf言語。PDAの性質を生かすため永続オブジェクトをサポートしている。
- Ruby 1993年
- オブジェクト指向スクリプト言語。クラスのMix-inなどのユニークな機能を持つほか、正規表現に渡るまで全てがクラスとして実装されていることが特徴。
- Perl 1994年
- Perl 5.0にてオブジェクト指向に対応。既存のPerl言語に後付けで拡張されたため記法は特殊だが、機能面では他のオブジェクト指向言語に引けを取らない。
- Adaのオブジェクト指向拡張 1995年
- 恐らく最も仕様が複雑なオブジェクト指向プログラミング言語。
- Java 1995年
- 仮想マシン(Java仮想マシン)上で動作することによる高い可搬性、リフレクションやスレッドの標準サポートと充実したライブラリ群で知られているオブジェクト指向プログラミング言語。
- Object Pascal (Delphi) 1995年
- Pascal言語のオブジェクト指向拡張。
- JavaScript 1996年
- ウェブページ上で実行することを主目的に開発されたスクリプト言語。プロトタイプベースの言語である。
- OCaml 1996年
- 関数型言語MLのオブジェクト指向拡張。クラスが型推論機構に組み込まれているという特徴がある。
- PHP 2000年
- 動的にWebページを生成することを主目的としたスクリプト言語。バージョン4からオブジェクト指向に対応し、バージョン5でアクセス権やインターフェースなど、オブジェクト指向の強化が行われている。
- C# 2000年
- Javaとよく似た作りでJavaと同じく仮想マシン上で動作するが様々な部分で拡張されている。.NET Framework上で動く。
- Visual Basic.NET 2002年
- 既存のVisual Basic(6.0以前)を大きく改訂してオブジェクト指向に対応。C#同様.NET Framework上で動く。
- COBOL 2002年
- 2002年に制定された国際規格に、オブジェクト指向機能が採用された。1994年には、国際規格案を先取りした商用コンパイラ上で利用可能になった。オブジェクト指向構文を使わないCOBOLプログラムと混在できる。
- Ceylon 2011年
- Java をもとにつくられ、Java のもつ長所と短所を考慮しつつ、商用言語として再設計された言語。
- Swift 2014年
- Objective-Cのオブジェクトを引き継ぎながらも、ベースのC言語を取り除いて新しく再設計された言語。
また、以下のような分類もある。
- ハイブリッド型オブジェクト指向プログラミング言語 (hybrid object-oriented language)
- 「ハイブリッド」という用語をどう定義するかによるが、たとえば「手続き型や関数型などの既存の他のパラダイムを主とした言語に、オブジェクト指向機能を拡張した」という言語を「ハイブリッドだ」、と言う者もいれば、「当初からオブジェクト指向を含む、複数のパラダイムを指向する言語として設計された」という言語を「ハイブリッドだ」と言う者もいる。例としては、以上のどちらの基準か、それとも別の定義によるものかは知らないが、OCaml、C++およびObjective-C、CLOS、Object Pascal、object-oriented COBOLなどを挙げる者がいる。
- 純粋なオブジェクト指向プログラミング言語 (pure object-oriented language)
- 「純粋」という用語をどう定義するかによるが、たとえばSmalltalkやRubyのように「あらゆる対象がオブジェクトである」という言語(たとえば、Rubyではnilも、ヌルポインタのようなものではなく、NilClassのインスタンスというオブジェクトである。この場合対照例としては、通常の数値型などがオブジェクトでないJavaであろう)を「純粋だ」と言う者もいれば、「当初からオブジェクト指向プログラミング言語として設計された」言語を「純粋だ」と言う者もいる(こちらの定義ではJavaも含まれるだろうか)。例としては、以上のどちらの基準か、それとも別の定義によるものかは知らないが、Smalltalk、Eiffel、Self、Ruby、Swiftなどを挙げる者がいる。
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
- JavaScript
- NewtonScript
- ドリトル
- Squeak eToys(Squeakの非開発者向けビジュアルスクリプト言語。SqueakToys とも)
なお、クラスベースの言語とインスタンス・ベースの言語との間には明確な境界線はない。たとえば、インスタンス・ベースの代表格ともいえる 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++のメソッド
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()
メソッドがその役割を果たす。ただし、Javaにおけるファイナライザは本当に必要でない限り使用するべきではなく、C++などのデストラクタとは違った意味を持つ。
データ型の理論においては保持されるデータが必ずその型で認められる正しい値の範囲に収まることを保証するため、生成されるオブジェクトのデータ・メンバが必ず適切な構築子によって初期化されるように求める。またオブジェクトが入出力機器やファイルや通信、プロセスやスレッド、ウィンドウとウィジェットなどハードウェアやオペレーティングシステム (OS) が提供する資源を管理するために利用される場合に、構築子や消滅子でそれらの資源の使用開始(オープン処理)や使用終了(クローズ処理)をそれぞれ管理し、通常のメソッドでそれらにまつわる各種サービスを提供するようにすることで、それらのリソースがあたかもプログラム中のオブジェクトであるかのように自然に取り扱うことができるようになる。
C++とJavaの場合、各クラスの名前がコンストラクタの名前として使用される。C++では一部のコンストラクタは型変換演算子として、また暗黙の型変換にも利用される。
ガーベジコレクション
オブジェクト指向プログラミング言語では、オブジェクトへの参照やポインタが多用される。そのため、オブジェクトのメモリへの割り当てに関して、自動ガベージコレクション機能を備えているものが多い。ただし、すべての言語が備えているわけではない。例えば、C++はガベージコレクションを備えていない。
アクセス権
オブジェクト指向プログラミングにおいて、オブジェクトは、カプセル化されておりブラックボックスである。したがって、処理するメッセージのカタログ、つまりインターフェースだけが利用者に公開され、内部の詳細は隠されるのが基本である。しかし、あるクラスのインスタンスの内部だけで利用されるメソッドまで公開してしまうと、利用者にとって煩雑である。また、定数データ・メンバのようなものは一々メソッドでアクセスするようにせず公開してしまっても、カプセル化の利点は失われず効率的でもある。そこで、オブジェクトを定義するプログラマが各データ・メンバやメソッドについて公開・非公開を設定できる機能を用意している言語は多い。
例えば、Javaでは、データ・メンバやメソッドの宣言にpublicと指定すれば、他オブジェクトから自由に利用でき(公開と呼ばれる)、privateと指定すればオブジェクト内だけで利用できるようになる(非公開と呼ばれる)。
しかし、ある機能を提供するのに、一個ではなく一群のクラスに属するオブジェクトでそれを記述するのが相応しい事例がある。そのような場合、関係する一群のオブジェクト間でだけデータ・メンバやメソッドを利用できれば便利である。それを可能にするための拡張がいくつか存在する。例えば、継承を利用しているときに、あるクラスが子孫にだけ利用を許可したいデータ・メンバやメソッドがある場合、Javaではprotectedを指定することでそれを実現できる(限定公開と呼ばれる)。また、ある一群の機能を実現するクラスのライブラリで、その実現に関連するクラスに属するオブジェクトだけがデータ・メンバやメソッド利用できるようにしたい場合も考えられる。また、Javaでは、ライブラリを構成するクラス群を表現するパッケージ (package) という仕組みがあり、特に指定がない場合は同一パッケージに属するクラスのオブジェクト間でのみデータ・メンバやメソッドを相互に利用可能である。その他にも、デザインパターンの一つであるFacade パターンでは、この仕組みがテクニックとして応用されている。また、C++ではフレンド宣言という仕組みがあり、あるクラスで外部非公開に指定されているデータ・メンバやメソッドについて、その利用を許可するクラスや関数のリストをクラス内に列挙することができる。
なお、public、private、protectedというキーワードは、多くのプログラミング言語で用いられているが、その示す意味は言語ごとに差異があるため、注意が必要である。
コンポジション
コンポジションは、複数のオブジェクトがある一つのオブジェクトの構成要素となっている巨大なオブジェクト群をいう。コンポジションのもとにあるオブジェクトは同一の生存期間を持ち、一つの巨大な仮想オブジェクトの構成部品として機能する。