コンテンツにスキップ

英文维基 | 中文维基 | 日文维基 | 草榴社区

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

出典: フリー百科事典『ウィキペディア(Wikipedia)』
削除された内容 追加された内容
用語と解説
m 用語と解説: い抜き言葉「やってる」「してる」の使用を回避(3回目)
126行目: 126行目:
:(''method overriding'')とは継承による階層的クラス構造のインスタンスにおいて、サブクラスのメソッドシグネチャの処理内容でそのスーパークラス側の同じメソッドシグネチャの処理内容を置き換える仕組みを指す。メソッドシグネチャとは「返り値+メソッド名+引数欄」で構成される識別単位である。この指定は親側のメソッドが上書きされるのをデフォルトにするのと、子側のメソッドで上書きするのをデフォルトにする二通りがある。前者なら子側のメソッドを''override''や''redefine''で修飾しそれで親側メソッドを上書きする。後者なら親側のメソッドを''virtual''や''deferred''や''abstract''で修飾しそれは子側メソッドで上書きされる。それとは別にただメソッドを上書き不可にする場合は''final''などで修飾する。オーバーライドはメソッド名を参照アドレスにマッピングする[[仮想関数テーブル]]と呼ばれる機能を用いて実現される。
:(''method overriding'')とは継承による階層的クラス構造のインスタンスにおいて、サブクラスのメソッドシグネチャの処理内容でそのスーパークラス側の同じメソッドシグネチャの処理内容を置き換える仕組みを指す。メソッドシグネチャとは「返り値+メソッド名+引数欄」で構成される識別単位である。この指定は親側のメソッドが上書きされるのをデフォルトにするのと、子側のメソッドで上書きするのをデフォルトにする二通りがある。前者なら子側のメソッドを''override''や''redefine''で修飾しそれで親側メソッドを上書きする。後者なら親側のメソッドを''virtual''や''deferred''や''abstract''で修飾しそれは子側メソッドで上書きされる。それとは別にただメソッドを上書き不可にする場合は''final''などで修飾する。オーバーライドはメソッド名を参照アドレスにマッピングする[[仮想関数テーブル]]と呼ばれる機能を用いて実現される。
;ドミナンス
;ドミナンス
:(''dominance'')は言語によってハイディング(''hiding'')マスキング(''masking'')とも呼ばれる。継承による階層的クラス構造において、サブクラスのメンバがスーパークラスの同名のメンバを隠していることを指す。親クラスのAメソッドを子クラスが同名Aメソッドでドミナンスした場合、子の型で参照してるインスタンスはそこでAのサーチが止まって子Aが呼び出される。ただし親の型で参照すれば親Aを呼び出せる。オーバーライドと異なり、参照する型でインスタンスの振る舞いを変えるための単純な仕組みでもある。
:(''dominance'')は言語によってハイディング(''hiding'')マスキング(''masking'')とも呼ばれる。継承による階層的クラス構造において、サブクラスのメンバがスーパークラスの同名のメンバを隠していることを指す。親クラスのAメソッドを子クラスが同名Aメソッドでドミナンスした場合、子の型で参照してるインスタンスはそこでAのサーチが止まって子Aが呼び出される。ただし親の型で参照すれば親Aを呼び出せる。オーバーライドと異なり、参照する型でインスタンスの振る舞いを変えるための単純な仕組みでもある。
;[[仮想継承]]
;[[仮想継承]]
:(''virtual inheritance'')は、多重継承での[[菱形継承問題]]を回避するための仕組みである。菱形継承問題とは共にAクラスを親とするBクラスとCクラスの双方を継承した場合に、その継承構造上でAクラスが二つ重なって存在することになる不具合である。仮想継承では専用のテーブルが用意されて、そこでクラス名が参照アドレスにマッピングされる。BクラスからのAクラスと、CクラスからのAクラスは共に同じ参照アドレスをマッピングするのでAクラスはひとつにまとめられる事になる。同時に一度辿ったクラスは省略される事にもなる。
:(''virtual inheritance'')は、多重継承での[[菱形継承問題]]を回避するための仕組みである。菱形継承問題とは共にAクラスを親とするBクラスとCクラスの双方を継承した場合に、その継承構造上でAクラスが二つ重なって存在することになる不具合である。仮想継承では専用のテーブルが用意されて、そこでクラス名が参照アドレスにマッピングされる。BクラスからのAクラスと、CクラスからのAクラスは共に同じ参照アドレスをマッピングするのでAクラスはひとつにまとめられる事になる。同時に一度辿ったクラスは省略される事にもなる。

2020年10月7日 (水) 11:52時点における版

オブジェクト指向プログラミング(オブジェクトしこうプログラミング、: object-oriented programming、略語:OOP)とは、互いに密接な関連性を持つデータメソッドをひとつにまとめてオブジェクトとし、それぞれ異なる性質と役割を持たせたオブジェクトの様々な定義と、それらオブジェクトを相互に作用させる様々なプロセスの設定を通して、プログラム全体を構築するソフトウェア開発手法である。

オブジェクト指向という用語自体は、計算機科学者アラン・ケイによって生み出されている。1962年公開の言語「Simula」の設計に印象を受けたケイが咄嗟に口にしたとされるこの造語は、彼が1972年から公開を始めた「Smalltalk」の言語設計を説明する中で初めて世間に発信された。なお、ケイが示したメッセージパッシングを中心にするオブジェクト指向は広く認知される事はなく、オブジェクトというプログラム概念を注目させるだけに留まっている。同時にケイの手から離れたオブジェクト指向は抽象データ型を中心にした解釈へと推移していき、1983年に計算機科学者ビャーネ・ストロヴストルップが公開した「C++」が契機になって、日本では一般にOOPの三大要素と呼ばれるカプセル化継承多態性といったパラダイムが確立されている。

特徴

現行のオブジェクト指向プログラミングは、1974年に計算機科学者バーバラ・リスコフらが提唱した抽象データ型を基礎的な考え方にする方向性で定着している。抽象データ型のプログラム実装スタイルを具体的に規定したものが1 - 3であり、日本では一般に三大要素と呼ばれている。これに沿った言語仕様を備えたプログラミング言語がオブジェクト指向準拠と判別されている。4はアラン・ケイが重視する元祖的なコンセプトであり、オブジェクト指向の源流思想として蛇足ながら紹介を加える。

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

カプセル化

一定の関連性を持つデータ(変数、プロパティ、フィールド、属性)と、それらを操作するメソッド(関数)をひとまとめにしてオブジェクトとし、外部に対して必要とされるデータとメソッドのみを公開し、それ以外を内部に隠蔽する仕組みがカプセル化と呼ばれる。公開されたデータは外部のメソッドから直接参照または変更する事ができる。公開されたメソッドは外部のメソッドから直接呼び出す事ができる。隠蔽されたデータとメソッドは外部からアクセスされないことが保証され、これは情報隠蔽と呼ばれる。メソッドを通してデータを参照または変更する仕組みはデータの抽象化を表現し、これはデータ抽象英語版と呼ばれる。データを参照するメソッドはゲッターまたはアクセッサと呼ばれる。データを変更するメソッドはセッターまたはミューテイタと呼ばれる。

継承

既存オブジェクトのデータ構成とメソッド構成を引き継いで、新しい派生オブジェクトを定義する仕組みが継承と呼ばれる。引き継ぐ際には新たなデータとメソッドを自由に追加できるので、派生オブジェクトの構成は既存要素+追加要素になる。既存の基底オブジェクトは親オブジェクト、その派生オブジェクトは子オブジェクトとも呼ばれる。クラスベースでは、親をスーパークラス、子をサブクラスと呼ぶ。一つのスーパークラスを継承するのは単一継承と呼ばれる。複数個のスーパークラスを継承してそれぞれの要素を引き継ぐのは多重継承と呼ばれる。UMLでは汎化と特化の関係で表現されている。メソッドの抽象化に焦点を当てた継承の方は実装継承英語版などと呼ばれる。UMLでは実現と実装の関係で表現されている。実装継承は特定のオブジェクトたちに共通した振る舞い側面を抜き出して抽象化する仕組みを指し、その抽象化オブジェクトはインターフェーストレイトプロコトル英語版などと呼ばれる。

多態性

