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

削除された内容 追加された内容
11行目:
 
== 特徴 ==
プログラミングパラダイムとしてのオブジェクト指向の確立は紆余曲折を経ており(後述)その詳細の解釈も様々であるが、一定の枠組みとなる三つの原則(''fundamental principle'')または三本の柱(''pillar'')が存在し、それに従った言語仕様を総体的または部分的に備えたプログラミング言語がオブジェクト指向準拠と判別される。1~3はオブジェクト指向プログラミングの三原則とされるものであり[[C++]]を契機にして提唱された。4を加えて四本の柱(''pillar'')とする考えもある。
 
5~7は[[Smalltalk]]が提唱する元祖オブジェクト指向のコンセプトであり、この三者は相互に関連して始めて一つの意味を表現している。その真髄はオブジェクトを媒体にしてコードとデータの融合を目指した高度な抽象化または代数化である。元祖オブジェクト指向は哲学的側面が強いものであり、それを実用的に演繹したものが1~3であると考える事も出来る。
35行目:
 
=== カプセル化 ===
任意のデータ(変数、プロパティ、属性、フィールド)群と、それに関連するコード(関数、プロシージャ、サブルーチン、メソッド)群をひとまとめにしてオブジェクトとし、外部に対して必要とされるデータとコードのみを公開する仕組みがカプセル化と呼ばれる。オブジェクトが持つコードは一般にメソッドと呼ばれる事が多い。また、オブジェクトのデザインはデータ構成を最初に決めて、それを参照ないし変更する目的に沿ったメソッド構成にするのが基本になる。外部公開されないデータとメソッドは隠蔽される事になり、これは計算機科学上の情報隠蔽または内部隠蔽として定義されている。公開とは該当オブジェクトの外にあるメソッドから直接アクセスできる事を意味し、隠蔽はその反対でアクセス禁止になる。アクセスとは、データに対しては参照(読み込み)または変更(書き込み)できる事であり、メソッドに対してはそれを呼び出す事を意味する。
 
外部公開されないデータとコードは隠蔽される事になり、これは計算機科学上の情報隠蔽または内部隠蔽として定義されている。公開とは該当オブジェクトの外にあるコードから直接アクセスできる事を意味し、隠蔽はその反対でアクセス禁止となる。アクセスとは、データに対しては参照(読み込み)または変更(書き込み)できる事であり、コードに対してはそれを呼び出す事を意味する。より詳しい解釈については様々だが、オブジェクト指向におけるカプセル化は、基本的に内部隠蔽に焦点を当てた考え方である。
 
=== 継承 ===
任意の既存オブジェクトのデータ構成とメソッド構成を引き継いで、新しいオブジェクトを定義する仕組みが継承と呼ばれる。引き継ぐ際には新たなデータとメソッドを自由に追加できるので、新生オブジェクトの構成は既存内容+アルファになり、結果的に既存オブジェクトの内容を使い回せる事にもなるのでソースコードの再利用性を高めるものとされた。オブジェクトはソースコード上でクラスとして定義されるので、継承元の親クラスはスーパークラス、継承先の子クラスはサブクラスと呼ばれる。一つのスーパークラスを持つ単一継承の他、複数のスーパークラスを持ちそれぞれの構成内容を引き継げる多重継承がある。
継承の元々の趣旨はオブジェクトの体系化である。ここでの体系化とは多様な要素をそれぞれ共通と特有の分節構成にし、共通部分から特有部分を派生させる形で関連付ける枝葉状の展開構図に投影する行為を指す。構造体の定義増加で発生しやすい似たようなデータ群の重複に伴う冗長さと、その整合性維持の手間隙を解決する為の手段であった。構造体=オブジェクトを複数の階層に分け、共通のデータ集合を親階層とし、特有のデータ集合を子階層として、子階層に親階層へのリンクを持たせて連結する構造にした。A階層から成る親オブジェクトから派生した子オブジェクトはA+B階層として構成され、これが継承と呼ばれた。アクセスを求められたデータがB階層に無い時は、次のA階層に有るか探す仕組みとなり、この連鎖によって傍からは一つのオブジェクトとして存在した。また、子階層の次のリンク先となる親階層は一本だけでなく複数本持つ事も出来るので、複数の親階層+子階層によるオブジェクト構成は多重継承(''multiple inheritance'')と呼ばれた。子階層が持つ親階層アドレスは一般にリスト化されており、自身に無いデータはリスト先頭の親階層から順々に検索された。その親階層が多重継承されてる場合も同様であり、それぞれの枝分かれには深さ優先検索が用いられた。
 
