「オブジェクト指向プログラミング」の版間の差分
削除された内容 追加された内容
m →脚注 タグ: 2017年版ソースエディター |
Goldensundown2 (会話 | 投稿記録) |
||
(同じ利用者による、間の8版が非表示) | |||
1行目:
{{プログラミング言語|index=おふしえくとしこうふろくらみんく}}
[[ファイル:Object oriented design object.jpg|境界|右|フレームなし]]
[[ファイル:History of object-oriented programming languages.svg|サムネイル|OOP言語の系譜(水色がOOP)|280x280ピクセル|リンク=Special:FilePath/History_of_object-oriented_programming_languages.svg]]▼
'''オブジェクト指向プログラミング'''(オブジェクトしこうプログラミング、{{Lang-en-short|''object-oriented programming''}}、略語:OOP)は、[[オブジェクト指向]]の[[プログラミングパラダイム|考え方]]<ref>コンピュータ・プログラミングのパラダイムについては『新しいプログラミング・パラダイム』などを参照: http://www.kyoritsu-pub.co.jp/bookdetail/9784320024939</ref>を取り入れた[[プログラミング (コンピュータ)|コンピュータ
== 特徴 ==
プログラミングパラダイムとしてのオブジェクト指向の確立は紆余曲折を経ており(後述)その詳細の解釈も様々であるが、一定の枠組みとなる三つの原則
#[[カプセル化]](''encapsulation'')
24行目:
#**[[多重ディスパッチ]](''multiple dispatch'')
#[[メッセージ (コンピュータ)|メッセージパッシング]](''message passing'')
#ローカル保持(''local retention, protection, hiding of state-process'')
#[[動的束縛|遅延バインディング]](''late binding'')
従来のプログラムは、動作を表現するコードが状態を表現するデータを操作する形で記述されるのが一般的だったが、開発規模の拡大に伴い解決の難しいソフトウェアバグ原因の大半は、データの予期せぬ変動と
カプセル化を推し進めたものとして'''プロトコル'''(''protocol'')がある。これはオブジェクトの構成要素を全隠蔽し、外部アクセス可能な要素だけを抜き出してまとめたものを、純粋抽象クラスまたは界面オブジェクトとして公開する仕組みを指
▲従来のプログラムは、動作を表現するコードが状態を表現するデータを操作する形で記述されるのが一般的だったが、開発規模の拡大に伴い解決の難しいソフトウェアバグ原因の大半は、データの予期せぬ変動と各データ間の不整合である事が経験則で知られるようになって来た。そこでデータを中心にしてプログラムを組み立てようとする考え方が生まれ、その目的に沿うものとしてデータとコードの複合体であるオブジェクトに白羽の矢が立てられた。オブジェクト内のデータは基本的に同じオブジェクト内のコードだけが参照または変動可能とする仕組みが考案され、これがカプセル化と呼ばれた。データを変動させたコード位置の把握を容易にする為に、各データにアクセス出来るコードの範囲を厳密に定めたカプセル化の機能は、範囲外アクセスをコンパイルレベルで禁止して想定外のデータ変動をもたらす人的ミスを減らした。また、データを変動させるコードを呼び出すそのまたコード位置の把握も芋蔓式に必要となったので、コードにもアクセス許可範囲が定められた。更にアクセス範囲の広さにも段階がつけられ、特定の外部をも含んだ柔軟な内部隠蔽の設定が可能となった。
▲カプセル化を推し進めたものとして'''プロトコル'''(''protocol'')がある。これはオブジェクトの構成要素を全隠蔽し、外部アクセス可能な要素だけを抜き出してまとめたものを、純粋抽象クラスまたは界面オブジェクトとして公開する仕組みを指す。仕組み的に外部公開される要素はメソッドに限られ、データもそれ専用のメソッドを通して参照ないし変動される。プロトコルは後述の多態性にも関連しており、特に動的ディスパッチを表現するメカニズムにもなった。多くのプログラミング言語ではインターフェースの名で知られているものである。
; 継承▼
プログラム内の膨大な数のデータは通常グループ化(構造体)されて扱われていたが、複数の構造体間にまたがる共通のデータ集合が数多く目に付くようになり、その冗長さと整合性の問題が浮き彫りとなっていた。これを解決する為に構造体=オブジェクトを複数の階層要素に分け、共通のデータ集合を親階層とし、子階層に親階層のアドレスを持たせて連結する構造が考案された。A階層から成る親オブジェクトから派生した子オブジェクトはA+B階層として構成され、これが継承と呼ばれた。コードから参照されたデータがB階層に無い時は、次のA階層に有るか探す仕組みとなり、この連鎖によって傍からは一つのオブジェクトとして存在した。データと同様に共通のメソッド集合も親階層にまとめられた。その応用として後述の仮想関数と同義である抽象メソッド集合だけの階層を独立させたインターフェースまたは純粋抽象クラスがあり、これを親階層として継承(実装)する行為は'''[[派生型|サブタイピング]]'''(''subtyping'')となった。サブタイピングは後述の多態性のメカニズムの一つでもある。なお、継承クラスで任意の機能を追加出来る継承は従来コードの再利用性を高めるとも考えられたが、深い継承がもたらす構成把握の難化が問題視されてほぼ否定された。継承による再利用性はクラスライブラリの使用内に留まっているのが現状である。
なお、子階層の次のリンク先となる親階層は一本だけでなく複数本持つ事も出来るので、複数の親階層+子階層によるオブジェクト構成は'''多重継承'''と呼ばれた。子階層が持つ親階層アドレスは一般にリスト化されており、自身に無いデータはリスト先頭の親階層から順々に検索された。その親階層が多重継承されてる場合も同様であり、それぞれの枝分かれには深さ優先検索が用いられた。前述のサブタイピングは多重で行われる事が多い。
アドホック多態性は単にソースコードの記述を一部自動化するものである。'''関数オーバーロード'''は引数の並び方パターンによって同じ名前のメソッドをコンパイル時に自動的に差別化する機能である。'''演算子オーバーロード'''は、扱う数値の型に従って宣言された演算記号を関数名と見なすようにし、単項演算子なら右の数値を第一引数とし、二項演算子なら左右の数値をそれぞれ第一第二引数として関数呼び出しのコードが生成されるという仕組みだった。丸括弧の演算子は関数オブジェクトの表現として使用出来た。これらは静的な多態性とされる。▼
パラメータ多態性もソースコードの記述を一部自動化するものである。関数
サブタイプ多態性は動的なものである。最も初期のOOPであるSimula67は、シミュレーション内で扱う多種多様なオブジェクトを継承によって体系化したが、コード部分の細かな違いは共通スーパークラスに属する共通プロシージャ内の分岐フローで処理していた。サブクラスの数だけ分岐構文が増える頻雑さを解消する為に、共通プロシージャをただの住所テーブルにしてサブクラスの実装時に同名プロシージャのアドレスを収納させ、共通プロシージャ呼び出し時にそのアドレスへジャンプするという機能が考案された。住所テーブルと化した共通プロシージャは仮想的存在と見なされたので、この機能は'''仮想関数'''と呼ばれた。'''動的ディスパッチ'''はSmalltalkのオブジェクト設計に由来するものであり、その実装の仕方は様々でやや曖昧な仕様でもある。メッセージを受け取ったレシーバーがオブジェクト内部で動的な状態に従い動的な処理を行って結果を返すというランタイム環境上のプロセスが後に動的ディスパッチのカテゴリで括られた。[[分散コンピューティング]]を表現する[[Object Request Broker|オブジェクト間通信]]とそれに基づく[[ソフトウェアコンポーネント]]も動的ディスパッチに該当するもの
▲アドホック多態性は単にソースコードの記述を一部自動化するものである。'''関数オーバーロード'''は引数の並び方によって同じ名前のメソッドをコンパイル時に自動的に差別化する機能である。'''演算子オーバーロード'''は、扱う数値の型に従って宣言された演算記号を関数名と見なすようにし、単項演算子なら右の数値を第一引数とし、二項演算子なら左右の数値をそれぞれ第一第二引数として関数呼び出しのコードが生成されるという仕組みだった。これらは静的な多態性とされる。
▲パラメータ多態性もソースコードの記述を一部自動化するものである。関数内またはクラス内の型指定部分をワイルドカードにして宣言しておき、ソースコード内で関数またはクラスが実装記述されると、その具体的な型指定を関数内またはクラス内のワイルドカードに当てはめたコード部分がコンパイル時に自動生成されるという機能だった。前者は'''ジェネリック関数'''と呼ばれ、後者はより広い範囲を扱う事から'''ジェネリックプログラミング'''と名付けられた。これらも静的な多態性に位置付けられている。
これは元々は、式指向(''expression-oriented'')プログラミングまたは関数型プログラミングで用いられていた[[高階関数]]の仕組みを[[参照透過性]]と相反する観点から独自に拡張させたパラダイムだった。この視点の下では、変換式に独自の記憶を持たせたものがオブジェクトとなり同時に多態性の表現に繋がった。なお、メッセージパッシングとは如何なる形態であれ、オブジェクトの動作の呼び出しを行うという一点で共通している。その仕組みの基本は関数と同様にパラメータ値付きメソッド名(セレクタ)とリターン値をやり取りする事であり、これがスタックエリアなどを通したサブルーチンコール形態ではなく、バイトデータ羅列の受け渡しで行なわれる場合は[[Object Request Broker|オブジェクト間通信]]に近いものとなった。メッセージパッシングとしての特徴は、パラメータ値とリターン値もそのままオブジェクトとなる事であり、得られたリターン値オブジェクトのメソッドを呼び出してそのまたリターン値を取得し、そのリターン値を別のオブジェクトメソッドを呼び出す為のパラメータ値オブジェクトに出来るといったオブジェクトメソッド呼び出しの連鎖動作が可能な点であった。メッセージパッシング=オブジェクトの相互作用と称される本来のスタイルはこの様なものである。
▲サブタイプ多態性は動的なものである。最も初期のOOPであるSimula67は、シミュレーション内で扱う多種多様なオブジェクトを継承によって体系化したが、コード部分の細かな違いは共通スーパークラスに属する共通プロシージャ内の分岐フローで処理していた。サブクラスの数だけ分岐構文が増える頻雑さを解消する為に、共通プロシージャをただの住所テーブルにしてサブクラスの実装時に同名プロシージャのアドレスを収納させ、共通プロシージャ呼び出し時にそのアドレスへジャンプするという機能が考案された。住所テーブルと化した共通プロシージャは仮想的存在と見なされたので、この機能は'''仮想関数'''と呼ばれた。'''動的ディスパッチ'''はSmalltalkのオブジェクト設計に由来するものであり、その実装の仕方は様々でやや曖昧な仕様でもある。メッセージを受け取ったレシーバーがオブジェクト内部で動的な状態に従い動的な処理を行って結果を返すというランタイム環境上のプロセスが後に動的ディスパッチのカテゴリで括られた。[[分散コンピューティング]]を表現する[[Object Request Broker|オブジェクト間通信]]とそれに基づく[[ソフトウェアコンポーネント]]も動的ディスパッチに該当するものである。仮想関数および動的ディスパッチと同義に扱われる用語に'''[[動的束縛|遅延バインディング]]'''(''late binding'')があるが、これはより幅広い意味を含みOOP外でも用いられる言葉である。'''多重ディスパッチ'''は動的な関数オーバーロードに近いものである。関数コール時または関数ブロック内で、それぞれの引数が動的に型審査されて型変化(''dynamic casting'')された後に、その引数パターンに対応した同名関数または分岐ルーチンに処理が移行されるという動的変化プロセスを指した。'''ダブルディスパッチ'''は多重ディスパッチの亜流的存在であり、二通りの考え方がある。動的型審査および型変化されるBオブジェクトを単一引数にしてAオブジェクトの仮想関数メソッドを呼び出す形態と、多重ディスパッチに用いる引数を二つに限定した形態である。いずれも実行時状態に応じた動的変化プロセスとなった。これは主にデータ集合を対象にして分類、解析、作用といった処理を連続的または再帰的に行うアルゴリズムで用いられた。
Smalltalkの影響を受けた後継言語においても、あらゆるプログラム要素をメッセージで表現しようとする理論の追求は避けられ、例えば制御フローは制御構文で扱われている。またメッセージングに対する認識もシフトし、オブジェクトの代表となるメソッドがパラメータを受け取り多様な処理と移譲を行ういわゆるレシーバーの仕組み自体がメッセージングそのものと見なされるようになった。そのメカニズムを応用した[[Object Request Broker|オブジェクト間通信]]を実現するバイトデータの送受信もメッセージパッシングの代表例となった。また、オブジェクトの単純なメソッドコール自体もメッセージと定義する見解も出たが、これは同時に物議を醸している。この様にメッセージパッシング設計の本質は見失われながらも、その側面的仕様は数々のOOP言語に導入されてもいる。▼
▲; メッセージパッシング
=== ローカル保持 ===
▲これは従来のパラメータ付きサブルーチン呼出とリターン値のやり取りを別の視点から再解釈したのであり、基本はバイトデータ(メッセージ)の送受信でセレクタ(メソッド名)とパラメータ、そしてリターン値をやり取りする仕組みだった。オブジェクトのレシーバー(代表メソッド)が渡されたメッセージを解釈し、オブジェクト内部でそれに準じた処理を行い、結果をリターンした。パラメータとリターン値もまたオブジェクトだったので、リターン値をメッセージ内のパラメータにして後続のオブジェクトへ送る事もでき、そのリターン値にまた別のメッセージを送る事も出来た。Smalltalk設計では真偽値、数値、コードブロックもオブジェクトとなったので、真偽値または数値に対して規定の予約語(セレクタ)と併せたコードブロックをメッセージとして送る事で条件分岐や反復処理といった制御フローを表現出来た。オブジェクトを次々とメッセージとして送るのは順次処理となった。メッセージパッシングとは、オブジェクトにオブジェクトを引き合わせて、双方に関連した手続きまたは制御フローを発生させるルーチンワークを意味しており、この連鎖的かつ再帰的な繰り返しがプログラム全体の流れとなった。この斬新なパラダイムを実際のプログラムの形にする為には比較的緻密な設計が要求され、またパターン化されたプロセスにおいても柔軟さを維持する為のワンステップを必要とするので開発工数とCPU負荷が増大する欠点もあった。これが倦厭されてOOP設計の本質でありながら広くは支持されず、OOPに対する関心の焦点は本来は枝葉の部分であるはずのクラスのメカニズムに移る事になった。
。
=== 遅延バインディング ===
▲Smalltalkの影響を受けた後継言語においても、あらゆるプログラム要素をメッセージで表現しようとする理論の追求は避けられ、例えば制御フローは制御構文で扱われている。またメッセージングに対する認識もシフトし、オブジェクトの代表となるメソッドがパラメータを受け取り多様な処理と移譲を行ういわゆるレシーバーの仕組み自体がメッセージングそのものと見なされるようになった。そのメカニズムを応用した[[Object Request Broker|オブジェクト間通信]]を実現するバイトデータの送受信もメッセージパッシングの代表例となった。また、オブジェクトのメソッドコールもメッセージと定義する見解も出たが、これは同時に物議を醸している。この様にメッセージパッシング設計の本質は見失われながらも、その側面的仕様は数々のOOP言語に導入されてもいる。
。
== 歴史 ==
69 ⟶ 75行目:
== OOP言語一覧 ==
▲[[ファイル:History of object-oriented programming languages.svg|サムネイル|OOP言語の系譜
オブジェクト指向を総体的または部分的にサポートする機能を備えたプログラミング言語の公開は、1980年代後半から顕著となった。OOP言語の分類法は複数あるが、Smalltalkをルーツとするメッセージパッシングの構文が重視されてるか否かで大別される事が多い。そうでないものがOOP言語の主流となっており「C++」「Java」「C#」「Swift」などがその代表とされる。メッセージパッシングを重視するOOP言語には「Smalltalk」「Objective-C」「Self」などがある。言語仕様の中でオブジェクト指向の存在感が比較的高い代表的なプログラミング言語を以下に列挙する。
[[Simula|'''Simula 67''']] 1967年
: 1962年に公開された[[Simula]]の後継版であり、[[クラス (コンピュータ)|クラス]]のプログラミング概念を導入した最初の言語である。現実世界の擬似モデルを観測するシミュレーション・プログラム制作用に開発されたもので、クラスを実メモリに展開したオブジェクトは、その観測対象要素となった。
'''[[Smalltalk]]''' 1972年
120 ⟶ 127行目:
'''[[Kotlin]]''' 2011年
: [[Javaバイトコード]]を出力し、[[Java仮想マシン]]上で動作する
[[Swift (プログラミング言語)|'''Swift''']] 2014年
:高度に整えられたマルチパラダイムプログラミング言語。クラスのメカニズムをベースにしたオブジェクト指向
== OOP言語の仕組み ==
171 ⟶ 178行目:
<ref>クラス/メソッドの定義 (Ruby manual) [http://www.ruby-lang.org/ja/old-man/html/_A5AFA5E9A5B9A1BFA5E1A5BDA5C3A5C9A4CEC4EAB5C1.html]</ref>元々はSmalltalkから始まった用語である。
====
{{seealso|this (プログラミング)}}
そしてあるオブジェクトOにメッセージを配送し適切なメッセージ処理コード(振る舞い)を呼び出す際には、まず対象となるオブジェクトOについて共通部分の格納場所を見つけて適切なコードを選び出し、次にそのコードに対して処理対象となるオブジェクトO固有のデータの所在を示す'''オブジェクトID'''を渡すようになっている。
|