異なる種類のオブジェクトに同一の操作インターフェースを持たせる仕組みが多態性と呼ばれる。オブジェクト指向下の多態性は、クラスの派生関係またはオブジェクトの動的バインディング機能によって、コンパイル時のメソッド名から呼び出されるプロセス内容が実行時に決定されるという仕組みの振る舞いサブタイピング英語版を指す。これはサブタイプ多相英語版の一種である。その代表格は仮想関数英語版であり、オブジェクト指向で多態性と言えばこれを指して説明されることが多い。仮想関数は、メソッドが所属するクラスの派生関係のみに焦点を当てた一重ディスパッチであり、スーパークラス抽象メソッドの呼び出しを、それをオーバーライドしたサブクラス実装メソッドの呼び出しにつなげる機能である。一重ディスパッチとはプロセス選択に関与するオブジェクトが一つであることを意味しており、二つ以上の場合は多重ディスパッチになる。多重の方はメソッドが属するクラスの派生関係だけでなく、そのメソッドの各引数のクラスの派生関係にも注目した形態であり、各引数は実行時の型判別とダウンキャストされて、その引数型の組み合わせに対応したプロセスを選択する。一重ディスパッチと多重ディスパッチは動的ディスパッチ英語版という分類用語に包括されており、仮想関数はクラスベース向けに特化された動的ディスパッチとも定義されている。クラス機構の代わりにプロトタイプ機構を用いるプロトタイプベースの方では、オブジェクト(フレーム)のメソッド名スロットに当てはめられるメソッド実装の参照が随時切り替えられることにより、そのメソッド名から呼び出されるプロセスが実行時に決定されるという仕組みで広義の振る舞いサブタイピングを表現している。この仕組みも動的ディスパッチという分類用語に包括されており、便宜的にそのまま動的ディスパッチと呼ばれることが多い。

メッセージパッシング

I thought of objects being like biological cells and/or individual computers on a network, only able to communicate with messages.
(さながら生物の細胞、もしくはネットワーク上の銘々のコンピュータ、それらはただメッセージによって繋がり合う存在、僕はオブジェクトをそう考えている) — Alan Kay
... each object could have several algebras associated with it, and there could be families of these, and that these would be very very useful.
(銘々のオブジェクトは自身に伴う幾つかの「代数」を持つ、またそれらの家族たちもいるかもしれない、それらは極めて有用になるだろう) — Alan Kay
The Japanese have a small word - ma ... The key in making great and growable systems is much more to design how its modules communicate rather than what their internal properties and behaviors should be.
(日本語には「間」という言葉がある・・・成長的なシステムを作る鍵とは内部の特徴と動作がどうあるべきかよりも、それらがどう繋がり合うかをデザインする事なんだ) — Alan Kay
I realized that the cell/whole-computer metaphor would get rid of data, ...
(僕はこう気付いた、細胞であり全体でもあるコンピュータメタファーはデータを除去するであろうと、) — Alan Kay
... there were two main paths that were catalysed by Simula. The early one (just by accident) was the bio/net non-data-procedure route that I took. The other one, which came a little later as an object of study was abstract data types, and this got much more play.
(Simulaを触媒にした二本の道筋があった。初めの一本はバイオネットな非データ手法、僕が選んだ方だ。少し遅れたもう一本は抽象データ型、こっちの方がずっと賑わってるね。) — Alan Kay

歴史

1954年に初の高水準言語FORTRANが登場すると、開発効率の劇的な向上と共にソフトウェア要求度も自然と高まりを見せてプログラム規模の急速な拡大が始まった。それに対応するために肥大化したメインルーチンをサブルーチンに分割する手法と、スパゲティ化したgoto命令制御構造文に置き換える手法が編み出され、これらは1960年に公開された言語「ALGOL60」で形式化された。当時のALGOLはアルゴリズム記述の一つの模範形と見なされたが、それと並行して北欧を中心にした計算機科学者たちはより大局的な観点によるプログラム開発技法の研究を進めていた。

Simulaの開発(1962 - 72)

1962年、ノルウェー計算センターでモンテカルロ法シミュレーションを運用していた計算機科学者クリステン・ニゴールは、ALGOL60を土台にしてProcessと呼ばれるコルーチン機構を加えたプログラミング言語「Simula」を公開し、続けてその拡張にも取り組んだ。ニゴールの同僚で、1963年にSimulaを汎用機UNIVAC系統上で運用できるように実装した計算機科学者オルヨハン・ダールは、Processにローカル変数構造を共有する複数の手続き(サブルーチン)を加えてパッケージ化する言語仕様を考案した。程なくしてALGOL60コンパイラに準拠していての限界を悟ったニゴールとダールは、1965年からSimulaを一から再設計するように方針転換した。その過程で彼らは、計算機科学者アントニー・ホーアが考案して1962年のSIMSCRIPT(FORTRAN用のスクリプト)に実装していたRecord Classを参考にしている。Record Classはソースコード水準の抽象表現を、各汎用機に準拠したマシンコード水準の実装符号に落とし込む段階的データ構造のプログラム概念であった。これをモデルにした継承とその構造上に成り立つ仮想手続き(仮想関数)の仕組みも考案され、この両機能を備えたProcessのパッケージ化を「クラス」とし、クラスをメモリに展開したものを「オブジェクト」とする言語仕様がまとまり、1967年に「Simula67」が初公開された。オブジェクトという用語は、MITの計算機科学者アイバン・サザランドが1963年に開発したSketchpadCADGUIの元祖)の設計内にあるObjectが先例であった。Simula67コンパイラはまずUNIVAC上で運用され、翌年から汎用機バロースB5500などでも稼働されて北欧、ドイツ、ソ連の各研究機関へと広まり、1972年にはIBM汎用機System/360などにも導入されて北米全土にも広まった。その主な用途は物理シミュレーションであった。

構造化プログラミングの提唱(1969 - 75)

Simulaの普及と前後して1960年代半ばになると、プログラム規模の際限ない肥大化に伴う開発現場の負担増大が顕著になり、いわゆるソフトウェア危機問題が計算機科学分野全般で取り沙汰されるようになった。その解決に取り組んだ計算機科学者エドガー・ダイクストラは、1969年のNATOソフトウェア工学会議で「構造化プログラミング」という論文を発表しトップダウン設計、段階的な抽象化、階層的なモジュール化、共同詳細化(抽象データ構造と抽象ステートメントのjoint)といった構造化手法を提唱した。ダイクストラの言う構造化とは開発効率を高めるための分割統治法を意味していた。なおこの構造化プログラミングは後に曲解されて制御構造文を中心にした解釈の方で世間に広まり定着している。共同詳細化は抽象データ構造を専用ステートメントを通して扱うという概念である。これはSimulaの手続きを通してクラス内の変数にアクセスするという仕組みをモチーフにしていた。段階的な抽象化と階層的なモジュール化は時系列的にも、SIMSCRIPTの段階的データ構造と、Simura67の継承による階層的クラス構造を模倣したものであった。ダイクストラホーアダールの三名は1972年に『構造化プログラミング』と題した共著を上梓していることから互いの研鑽関係が証明されている。その階層的プログラム構造という章の中でダールは、Simulaの目指した設計を更に明らかにした。

influenced by Sketchpad, Simula, the design for the ARPAnet, the Burroughs B5000, and my background in Biology and Mathematics, I thought of an architecture for programming.
SketchpadSimulaアーパネットバロースB5000、それと専攻していた生物学と数学に影響されて僕はプログラミングアーキテクチャを思索していた) — Alan Kay

1974年にMITの計算機科学者バーバラ・リスコフとステファン・ジルは「抽象データ型」というプログラム概念を提唱した。彼女らの理論は、ダイクストラが提示したモジュールの共同詳細化を、その振る舞いによって意味内容が定義される抽象データという考え方でより明解に形式化し、データ階層ないしモジュール階層の連結関係を、上位概念と下位概念置き換え原則で明確に標準化した。一方、1970年に構造化言語Pascalを開発していた計算機科学者ニクラウス・ヴィルトは、ダイクストラによる共著出版後の1975年にモジュール化言語Modulaを提示してモジューラプログラミングというパラダイムを生み出している。このようにいささか奇妙ではあるが、Simulaのクラスとオブジェクトというプログラム概念は、巷で言われる構造化からモジュール化へといった進化の流れとは関係なく、しかもその前段階においてさながら彗星のように生まれたパラダイムであった。

Smalltalkとオブジェクト指向の誕生(1972 - 81)

SimulaのProcessおよび67年版からのクラスの仕様は、パロアルト研究所の計算機科学者アラン・ケイによるオブジェクトと「メッセージ」というプログラム概念のヒントになった。ケイはプログラム内のあらゆる要素をオブジェクトとして扱い、オブジェクトはメッセージの送受信でコミュニケーションするという独特のプログラム理論を提唱した。メッセージとはプログラムコードとしても解釈できるデータ列のことであり、そのデータ列を評価(eval)することで新たなデータを導出できるという仕組みを意味していた。オブジェクトが受け取ったメッセージは任意のタイミングで評価できるので非同期通信、単方向通信(送りっぱなし処理)をも可能にしていた。この発想の背景にはLISPの影響があった。オブジェクトとメッセージの構想に基づいて開発された「Smalltalk」はプログラミング言語とGUI運用環境を併せたものとなり、1972年にゼロックスAlto上で初稼働された。Smalltalkの設計を説明するためにケイが考案した「オブジェクト指向」という用語はここで初めて発信された。またメッセージの構想はMITの計算機科学者カール・ヒューイットに能動的なプロセス代数を意識させて、1973年発表のアクターモデルのヒントにもなっている。しかし、データ列が常にコード候補として扱われる処理系は、当時のコンピュータには負荷が大きく実用的な速度を得られないという問題にすぐ直面した。Smalltalk-74とSmalltalk-76の過程で、やむなくメッセージはセレクタ仕様の追加と共に構想通りの評価ができないほどシステム向けに最適化され、レシーバーは動的ディスパッチとメソッド仕様が中心になり、オブジェクトはクラス定義の存在感が大きくなった。