データと同様に共通のコード(メソッド)群も親階層にまとめられた。その応用として抽象メソッド(後述の[[仮想関数]]と同義)だけの階層を独立させた純粋抽象クラスすなわち[[インタフェース (抽象型)|インターフェース]]があり、これを親階層として継承(実装)するのは{{仮リンク|サブタイピング|en|Subtyping|label='''サブタイピング'''}}(''subtyping'')となった。サブタイピングは継承を「抽象化」方向へ推し進めたパラダイムと言える。同じ継承パラダイムのカテゴリで括られる事が多いが、[[継承 (プログラミング)|継承]]が[[オントロジー (情報科学)|存在論]](''ontology'')に拠った事前的なストラクチャ分析手法であるのに対し、{{仮リンク|サブタイピング|en|Subtyping|label=}}は目的論(''teleology'')に拠った事後的なプロセス設計手法であるので、この両者の趣旨は明確に異なっている。サブタイピングのパラダイム下では任意の階層から一定の振る舞い定義を抽出して[[インタフェース (抽象型)|インターフェース・オブジェクト]]となる親階層を事後的に形成する。振る舞い定義とは、ある目的(''object'')の為に働く抽象メソッド集合である。[[インタフェース (抽象型)|インターフェース]]はその抽象メソッド集合の実装階層の各メソッドアドレスのみを所有している抽象階層となる。
 
なお、派生クラスで任意のコードとデータを追加できる継承の利点としてよく語られていたものにソースコードおよびプログラムモジュールの再利用性向上があるが、深い継承構造はクラスの構成把握を困難にするという欠点が明らかになったのでこれは否定された。継承による再利用性はクラスライブラリの使用範囲内に留まっているのが現状である。
 
=== 多態性 ===
アドホック多態性は単にソースコードの記述を一部自動化するものである。'''関数オーバーロード'''は引数の並び方パターンによって同じ名前のメソッドをコンパイル時に自動的に差別化する機能である。'''演算子オーバーロード'''は、扱う数値の型に従って宣言された演算記号を関数名と見なすようにし、単項演算子なら右の数値を第一引数とし、二項演算子なら左右の数値をそれぞれ第一第二引数として関数呼び出しのコードが生成されるという仕組みだった。丸括弧の演算子は関数オブジェクトの表現として使用出来た。これらは静的な多態性とされる。
 
パラメータ多態性もソースコードの記述を一部自動化するものである。関数&クラスのコード内の特定の型部分をワイルドカードにして記述しておき、ソースコード内で具体的な型の指定と共に関数&クラスの呼び出しが記述されると、その型を先のワイルドカードに当てはめた関数&クラスのコードがコンパイル時に自動生成されるという機能だった。&の前者は'''ジェネリック関数'''と呼ばれ、&の後者はより広い範囲を扱う事から'''ジェネリックプログラミング'''と名付けられた。これらも静的な多態性に位置付けられている。
 