Smalltalk is not only NOT its syntax or the class library, it is not even about classes. I'm sorry that I long ago coined the term "objects" for this topic because it gets many people to focus on the lesser idea.The big idea is "messaging".
(Smalltalkはその構文やライブラリやクラスをも関心にしていないという事だけではない。多くの人の関心を小さなアイディアに向かせたことから、僕はオブジェクトという用語を昔作り出したことを残念に思っている。大切なのはメッセージングなんだ。) — Alan Kay

1980年のSmalltalk-80は、元々はメッセージを重視していたケイを自嘲させるほど同期的で双方向的で手続き的なオブジェクト指向へと変貌していた。それでも動的ディスパッチと委譲でオブジェクトを連携させるスタイルは画期的であり、1994年に発表されるデザインパターンの模範にもされている。1981年に大手専門誌BYTE MagazineがSmalltalkとケイ提唱のオブジェクト指向を紹介して世間の注目を集める契機になったが、ケイの思惑に反して技術的関心を集めたのはクラス機構の方であった。オブジェクト指向は知名度を得るのと同時に、Simula発のクラスとそれを理論面から形式化した抽象データ型を中心に解釈されるようになり、それらの考案者がケイの構想とは無関係であったことから、オブジェクト指向の定義はケイの手を離れて独り歩きするようになった。

C++の開発(1979 - 88)

Simulaを研究対象にしていたAT&Tベル研究所の計算機科学者ビャーネ・ストロヴストルップは、1979年からクラス付きC言語の開発に取り組み、1983年に「C++」を公開した。C++で実装されたクラスは、Simula譲りの継承と仮想関数に加えて、レキシカルスコープの概念をクラス定義とその継承構造に応用したアクセスコントロールを備えていた。C++で確立されたアクセスコントロールはカプセル化の元になったがコードスタイル上ほとんどザル化されており、その理由からストロヴストルップ自身もC++は正しくない(not just)オブジェクト指向言語であると明言している。1986年にソフトウェア技術者バートランド・メイヤーが開発した「Eiffel」の方は、正しいオブジェクト指向を標榜してクラスのデータ抽象を遵守させるコードスタイルが導入されていた。クラスメンバ(フィーチャー)は属性、手続き、関数の三種構成で、手続きで属性を変更し関数で属性を参照するという形式に限定されており、これは抽象データ型の振る舞い意味論に沿った実装であった。アクセスコントロールはC++のアクセス修飾子による段階的レキシカルスコープ定義に対して、自身のクライアントクラスを定義する書式になり、これはモジューラプログラミングの情報隠蔽論に沿った実装であった。C++の仮想関数は延期フィーチャーとして実装された。

I made up the term ‘object-oriented’, and I can tell you I didn’t have C++ in mind.
(僕はオブジェクト指向という言葉を作った、それとC++に関心がなかったことも分かっている) — Alan Kay

1986年からACMオブジェクト指向会議(OOPSLA)を開催し、そのプログラミング言語セクションでは抽象データ型の流れを汲むクラス・パラダイムが主要テーマにされ、それを標準化するための数々のトピックが議題に上げられている。モジュール性、情報隠蔽、抽象化、再利用性、階層構造、複合構成、実行時多態、動的束縛、自動メモリ管理といったものがそうであり、参画した識者たちによる寄稿、出版、講演を通して世間にも広められた。契約による設計を提唱するメイヤーが1988年に刊行した『オブジェクト指向ソフトウェア構築』は名著とされ、Eiffelを現行の模範形とする声も多く上がった。ただしこれは学術寄りの意見でもあったようで、世間のプログラマの間では厳格なEiffelよりも柔軟で融通の利くC++の人気の方が高まっていた。また、Smalltalkが提唱するメッセージ・メタファも単にオブジェクト指向の発案元であるという理由から一目置かれており、クラスのメソッド呼び出しをオブジェクトにメッセージを送ることになぞらえる考えが広まった。これは実行時の選択メソッドをメッセージの発送先にする意味合いで動的(一重と多重)ディスパッチの呼称の由来になっている。他方でSmalltalkの仕様に忠実であろうとする動きもあり、1984年に計算機科学者ブラッド・コックスが開発した「Objective-C」はSmalltalkをモデルにしてそれを平易化した言語であった。そのメッセージレシーバーはメソッドリストにないセレクタを受け取った場合にのみ動的ディスパッチ機構に移るというスタイルで形式化された。程なくしてこのメッセージレシーバー実装の必要性には疑問符が付けられるようになったが、遠隔手続き呼出しのスタイルにはマッチしていたのでメッセージにある種の理想を抱く風潮はいつまでも残った。

プロトタイプベースの考案(1985 - 90)

Smalltalkのオブジェクト指向は、アラン・ケイがその影響を言及していたLISPコミュニティを逆に感化して、Smalltalkが提示したメタオブジェクトの概念を通したオブジェクト指向とLISP風プログラミングの連携に向けた構想が練られるようになった。LISPの基礎であるアトムとシンボル型に倣う形で、オブジェクトのメンバ変数名とメンバ関数名自体をその都度評価(eval)して実行時の内容を導出し、実際の変数参照や関数呼出につなげるというアイディアが生まれた。これは実装面では単に、フレームと呼ばれる動的配列のスロットに変数や関数のポインタを付け替えするという機構にまとめられた。識別子(変数名と関数名)がマッピングされたスロットも増設削減可能であり、原型クラスと継承クラスのポインタも加えられた。このメタオブジェクト設計を導入して1985年にMIT人工知能研究所LISPマシン上で「Flavors」が実装された。LISPの視点から保有スロット識別子の構成による動的型付けが盛り込まれ、その構成をパーツ化して多重継承させるミックスインという設計が考案され、また実行時の逐次型判別に焦点を当てたダックタイピングの概念も生まれた。1988年にFlavorsのデザインをCommon Lispに融合させた「CLOS」が公開されたが、こちらは関数を中心にして抽象データ型から距離を置いたスタイルになった。Flavorsのデザインはパロアルト研究所にも回帰され、計算機科学者デビッド・アンガーがSmalltalkの方言として制作する「Self」を1987年に初回稼働して90年に一般公開した。LISPコミュニティから逆輸入されてSelfに導入されたメタオブジェクト設計は、後にプロトタイプベースまたはインスタンスベースと呼ばれるパラダイムに発展する。同時にそれと、従来のクラス機構を中心にしたオブジェクト指向言語を区別するためのクラスベースという言葉も生まれた。

コンポーネントとネットワーク(1989 - 97)

ネットワーク技術の発展に連れて、データとメソッドの複合体であるオブジェクトの概念は、分散システム構築のための基礎要素としての適性を特に見出される事になり、IBM社アップル社サン社などが1989年に共同設立したOMGは、企業システムネットワーク向け分散オブジェクトプログラミングの標準規格となるCORBAを1991年に公開した。その前年にマイクロソフト社ウェブアプリケーション向けの分散オブジェクト技術となるOLEを発表し、1993年にはCOMと称するソフトウェアコンポーネント仕様へと整備した。このCOMの利用を眼目にしてリリースされた「Visual C++」「Visual Basic」はウェブ時代の新しいプログラミングスタイルを普及させる先駆になった。この頃に抽象データ型のメソッドを通したデータ抽象、データ隠蔽、アクセスコントロールおよび分散オブジェクト=プロセス間通信インターフェース機構によるプログラムの抽象化といった概念は、カプセル化という用語にまとめられるようになった。クラスの継承が最もオブジェクト指向らしい機能と見なされていたのが当時の特徴であり、深い継承構造を技巧的と見る風潮さえ存在した。継承構造を利用したサプタイピングは多態性という用語に包括され、多重継承の欠点が指摘されると分散オブジェクトのそれに倣ったインターフェースの多重実装設計が取り上げられた。こうしてカプセル化の誕生と連動するようにしていわゆるオブジェクト指向の三大要素がやや漠然と確立されている。1996年にサン社がリリースした「Java」は三大要素が強く意識されたクラスベースであり、その中の分散オブジェクト技術はBeansと呼ばれた。類似の技術としてアップル社もMacOS上でObjective-Cなどから扱えるCocoaを開発している。また、1994年から96年にかけて「Python」「Ruby」「JavaScript」といったオブジェクト指向スクリプト言語がリリースされ、プロトタイプベースという新しいプログラミングスタイルを定着させている。1994年のGOFデザインパターンの発表と1997年のOMGによるUML策定は、オブジェクト指向プログラミングの標準化を促進させた。

代表的なオブジェクト指向言語

オブジェクト指向言語は、抽象データ型の仕組みに沿ったクラスベース、メタオブジェクトの仕組みに沿ったプロトタイプベースSmalltalkをルーツにしたメッセージ構文ベースの三タイプに分類されるのが一般的である。クラスベースでは「C++」「Java」「C#」が代表的である。プロトタイプベースでは「Python」「JavaScript」「Ruby」が有名である。メッセージ構文ベースでは「Smalltalk」「Objective-C」「Self」などがある。言語仕様の中でオブジェクト指向の存在感が比較的高い代表的なプログラミング言語は以下の通りである。

Simula 67 1967年
1962年に公開されたSimulaの後継版であり、クラスのプログラム概念を導入した最初の言語である。物理モデルを解析するシミュレーション制作用に開発されたもので、クラスをメモリに展開したオブジェクトはその観測対象要素になった。Simulaのクラスは、一つのローカル変数構造と複数のプロシージャをまとめたミニモジュールと言えるものであったが、継承と仮想関数という先進的な設計を備えていた事でオブジェクト指向言語の草分けと見なされるようになった。クラスベースの源流である。
Smalltalk 1972年
メッセージパッシングのプログラム概念を導入した最初の言語。数値、真偽値、文字列から変数、コードブロック、メタデータまでのあらゆる要素をオブジェクトとするアイディアを編み出した最初の言語でもある。オブジェクト指向という言葉はSmalltalkの言語設計を説明する中で生み出された。オブジェクトにメッセージを送るという書式であらゆるプロセスを表現することが目標にされている。メッセージレシーバー委譲の仕組みは、形式化されていない動的ディスパッチと動的バインディング相当のものでありプロトタイプベースの源流にもなった。GUI運用環境に統合された専用のランタイム環境上で動作させる設計も模範にされ、これは後に仮想マシン仮想実行システムと呼ばれるものになる。
C++ 1983年
C言語クラスベースのオブジェクト指向を追加したもの。Simulaの影響を受けている。静的型付けクラスが備えられてカプセル化、継承、多態性の三仕様を実装している。カプセル化ではアクセス修飾子とフレンド指定子の双方から可視性を定義できる。継承は多重継承、オーバーライド制約用の継承可視性、菱形継承問題解決用の仮想継承も導入されている。多態性は仮想関数によるサブタイプ多相、テンプレートクラス&関数によるパラメトリック多相、関数&演算子オーバーロードによるアドホック多相が導入されている。元がC言語であるため、オブジェクト指向から逸脱したコーディングも多用できる点が物議を醸したが、その是非はプログラマ次第であるという結論に落ち着いた。
Objective-C 1984年
C言語メッセージ構文ベースのオブジェクト指向を追加したもの。こちらはSmalltalkの影響を受けており、それに準じたメッセージパッシングの書式が備えられた。メッセージを受け取るクラスの定義による静的型付けと共に、メッセージを委譲するオブジェクトの実行時決定による動的型付けも設けられている。オブジェクト指向的にはC++よりも正統と見なされた。制御構造文が追加され、メッセージを送る書式も平易化されており、Smalltalkよりも扱いやすくなった。
Object Pascal 1986年
Pascalにクラスベースのオブジェクト指向を追加したもの。当初はモジュールのデータ隠蔽的なカプセル化、単一継承、仮想関数による多態性という基本的なものだった。静的型付け重視である。関数&演算子オーバーロード(アドホック多相)とジェネリクス(パラメトリック多相)は当初採用に到らなかった。ヴィルト監修のアップル社による初回バージョンを基礎にして様々な企業団体による派生版が公開されており、その特徴と機能追加も様々である。
Eiffel 1986年
C++の柔軟性と融通性とは正反対のオブジェクト指向言語。クラスベース静的型付け重視である。契約による設計に基づくアサーションの挿入でクラスの状態および演算用の引数と返り値を細かくチェックできる。例外処理も備えられている。クラスメンバ(フィーチャー)はデータ、アクセッサ、ミューテイタの三種限定でオーバーロードはできない。カプセル化の可視性は自身に依存するクラス(クライアント)を定義する形で決められる。多重継承可能であり、クラス間の繋がりを仮想継承機能、各種オーバーライド指定子、名前衝突を解決するリネーミング機能などで綿密に設定できる。仮想関数とジェネリッククラスも導入されている。ガーベジコレクション機能が初めて導入されたオブジェクト指向言語でもある。
Self 1987年
メッセージ構文ベースのオブジェクト指向言語。デフォルト配備のオブジェクトを複製して、そのスロットに任意のプロパティとメソッドを動的バインディングできるというプロトタイプベースを初めて導入したオブジェクト指向言語でもある。ゆえに動的型付け重視である。当初はSmalltalkの派生言語として公開されており、それと同様に専用のランタイム環境上で実行され、GUI運用環境の構築も目標にしていた。Selfのランタイム環境は実行時コンパイラ機能を初めて実装したことで知られており画期的な処理速度を実現している。この技術はJava仮想マシンの土台になった。
CLOS 1988年
Common Lispプロトタイプベースに似たオブジェクト指向を追加したもの。構造体オブジェクトと多重定義関数オブジェクトに分離されており、前者のスロットには任意のフィールドを、後者のスロットには任意のパラメータパターンを動的バインディングできる動的型付け重視である。動的型付けの構造体オブジェクトを引数として渡される関数の多重ディスパッチが重視されている。
Python 1994年
プロトタイプベースのオブジェクト指向スクリプト言語。基本データ型コレクション型などよく使われるデータ要素を全て組み込みのオブジェクトにしており、手続き型プログラミングスタイルでの関数の対象値としても扱いやすく、またコレクション型を扱うのに適した関数型プログラミング構文も導入されている。関数もオブジェクトなので柔軟に扱える。オブジェクトは自由にプロパティとメソッドを付け替えして様々に応用できるようデザインされている。オブジェクトはダックタイピングで型判別されるので変数ないし関数の型宣言と型注釈は撤廃されている。ゆえに動的型付け重視である。Pythonのプロトタイプオブジェクトはクラスと呼ばれている。多重継承可能であり親要素の参照順序はC3線形化で解決されている。アクセスコントロールはなくデータ抽象を軽視するコードスタイルと相まってカプセル化は備えられていない。多態性はメソッドの動的バインディングで行われる。後期バージョンで型ヒントが追加され、それに伴いジェネリクスも導入された。
Java 1995年
C++をモデルにしつつ堅牢性とセキュリティを重視したクラスベースのオブジェクト指向言語。静的型付け重視である。パッケージ中心のカプセル化、単一のみの継承、仮想関数と多重実装可なインターフェースによる多態性と、基本に忠実なクラスベースである。C++のポインタと値型インスタンスと演算子オーバーロードは除外され、参照型インスタンスに統一されて例外処理は残された。クラスメタデータを操作できるリフレクションは初期から採用された。オブジェクト指向とマルチスレッド仕様の調和が図られ、コンポーネント指向による動的クラスローディングの存在感が高められている。中期からデータコンテナ系のクラスと関数型インターフェースなどに限ってジェネリクスが導入されている。仮想マシン上で実行される。仮想マシンガーベジコレクションの技術は比較的高度と見なされている。
Delphi 1995年
Object Pascalを発展させたもの。それと同様にこちらも基本に忠実なクラスベースで静的型付け重視であった。当初はデータベース操作プログラム開発を主な用途にして公開された。クラスとレコード(構造体)に同等の比重が置かれていた。一時期Javaの対抗馬になった。
Ruby 1995年
Pythonを意識して開発されたオブジェクト指向スクリプト言語。Smalltalkを一つの理想にして動的型付けを重視している。日本で誕生してグローバル化したプログラミング言語である。LISPとSmalltalkのメタプログラミング的なオブジェクト指向から、PythonとJavaScriptのプロトタイプベースなオブジェクト指向までのスタイルを幅広く取り入れており、様々な有用なプログラミング手法を採用している技のデパートのような言語である。
JavaScript 1996年
プロトタイプベースのオブジェクト指向スクリプト言語。型宣言と型注釈を撤廃してダックタイピングする動的型付け重視である。すべてをオブジェクトにするSmalltalkの思想に忠実な言語であり、Pythonと似ているがそれよりもプロトタイプベース性と関数型スタイルを追求している。関数とインスタンスをほぼ同等なオブジェクトにしている。返り値無しの関数はnew指定でインスタンス生成用の写像になり、その関数のローカル要素がインスタンスのプロパティとメソッドになる。new指定関数は共通のプロトタイプを持つインスタンス(クローンオブジェクト)を継続生成する。JavaScriptのインスタンスはクロージャに近いことから高階関数も自然表現されて関数型プログラミングとの融合も実現している。WEBアプリケーション開発を主な用途にして公開されたのでオブジェクトはGUIパーツの構築にも最適化されている。ECMAScriptとして標準化されており、2015年版からはクラスベース向けの構文もサポートするようになった。
C# 2000年
Javaを強く意識してマイクロソフト社が開発したクラスベースのオブジェクト指向言語。C++の柔軟性と融通的を残しながら、テンプレートメタプログラミング的なコーディングサポートも加えてマルチパラダイムに発展させている。アドホック多相では拡張メソッド、インデクサ、演算子オーバーロードなどを備えている。パラメトリック多相ではジェネリクスの型変数の共変性と反変性、型引数の型制約などを備えている。サブタイプ多相はクラスは単一継承でインターフェースは多重実装と基本通りである。関数型構文では特にデリゲートの有用性が高い。基本は静的型付けであるが、動的束縛型とダックタイピングによる動的型付けの存在感が高められているので漸進的型付けの言語と見なされている。.NET Framework共通言語基盤=仮想実行システム)上で実行される。
Scala 2003年
クラスベースのオブジェクト指向と関数型プログラミングを融合させた言語。クラス機構と関数型の型システムに同等の比重が置かれており静的型付け重視である。ミックスイン相当のトレイトジェネリクスを連携させた多態性が重視されている。型変数のバリアンス共変と反変双方の型境界、抽象タイプメンバ、ジェネリックトレイト、抽象クラスの組み合わせでオブジェクトを様々に派生型付けできる。シングルトンオブジェクトの役割が形式化されて従来のクラス静的メンバの新解釈にも用いられている。専用の定義書式によりイミュータブルなオブジェクトが重視されている。上述の派生型付けスタイルとオブジェクトのパターンマッチング式とオブジェクト引数の抽出構文の併用はモナドを彷彿とさせるものであり、抽象データ型を値として扱う独特の関数型スタイルを表現できる。Java仮想マシン上で動作するJavaテクノロジ互換言語である。
Kotlin 2011年
静的型付けのクラスベースのオブジェクト指向であるが、手続き型プログラミングに回帰しており、クラス枠外の関数とグローバル変数の存在感が高められている。クラスはpublicアクセスとfinal継承がデフォルトにされて、カプセル化と継承が公然と軽視されている。これによりインスタンスは手続き型の関数の対象値としての役割が強められ、その操作をサポートする関数型構文も導入されている。仮想関数と抽象クラスによる多態性は標準通りである。Java仮想マシン上で動作するJavaテクノロジ互換言語である。
TypeScript 2012年
JavaScriptを強く意識してマイクロソフト社が開発したオブジェクト指向スクリプト言語。JavaScriptのプログラムを静的型付けで補完した言語である。クラスベース向けの構文と、関数型プログラミング型システムのスタイルが加えられている。特に後者の性質が強調されている事から静的型付け重視である。継承構造によるサブタイプ多相はほぼ除外されており、パラメトリック多相とアドホック多相でオブジェクトを扱うというジェネリクスとバリアンスと型制約と型注釈を重視するデザインになっている。オブジェクト指向ではあるが関数型の性格が強めである。
Swift 2014年
Objective-Cを発展させたものであるが、メッセージ構文は破棄されており、クラスベースのオブジェクト指向になっている。オブジェクトのイミュータブル性重視の構文が採用されている。プロテクト可視性の削除によってクラスの縦並びの継承は軽視されており、プロトコルの横並びの多重実装を重視している。プロトコルはインターフェースミックスインの中間的機能であり、インスタンスはプロトコルを基準にして型分類され、また抽象化される。プロトコルとジェネリクスの連携による多態性が重視されている。モジュールの動的ローディングは不透明型の仕組みで補完されている。静的型付け重視である。