サブタイプ多態性は動的なものである。最も初期OOP代表例であるSimula67'''仮想関数'''は、シミュレーション内で扱う多種多様なオブジェクトを上述の継承によって体系化したが、コード部分の細かな違いは共通おけるスーパークラスに属する共通プロシージャ内の分岐フローで処理していた。サブクラスの数だけ分岐構文増える頻雑さ解消する利用しめに機能であり共通プロシージャをただ一つ[[仮想関数テブル|アパークラス抽象メソッレス置場]]の呼び出しから、その実行時対応た様々なサブクラス実装メソッド同名プロシージャのアドレスを収納多方向分岐させ、共通プロシージャ呼び出し時にそのアドレスへジャンプすという機能が考案された。[[仮想関数テーブル|アドレス置場]]はプロシージャの仮想的存在と見なされたので、この機能は'''仮想関数'''と呼ばれた。仮想関数は明確にコードを「抽象化」した仕組みでもあるを指す。'''動的ディスパッチ'''はSmalltalkの、主に[[分散コンピューティング]]を表現する[[Object Request Broker|オブジェクト設計に由来す間通信]]で用いられもの技法であり、その実装の仕方は様々でやや曖昧な仕様でもある。呼び出されたローカルセージを受け取っドまレシーバはリモトメソッドがオブジェクト内部で動的なの実行時状態に基づ動的て臨機応変な処理を行ってその結果を返すといったランタイム環境上のプロセスが後に動的ディスパッチのカテゴリで括られた。[[分散コンピューティング]]を表現する[[Object Request Broker|オブジェクト間通信]]とそれに基づく[[ソフトウェアコンポーネント]]も動的ディスパッチに該当するものであている。'''多重ディスパッチ'''は動的な関数オーバーロードに相当する。関数コール時または関数ブロック内で、それぞれの引数が動的に型審査されて型変化(''dynamic casting'')された後に、その引数パターンに対応した同名関数または分岐ルーチンに処理が移行されるという動的変化プロセスを指した。'''ダブルディスパッチ'''は多重ディスパッチの亜流的存在であり、二通りの考え方がある。動的型審査および型変化されるBオブジェクトを単一引数にしてAオブジェクトの仮想関数メソッドを呼び出す形態と、多重ディスパッチに用いる引数を二つに限定した形態である。いずれも実行時状態に応じた動的変化プロセスとなった。これは主にデータ集合を対象にして分類、解析、作用といった処理を連続的または再帰的に行うアルゴリズムで用いられた。
 
=== 抽象化 ===
純粋抽象クラスの仕組みがこれに相当する。実例としては[[ソフトウェアコンポーネント]]や[[Java]]などのプログラミング言語で用いられている[[インタフェース (抽象型)|インターフェース]]がある。これは[[カプセル化]]の{{仮リンク|プロトコル(OOP)|en|Protocol (object-oriented programming)|label=プロトコル}}、[[継承 (プログラミング)|継承]]の{{仮リンク|サブタイピング|en|Subtyping|label=}}、[[ポリモーフィズム|多態性]]の{{仮リンク|仮想関数(OOP)|en|Virtual function|label=仮想関数}}をまとめたパラダイムと考える事ができる。極論的にOOP三原則の行き着く先はこの抽象化であるとも言える。この[[インタフェース (抽象型)|インターフェース]]の仕組みを中心にした抽象化の理念は、任意の目的(''object'')を達成するためのデータとコードの複合体すなわち[[オブジェクト (プログラミング)|オブジェクト]]の設計部分と実装部分を明確に分離する事である。設計部分のプログラム投影物が[[インタフェース (抽象型)|インターフェース]]の仕組みであり、プログラム概念的には抽象化と呼ばれる。[[C++]]開発者の[[ビャーネ・ストロヴストルップ]]は、広義のオブジェクト指向の主要サポート案件として抽象化、継承、実行時多態性(''run-time polymorphism'')の三点を挙げていた。
 