用語と解説

クラス
クラス(class)の仕組みを中心にしたオブジェクト指向をクラスベースと言う。クラスはデータメンバとメソッドをまとめたものであり、操作的意味論を付加された静的レコードとも解釈される。クラスはインスタンスのひな型になる。クラスはカプセル化、継承、多態性の三機能を備えていることが求められている。カプセル化はデータメンバとメソッドの可視性を指定する機能である。継承は自身のスーパークラスを指定する機能である。多態性はオーバーライドと仮想関数テーブルを処理する機能である。コンストラクタとデストラクタの実装も必要とされている。前者はインスタンス生成時に、後者はインスタンス破棄時に呼び出されるメソッドである。
プロトタイプオブジェクト
プロトタイプ(prototype)の仕組みを中心にしたオブジェクト指向をプロトタイプベースと言う。プロトタイプベースで言われるオブジェクトとは、中間参照ポインタの動的配列を指す。この動的配列は一般にフレームと呼ばれる。中間参照ポインタは一般にスロットと呼ばれる。スロットにはデータメンバとメソッドの参照が代入されるので、オブジェクトはクラスと同様にデータメンバとメソッドをまとめたものになる。プロトタイプベースの実装形式は言語ごとに様々であるが基本は概ね以下のようになる。オブジェクトはプロトタイプオブジェクトとクローンオブジェクトに分かれる。前者はクラス、後者はインスタンスに当たるものである。前者はシステム提供プロトタイプとユーザー定義プロトタイプに分かれる。プログラマはシステム提供プロトタイプを派生させてユーザー定義プロトタイプを作成する。プロトタイプは規定または事前の設計に基づいたデータメンバとメソッドを保持しており、クローンオブジェクトのひな型になる。クローンはそのプロトタイプへの参照を保持しており、プロトタイプはその親プロトタイプへの参照を保持している。これは継承相当の機能になる。プロトタイプを複製してクローンオブジェクトが生成される。クローンオブジェクトはそのプロトタイプと同じデータメンバとメソッドを保持する事になるが、プロトタイプ専用に指定されたメンバは除かれる。クローンオブジェクトのメソッドは自由に付け替えできるので、これは多態性相当の機能になる。
メッセージ
オブジェクト指向で言われるメッセージ(message)は、複数の方面の考え方が混同されている曖昧な用語になっている。元々はSmalltalkから始まったメッセージ構文ベースのオブジェクト指向の中心機構である。以前はクラスベースの方でもメソッドの呼び出しをメッセージを送るという風に考えることが推奨されていた。メッセージはオブジェクトのコミュニケーション手段と標榜されているが、その忠実な実装内容はそれほど知られていないのが実情である。最も混同されているものにアクターモデルがあるが、そこで言われる非同期性とオブジェクト指向で言われる評価の遅延性は現行の実装スタイルではそれほど共通していない。コンポーネント準拠ソフトウェア工学とオブジェクトリクエストブローカーで言われるソフトウェアコンポーネント同士の通信もメッセージパッシングと呼ばれることが多いが、その仕様と機能は動的ディスパッチに該当するものである。メッセージのオブジェクト指向的運用はメッセージングと名付けられている。具体的な機能例としてはSmalltalk、Objective-C、Selfのメッセージレシーバーと、Rubyのメソッドミッシングなどがあるが、いずれもメッセージングの本質ではないとも言われている。
インスタンス
instance)はクラスベースではクラスを実体化(量化)したものであり、実装レベルで言うとデータメンバと仮想関数テーブルをメモリ上に展開したものになる。プロトタイプベースではプロトタイプオブジェクトのクローンで生成されたオブジェクトを指す。実装レベルで言うとメモリ上に展開された中間参照ポインタの動的配列になる。
データメンバ
data member)はクラスまたはオブジェクトに属する変数。言語によってフィールド、プロパティ、メンバ変数、属性と呼ばれる。データメンバは、クラスデータメンバとインスタンスデータメンバに分かれる。クラスデータメンバは静的データメンバとも呼ばれる。その中で定数化されたものはクラス定数と呼ばれる。クラスデータメンバはクラス名の名前空間でスコープされたグローバル変数と同じものであり、プログラム開始時から終了時まで確保される。インスタンスデータメンバはインスタンス生成時にメモリ上に確保されるものであり、その破棄時に消滅する。プロトタイプベースではプロトタイプオブジェクト専用のデータメンバが静的データメンバ相当になる。
メソッド
method)はクラスまたはオブジェクトに属する関数。言語によってはメンバ関数とも呼ばれる。データメンバの参照に特化したものはゲッター(getter)アクセッサ(accessor)と呼ばれる。データメンバの変更に特化したものはセッター(setter)ミューテイタ(mutator)と呼ばれる。メソッドは、クラスメソッドとインスタンスメソッドに分かれる。クラスメソッドは静的メソッドとも呼ばれる。クラスメソッドはクラス名の名前空間でスコープされたグローバル関数と同じものである。インスタンスメソッドを呼び出すにはそのインスタンス参照が必要になり、これはthis参照と呼ばれる。プロトタイプベースではプロトタイプオブジェクト専用のメソッドが静的メソッド相当になる。
コンストラクタ
constructor)はインスタンス生成時に呼び出されるそのクラスのメソッドである。インスタンスデータメンバを任意の値で初期化するためのものであるが、その他の初期化コードも記述できる。プロトタイプベースではシステム提供プロトタイプが保持する生成用メソッドまたは生成用のグローバル関数がコンストラクタ相当になる。
デストラクタ
destructor)はインスタンス破棄時に呼び出されるそのクラスのメソッドである。インスタンス破棄の影響を解決する任意の後始末コードを記述できる。インスタンスの破棄は占有メモリの解放を意味する。なお、ガーベジコレクタ実装言語ではファイナライザになっている事がある。プログラマが呼び出すデストラクタの方はその終了がメモリ解放に直結しているのに対し、ガーベジコレクタが呼び出すファイナライザの方はそうではない。
this参照
this)は、言語によっては「self」や「me」とも呼ばれる。インスタンスメソッドを呼び出す時は、このthis参照が暗黙の引数として渡されている。this参照はその該当インスタンスを指すポインタである。そのメソッド内でデータメンバにアクセスする時は、システム内で「this.データメンバ」のように変換されている。データにメソッドを付属させるカプセル化を実現するための仕組みである。
super参照
super)は、継承構造のインスタンスメソッド内で用いられるものであり、現行クラスの直上スーパークラスのメソッドとデータメンバにアクセスするための参照である。オーバーライドやドミナンスを無視して、スーパクラスのメンバを呼び出すための仕組みである。
アクセスコントロール
access control)は、カプセル化の情報隠蔽に基づいた機能であり、クラス内のデータメンバとメソッドの可視性を決定するものである。これはレキシカルスコープ基準とクライアント基準の二通りがある。レキシカルスコープ基準の可視性はプライベート、プロテクト、パブリックの三種が基本である。プライベートは同クラス内のみ、プロテクトは同クラス内と派生クラス内のみ、パブリックはどこからでもアクセス可能である。クライアント基準の可視性は自クラス内のメンバへのアクセスを許可するクライアントクラスないしフレンドクラスを定義する方法で決められる。言語によってはクライアントクラス指定は同時にそのサブクラス指定も兼ねている。この場合は継承関係を利用したクラス群の一括クライアント定義が可能である。
コピーコンストラクタ
copy constructor)は、メソッドの引数に対する値インスタンスの値渡しの時に呼び出されるコンストラクタである。値渡しはインスタンス内容全体のメモリコピーであり、基本データ型では特に問題は生じないが、そうでないクラスのインスタンスでは例えばあるリソースへの参照を保持している場合に好ましくない保持重複が発生する事になる。呼び出されたコピーコンストラクタは値インスタンスを受け取り、単純コピーが許されない部分に任意の処理を施して生成した値インスタンスのコピーを引数へと渡す。
オーバーロード
overloading)は一つのメソッド名に複数の異なるパラメータリスト(引数欄)を付けたものを列挙してメソッド名を多重定義すること。演算子もオーバーロード対象であり、単項演算子なら一つの引数の型、二項演算子なら二つの引数の型を多重定義することで演算対象の値の型ごとに計算内容をカスタマイズできる。任意数の引数の型を多重定義できる( )演算子は、クロージャまたは関数オブジェクトの表現に用いられる。アドホック多相とされる。
メソッド拡張
method extension)は、クラス定義とは別の場所でそのクラスに対する追加メソッドを定義できる機能である。これは状況に合わせてデータ抽象の表現に幅を持たせることを目的にしている。これには数々の書式があるが代表的なのは、静的メソッドまたは静的関数の第1引数をthis修飾して、その第1引数のクラス(型)に対してその静的メソッドをインスタンスメソッドとして追加するというものである。静的メソッドはそのクラススコープ内の限定拡張にできる。静的関数はネスト関数にしてそのローカルスコープ内の限定拡張にできる。双方はグローバル用途にすることもできる。アドホック多相とされる。
オーバーライド
method overriding)とは継承による階層的クラス構造のインスタンスにおいて、サブクラスのメソッドシグネチャの処理内容でそのスーパークラス側の同じメソッドシグネチャの処理内容を置き換える仕組みを指す。メソッドシグネチャとは「返り値+メソッド名+引数欄」で構成される識別単位である。この指定は親側のメソッドが上書きされるのをデフォルトにするのと、子側のメソッドで上書きするのをデフォルトにする二通りがある。前者なら子側のメソッドをoverrideredefineで修飾しそれで親側メソッドを上書きする。後者なら親側のメソッドをvirtualdeferredabstractで修飾しそれは子側メソッドで上書きされる。それとは別にただメソッドを上書き不可にする場合はfinalなどで修飾する。オーバーライドはメソッド名を参照アドレスにマッピングする仮想関数テーブルと呼ばれる機能を用いて実現される。
ドミナンス
dominance)は言語によってハイディング(hiding)マスキング(masking)とも呼ばれる。継承による階層的クラス構造において、サブクラスのメンバがスーパークラスの同名のメンバを隠していることを指す。親クラスのAメソッドを子クラスが同名Aメソッドでドミナンスした場合、子の型で参照しているインスタンスはそこでAのサーチが止まって子Aが呼び出される。ただし親の型で参照すれば親Aを呼び出せる。オーバーライドと異なり、参照する型でインスタンスの振る舞いを変えるための単純な仕組みでもある。
仮想継承
virtual inheritance)は、多重継承での菱形継承問題を回避するための仕組みである。菱形継承問題とは共にAクラスを親とするBクラスとCクラスの双方を継承した場合に、その継承構造上でAクラスが二つ重なって存在することになる不具合である。仮想継承では専用のテーブルが用意されて、そこでクラス名が参照アドレスにマッピングされる。BクラスからのAクラスと、CクラスからのAクラスは共に同じ参照アドレスをマッピングするのでAクラスはひとつにまとめられる事になる。同時に一度辿ったクラスは省略される事にもなる。
C3線形化
C3 linearization)は、多重継承で用いられる幅優先のメソッド解決順序(Method Resolution Order)である。メソッドはクラスメンバと読み替えてもよい。自クラス内に無いメンバを探すための親クラスの順序を決定する仕組みである。一般の多重継承では、深さ優先検索によるメソッド解決順序が用いられて親クラスの重複は仮想継承で解決される。それに対してC3線形化では、深さレベルを先行基準にした親クラスの整列と重複した親クラスのマージが行われて、その結果の親クラス線形化リストは幅優先検索になる。
抽象クラス
abstract class)は、クラスメンバの一部のメソッドだけが抽象化されているクラスを意味する。抽象化されたメソッドとは、メソッドシグネチャ(返り値+メソッド名+引数欄)だけが定義されてコード内容が省略されているメソッドを意味する。抽象クラスはインスタンス化できないので継承専用になる。抽象メソッドは、サブクラスの方でメソッドのコード内容が実装されてオーバーライドされる。
インターフェース
interface)はプログラム概念と機能名の双方を指す用語である。言語によってはプロトコルと言われる。抽象メソッドと実メソッドをメンバにできる純粋抽象〜半抽象クラスを意味する。クラスの振る舞い側面を抜き出した抽象体である。クラスによるインターフェースの継承は実装と呼ばれる。多重実装可が普通である。ミックスインとの違いは、抽象階層に焦点が当てられている事であり、直下の実装オブジェクトを共通の振る舞い側面でまとめることがその役割である。インターフェースは自身の実装オブジェクトをグループ化できる。記名的型付け英語版に準拠しているのでインターフェースの実装の明記が振る舞い側面の識別基準になる。インターフェースは抽象メソッド主体なので多重継承時のメンバ名の重複はあまり問題にならない。共通の実装メソッドに集約されるからである。インターフェースは非インスタンス対象である。
ミックスイン
mixin)はインターフェースに似たプログラム概念を指す用語である。機能名は言語によってトレイト、プロトコル、構造型(structural type)と言われる。抽象メソッドと実メソッドとデータメンバをメンバにできる継承専用クラスを意味する。クラスを特徴付けるための構成パーツである。クラスによるトレイトの継承は実装と呼ばれる。多重実装可が普通である。インターフェースとの違いは、トレイトの実装階層に焦点が当てられている事であり、オブジェクトを所有メンバで特定してまとめることがその役割である。トレイトは自身の上位集合であるオブジェクトをグループ化できる。構造的型付け英語版に準拠しているので所属メンバ構成自体がトレイト等価性の識別基準になる。これはトレイト実装を明記していなくても、そのトレイトが内包する全メンバを所持していれば同じトレイトと見なされることを意味する。トレイトは合成や交差が可能である。トレイトは多重継承時のメンバ名重複の際にその参照の優先順位に注意する必要がある。トレイトは非インスタンス対象である。
ダックタイピング
(duck typing)は、特定のデータメンバ名またはメソッド名(メソッドシグネチャ)または識別子を持っているかどうかでインスタンスを分類すること。または選り分けること。選り分けられたインスタンスはその指名データメンバないし指名メソッドでのアクセス対象になる。動的型付けの機能であり、インスタンス、変数、オブジェクトを実行時に逐次判別するという点で型推論と区別されている。
型推論
オブジェクト指向下の型推論(type inference)は、型宣言ないし型注釈を省略して定義された変数の「型」が自動的に導き出される機能を指す。型はクラスと同義である。静的型付けの機能であり、ソースコードを解析しながら値の代入を始めとしたその変数の扱われ方によって型を導き出す。ここで導き出される「型」とは他の変数への代入可能性や、関数の引数への適用可能性といったあくまで等価性の基準で決められるので、プログラマが人為的な意味付けによる型定義を重視している場合は予期せぬ結果が発生することにもなる。
メタクラス
metaclass)とはクラス自体の定義情報であり、そのクラスが持つとされるデータメンバ、メソッド、スーパークラス、内部クラスなどの定義が記録されたいわゆるメタデータである。実装レベルでは、システム側が用意している特別なシングルトンオブジェクトと考えた方が分かりやすい。メタクラスの各種定義情報を参照または変更する機能はリフレクションと呼ばれる。メタクラスの変更はその対象クラスに直ちに反映される。クラスベースで用いられるものである。
リフレクション
reflection)は、メタクラスの定義情報を参照または変更する機能であるが、言語ごとに変更できる定義情報の範囲は異なっている。データメンバではデータ型、識別子、可視性が変更対象になる。メソッドではリターン型、識別子、パラメータリスト、可視性、仮想指定が変更対象になる。双方の追加定義と削除もできる事がある。スーパークラスも変更できる事がある。また、実行時の文字列(char配列やString)をデータメンバとメソッドの内部識別子として解釈できる機能もリフレクションに当たる。これは実行時の文字列によるデータメンバの参照とメソッドの呼び出しを可能にする。
メタアノテーション
metadata annotation)はクラスに任意の情報を埋め込める機能である。情報とは文字列と数値からなるキーワード、シンボル、テキストである。プログラマが自由な形式で書き込んで随時読み取るものであるが、システムから認識される形式のものもある。実装レベルではメタクラスに書き込まれてリフレクション機能またはその糖衣構文で読み取ることになる。マーカーインターフェースの拡張とも見なされている。メタアノテーションはクラス単位だけでなく、言語によってはインスタンス単位やメソッド単位でも埋め込むことができる。アドホック多相とされる。
動的ディスパッチ
dynamic dispatch)は、コンパイル時のメソッド名から呼び出されるメソッド内容が実行時に決定される仕組み全般を指す用語である。メソッドに引数を渡しての呼び出しを、オブジェクトにメッセージを発送(ディスパッチ)することになぞらえた事が由来である。発送先は実行時に選択決定されるメソッド内容を指す。メッセージは「this参照×第1引数×第2引数..」といった直積集合で考えられているのでシングル、ダブルマルチプルといった呼称になっている。発送先はthisおよび各引数の派生関係の組み合わせで選択される。thisの派生関係のみ影響しているのは仮想関数なシングルディスパッチである。それがthisでなく引数ならばただのシングルになる。thisと各引数の2個以上の派生関係が影響しているのはダブルないしマルチプルディスパッチになる。
動的バインディング
dynamic binding)は、識別子が参照するまたは呼び出すオブジェクト、インスタンス、メソッド、データメンバなどのプログラム要素が、コンパイル時ではなく実行時に決められる仕組み全般を指す用語である。識別子はいわゆる変数名や関数名などを指す。
遅延バインディング
late binding)は、識別子が参照するオブジェクトをコンパイル時に決める事前バインディング(early binding)の対義語であり、この場合は識別子が参照するオブジェクトを実行時に決める動的バインディングと同じ意味で用いられる。また動的バインディングの中で、特に実行コードの動的ローディング機能を通して実装される方を遅延バインディングとする考え方もある。実行コードとはDLLやクラスライブラリやモジュールなどを指しており、それらが内包するクラスやメソッドを専用の不透明型または動的束縛型に代入する。その呼び出しのための内部識別子はコンパイル時には存在していないことが多いので、実行時の文字列(char配列やString)を内部識別子に解釈するためのリフレクション機能が多用されることになる。
パッケージ
package)は1個以上のクラスをまとめたものである。多くなったクラスをグループ化するための仕組みである。パッケージの定義は言語ごとに異なるが、名前空間namespace)と同等の機能になっているケースが多い。実装レベルではパッケージ名は自動的にクラス名の接頭辞になってクラス名を差別化し、名前衝突を回避している。
モジュール
module)は1個以上のクラスをまとめたものである。パッケージと似ているが、オブジェクト指向下のモジュールはもっぱら動的ローディング(遅延バインディング)と情報隠蔽に焦点を当てたプログラム概念である。情報隠蔽はカプセル化と同様に、自身の内包クラスの外部公開(輸出)と内部隠蔽を定義できる。自身が参照する他モジュールは輸入するという形式で明確に宣言される。動的ローディング用途のモジュールでは内包する基底クラスの詳細を明らかにしつつも、その派生クラスの種類と詳細を明らかにしていないケースが多々あるので、その派生クラスを代入するための動的束縛型は特に不透明型(opaque type)と呼ばれる。不透明型はもっぱら型制約と併せて用いられる。
モンキーパッチ
monkey patch)はモジュールやスクリプトファイルなどの動的ローディングを用いて、インタプリタ実行後またはコンパイル後のソースコード内容を変化させる手法である。ソースコードに特定のフィルター処理を記述しておき、その中で任意の箇所を動的ローディングされたモジュール内のクラスや関数や変数で置き換えさせる事で、その時の配置モジュールに合わせた処理内容の変化ができる。モジュールを外せばフィルター処理は無効になる。この置き換え(パッチ当て)は遅延バインディング相当である。ソースコードを変えなくてよいのが条件である。
ジェネリクス
generics)は、クラス内のデータメンバの型とメソッドの引数および返り値の型を総称化して型変数とし、コンストラクタの型引数に任意のクラスを適用してインスタンスを生成する仕組みを指す。型変数はその適用クラスで置き換えられる。クラス内の型の決定をコンストラクタまで先送りする手法であり、それはジェネリッククラスと呼ばれる。型変数をバリアンスにしたジェネリッククラスは、適用クラス値とその派生クラス値を同等に扱うことができる。型変数のバリアンスを用いないジェネリクスの方は特にテンプレートと呼ばれる。パラメトリック多相とされる。
テンプレート
template)は型変数のバリアンスを用いない方のジェネリクスを指す。コンパイル時にテンプレートクラスの型変数(仮型引数)部分に、そのコンストラクタコールそれぞれの型引数(実型引数)を当てはめたソースコードがそのまま複製される仕組みである。パラメトリック多相とされる。
バリアンス
variance)は、ある型で注釈された値に、その型の派生型または基底型の値も適用できるようにする仕組みである。その型の派生型の値も適用できるようにするのは共変性と呼ばれる。その型の基底型の値も適用できるようにするのは反変性と呼ばれる。バリアンスは一つの値を軸にした基底関係と派生関係の双方を包括する用語であるが、オブジェクト指向ではもっぱらジェネリクスのカテゴリで用いられており、ジェネリッククラスの型変数が対象にされている。ジェネリッククラスが扱う値の幅を持たせる機能である。共変クラスは適用クラス値とその派生クラス値を同等に扱うことができる。反変クラスは適用クラス値とその基底クラス値を同等に扱うことができる。バリアンスはジェネリッククラスの継承構造上で様々に応用される。
型制約
type constraint)は、ジェネリッククラスの型引数ないし型変数、代入値の型が実行時に決められる動的束縛型の変数、動的ローディング時に詳細が隠されたままの値が代入される不透明型の変数などの宣言に用いられるものである。それぞれは制約用の基準クラスで記号修飾され、その基準クラスの派生型の値が代入、束縛、適用されるという宣言になる。型引数と型変数では基準クラスの派生クラスが適用される宣言になる。動的束縛型では基準クラスの派生型の値が代入される宣言になる。不透明型では基準クラスの詳細不明な派生型の値が代入される宣言になる。これらは共変性に基づいている。それに対して反変性に基づいた型制約もあり、上述の説明内の’’派生’’を’’基底’’に置き換えたものになる。型制約は型境界(type bound)とも呼ばれる。
タイプメンバ
abstract type member)はジェネリッククラスのメンバ要素であり、ジェネリッククラス同士で型変数の内容をやり取りするための仲介要素である。Aクラスコンストラクタの型引数にBクラスを適用した際に、適切な代入定義が併記されたAクラス内のタイプメンバに、Bクラスがその内部で扱っている総称型もセットで適用できる。連想配列さながらにBクラスがキー的存在になってAクラスのタイプメンバ内容も決定されることから、この仕組みは関連型(associated type)と呼ばれる。
関数オブジェクト
オブジェクト指向下の関数オブジェクト(function object)は、メソッドそのものをオブジェクトして扱うというプログラム概念である。関数型プログラミングのクロージャをモデルにしている。単にインスタンスを関数名らしく見せるための糖衣構文である( )演算子オーバーロードや、メソッドシグネチャを型種にした関数ポインタ型の変数であるデリゲートなどの実装形式がある。デリゲート変数にはインスタンスメソッドへの参照が代入されてクラス種類とそのインスタンス種類による処理の多相を表現できる。プロトタイプベースにおける関数はオブジェクトそのものと言える存在であり、ローカル変数がプロパティ存在になっているのでクロージャが自然表現できる他、引数構成もプロパティ存在になっている場合はそれも変更できてクラスベースにおけるリフレクションを自然表現する。
コルーチン
オブジェクト指向下のイテレータジェネレータ、デコレータは、コルーチン(coroutine)機構に基づいている。通常のサブルーチンがコールする側の復帰アドレスだけをスタックに積むのに対して、コルーチンはコールする側とコールされる側双方の復帰アドレスをスタックに積むというサブルーチン機構である。各要素への作用が記されたオペレータが無名関数やラムダ式などの形態でデータコンテナに渡されると、各要素をフェッチするデータコンテナと、フェッチされた要素を参照ないし加工するオペレータが交互にコールスタックを用いて連携動作を繰り返す。イテレータはオペレータをそのまま扱う機能である。ジェネレータはオペレータが反復処理を終えた後にその総和値や選別リストを生成する機能である。デコレータはメソッドをデータコンテナと見なしそのメソッド内での関数コールをそれぞれ要素にして、オペレータがフェッチされた関数名と引数欄を見ながら任意の処理を挿入する機能である。
メッセージレシーバー
message receiver)はメッセージを受け取ることに特化されたメソッドである。メッセージレシーバーはインスタンスのデフォルトで呼び出される窓口レシーバーの形態と、指定メソッドが存在していない時に呼び出される補足レシーバーの形態がある。窓口レシーバーのメッセージはセレクタと引数のペアまたはそのどちらかだけという書式である。窓口レシーバーは極めて柔軟なプロセスを実現できるが、実装の煩雑さとオーバーヘッドが大きくなる。セレクタは識別子またはペア引数の注釈になる文字列である。セレクタはメソッドへの自動分岐が主な用途になるが、そのフィルター処理と取りこぼし処理の中でただのキーワードとしても自由に解釈できる。補足レシーバーのメッセージはメソッド名文字列と引数配列という書式になっており、いかなるメソッドシグネチャにも該当しなかった取りこぼしになる。このメソッド名文字列と引数配列を自由に解釈して柔軟な処理を行える。補足レシーバーの機能名はメソッドミッシングなどである。
サブタイピング
subtyping)はクラス(型)のあらゆる派生関係および派生構造の実装形式とその働き方を包括したプログラム概念である。サブタイプ多相(subtype polymorphism)とも呼ばれる。継承、オーバーライド、コンポジション、ジェネリクス、共変バリアンス、引数バリアンスによるディスパッチ、不透明型といったものは全てサブタイピングの一側面である。オブジェクト指向でよく使われるものは振る舞いサブタイピング(behavioral subtyping)であり、これに当てはまるものは継承とメソッドのオーバーライドを組み合わせた仮想関数である。
デリゲーション
委譲(delegation)。呼び出されたあるクラスのメソッドが自分への引数を他のクラスの同名メソッドにそのまま渡して、その同名メソッドからの返り値をそのまま呼び出し元に渡すという仕組みを指す。委譲先のクラスはhas-a関係で保有されているものになる。委譲先メソッドは必ずしも同名ではなくマッピング名の場合もあり、引数も構成を変えて渡される場合もある。
フォワーディング
転送(forwarding)。委譲先のクラスのメソッドが処理を行わずに、そのまた他のクラスの同名メソッドに引数をそのまま渡して、その返り値をそのまま呼び出し元に渡している場合、冒頭の委譲は転送になる。転送用メソッドではどのクラスに引数をパスするかという選択が行われるので、デリゲーションの多相を表現できる。
汎化
generalization)は継承によるis-a関係であり、サブクラスからスーパークラスへの連結を指す。
特化
specialization)は継承によるis-a関係であり、スーパークラスからサブクラスへの連結を指す。
実現
realization)はクラスの振る舞いの抽象化によるis-a関係であり、クラスからインターフェースへの連結を指す。振る舞いとは特定の目的に沿ったメソッド群である。
実装
implementation)はクラスの振る舞いの抽象化によるis-a関係であり、インターフェースからクラスへの連結を指す。振る舞いとは特定の目的に沿ったメソッド群である。
合成
composition)は強いhas-a関係。AクラスがBクラスをデータメンバにし、Aのコンストラクタと同時にBインスタンスが生成され、Aのデストラクタと同時にBインスタンスが破棄される場合、AはBの合成となる。Bが自身のサブクラスで交換される場合は分離とともに破棄される。
集約
aggregation)は弱いhas-a関係。AクラスがBクラスをデータメンバにし、Aクラスのコンストラクタとは関係なくBインスタンスが生成され、AクラスのデストラクタでBインスタンスが破棄されず、また分離時も破棄されない場合、AはBの集約となる。Aクラスがコレクション(配列、List、Set、Map)の仕組みでBインスタンスを持つ場合も、AはBの集約となる。コレクション性を強調する場合は収容(containment)とすることもある。
関連
association)。AクラスがBクラスのメソッドを呼び出す場合、AはBに関連しているとなる。AはBへの誘導可能性を持つとされる(A→B)。has-a関係で保有しているインスタンスのメソッドを呼び出すという意味で関連線は合成線または集約線と重ねて引かれることが多い。
依存
dependency)。AクラスのメソッドがBクラスのインスタンスを引数または返り値にしている場合、AはBに依存しているとなる。返り値の例として、Aのメソッドがその返り値としてBインスタンスを生成する場合も、AはBに依存しているとなる。
SOLID
SOLID Principles)は、汎化・特化・実現・実装・関連・依存の連結線に焦点を当てたクラスの設計原則である。(S)単一責任原則・(O)解放閉鎖原則・(L)リスコフの置換原則・(I)インターフェース分離原則・(D)依存の逆向き原則といった五つから成り立っている。1974年にバーバラ・リスコフが提唱した(L)と、1988年にバートランド・メイヤーが提唱した(O)に、ロバート・マーティンが(S)(I)(D)を加えて2000年に発表されている。これはSOLIDの文字通りの順に解釈できるようになっている。
(S)単一責任原則は、クラス(属性・操作)はただ一つの機能を表現するようにデザインすることを推奨している。(O)解放閉鎖原則は、クラスを抽象クラス(汎化・実現)と実装クラス(特化・実装)に分けてデザインすることを推奨している。(L)リスコフの置換原則は、汎化と特化に対する枠組みであり、実装クラスはその抽象クラスに対して振る舞い(=仮想関数)的に等価計算が可能であることを推奨している。(I)インターフェース分離原則は、実現と実装に対する枠組みであり、一つの抽象クラスは互いにその動作内容に影響し合うメソッドたちのみで構成されることを推奨している。(D)依存の逆向き原則は、関連と依存に対する枠組みであり、AクラスからBクラスに向けて関連線を引きたい場合は、Bクラスからその抽象クラスをAクラスに向けて実現し、その抽象クラスに対してAクラスからの関連線を引くことを推奨している。Bクラスとその抽象クラスを結ぶ依存線が、Aクラスからの関連線と逆向きになることがその名の由来である。
GOFデザインパターン
生成に関するパターン
  • Abstract factory
  • Factory Method
  • Builder
  • Prototype
  • Singleton
  • 構造に関するパターン
  • Adapter
  • Bridge
  • Composite
  • Decorator
  • Facade
  • Flyweight
  • Proxy
  • 振る舞いに関するパターン
  • Chain of Responsibility
  • Command
  • Interpreter
  • Iterator
  • Mediator
  • Memento
  • Observer
  • State
  • Strategy
  • Template Method
  • Visitor
  • 脚注

    関連項目