この「設計と実装の分離」はよく聞かれる言葉であるが、プログラミング(コーディング)レベルでは、ソースコードの随所に抽象ポイントを設ける事を意味する。OOPにおける抽象ポイントとは上述の[[インタフェース (抽象型)|インターフェース]]にアクセスする為のアドレス置場の事である。アドレスは実装面ではグローバルなネットワーク番地、ローカルなメモリ番地、または特殊なアドレス指定キーワードなどで表現される。抽象ポイント=アドレス置場に収納されるインターフェースのアドレスは、プログラム実行時に随時切り替えられて柔軟な多分岐プロセスを可能にし、これがOOPの利点とされる仕様変更とエラー修正が比較的容易な保守性と堅牢性につながるものになる。なお、抽象ポイント=アドレス置場の積極利用は決して新しい考え方ではなく、その原点は[[プロセッサ]]の間接アドレス参照(後年に[[ポインタ (プログラミング)|ポインタ]]と定義されるもの)機能であり、[[機械語]]と[[アセンブリ言語|アセンブラ]]の時代から存在する古典的な標準手法でもある。当時は処理の多分岐化の為だけでなくコードサイズ節約やクロック数削減のテクニックとしても使われていたが、プログラムの構造化などが十分に普及していない黎明期においては、間接アドレス参照の多用はプログラムの可読性を下げるものとしてやがて倦厭されるようになった。C言語の学習でもポインタが鬼門とされており、この方面の理解の難しさを示している。しかし[[プログラミングパラダイム|プログラミング理論]]と[[ソフトウェア工学]]の発展に伴い、[[クラス図]]を始めとする[[統一モデリング言語|各種ダイアグラム]]による綿密な設計の下で抽象ポイントを用いれば、可読性を損ねずに保守性や堅牢性を向上させる事が知られるようになった。
 
=== メッセージング ===
メッセージングは哲学的側面が強いパラダイムである。仕組み的には、オブジェクト(=インスタンス)のメソッドコール、または[[Object Request Broker|オブジェクト間通信]]におけるリモートメソッドコールと同じものと考えてよいが、メッセージングのパラダイム下では、イメージ的にオブジェクトそのものをメソッドとしてコールする点が異なっている。それは各オブジェクトが一般にレシーバーと呼ばれるデフォルトメソッドを持つ事で実現されている。オブジェクトのコールとは、このレシーバーをコールするのと同義となる。引数無しでコールされる事はなく基本的に一つのオブジェクトが引数となってコールされる。これも留意すべき点である。元祖オブジェクト指向では「''EverythingIsAnObject''」の通り、[[プリミティブ型|プリミティブ]]から[[構造体|データストラクチャ]]、[[コードブロック]]まであらゆるプログラム要素がオブジェクトとされる。ここにオブジェクトA、Bがあるとすると、メッセージングの基本構文は「A B」のようになる。これは「Bを引数にしてAを呼び出す」の意味であり、イメージ的に「Aに対してBというメッセージを送る」と形容される。引数Bを受け取ったAのレシーバー内で任意の処理が行われた後に結果値としてのオブジェクトが返される。Aそのものが返される事もあれば、別のオブジェクトが返される事もある。この流れがメッセージングと呼ばれるものである。返値オブジェクトに対して別のメッセージを送る事も可能であり、また返値オブジェクトをメッセージにして別のオブジェクトに送る事も出来る。こうしたメッセージングの連鎖はAのレシーバー内でも同様に行なわれる。メッセージング・パラダイム下でのオブジェクトは言わば独自の記憶を備えた変換式であり、これらオブジェクトのコミュニケーションとは[[高階関数]]と[[第一級関数]]の仕組みと同じものである。メッセージング・パラダイムの本質はオブジェクトの代数(''algebra'')化であり、その代数値は前述のメッセージングの連鎖による結果値である。メッセージングを行なうオブジェクトもまたメッセージング連鎖の集合体という事になる。それはただの[[プリミティブ|プリミティブ値]]であっても決して例外ではない。レシーバーでの処理をコードとすると、メッセージング・パラダイム下でのデータはメッセージング連鎖によるコードの集合体であり、その各コードは他のデータ群を参照しており、その各データもまたコードの集合体~という風になる。勿論最終的には根っことなるオブジェクトの定形データに行き着いてそれが値算出の原点になるが、こうなるとイメージ的にコードとデータが融合して両者の区別はなくなる。オブジェクトは抽象化の一形態である代数的(''algebraic'')存在なので、代数計算と同様に理念的には単体で成り立つ事はなく、オブジェクト自体もその様にデザインされる事が原則的に求められている。二つ以上のオブジェクトが出会うメッセージングによって始めて一つのデータが正式に体現される事になり、同時に一つのプロセスが発生する。これが引数無しでもよいメソッドと引数が要るメッセージングの明確な違いである。