「オブジェクト指向プログラミング」の版間の差分
SpaceMeteor (会話 | 投稿記録) Apple改名に伴う変更 |
|||
(同じ利用者による、間の20版が非表示) | |||
6行目: | 6行目: | ||
{{Wikibooks|オブジェクト指向|オブジェクト指向}} |
{{Wikibooks|オブジェクト指向|オブジェクト指向}} |
||
'''オブジェクト指向プログラミング'''(オブジェクトしこうプログラミング、{{Lang-en-short|''object-oriented programming''}}、略語:OOP)とは、 |
'''オブジェクト指向プログラミング'''(オブジェクトしこうプログラミング、{{Lang-en-short|''object-oriented programming''}}、略語:OOP)とは[[プログラミング]]技法の一つであり、任意の関連性を持つ[[変数 (プログラミング)|データ]]([[変数 (プログラミング)|変数]]または[[プロパティ]])と[[サブルーチン|コード]]([[関数 (プログラミング)|関数]]または[[メソッド (計算機科学)|メソッド]])をひとつにまとめて[[オブジェクト (プログラミング)|オブジェクト]]とし、それぞれ異なる性質と役割を持たせたオブジェクトの様々な定義と、それらオブジェクトを相互作用させる様々なプロセスの設定を通して、[[コンピュータプログラム]]を構築することを主眼にしている。 |
||
'''[[オブジェクト指向]]'''という |
'''[[オブジェクト指向]]'''という言葉自体は、計算機科学者[[アラン・ケイ]]によって作り出されている。[[Simula]]言語などにインスパイアされたケイが1967年頃に口にしたと伝えられるこの造語は<ref name="造語">“At Utah sometime after Nov 66 when, 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. It was probably in 1967 when someone asked me what I was doing, and I said: "It's object-oriented programming".”[http://www.purl.org/stefan_ram/pub/doc_kay_oop_en%7CDr. Alan Kay on the Meaning of “Object-Oriented Programming”]</ref>、彼が1972年から開発を始めた言語[[Smalltalk]]の設計を説明する過程で明確な用語として発信され、1981年頃から知名度を得るようになった。80年代半ばになるとオブジェクト指向の解釈は、元々のアラン・ケイによる[[Smalltalk]]の様式と、1983年に計算機科学者[[ビャーネ・ストロヴストルップ]]が公開した[[C++]]の様式に二分された。前者では[[メッセージング]]という概念が基礎にされ、後者では[[Simula|Simula67]]由来の諸機能を加えた[[抽象データ型]]のスーパーセットが基礎にされていた。現在では、C++とそのモデルになったSimula67の様式がオブジェクト指向の標準になっている。 |
||
== 特徴 == |
== 特徴 == |
||
{{独自研究|date=2021-04}} |
|||
現行のOOPの世界では[[クラスベース]]と呼ばれるスタイルが{{要出典範囲|大幅な主流にされており、それに次ぐものとして[[プロトタイプベース]]がある|date=2021-04}}。クラスベースのOOP言語は「[[C++]]」「[[Java]]」「[[C Sharp|C#]]」などを代表格にして数多くあり、プロトタイプベースは「[[JavaScript]]」「[[Self]]」を{{要出典範囲|代表にして少数派である|date=2021-04}}。{{独自研究範囲|他のマイナーなスタイルとしては「[[Smalltalk]]」「[[Objective-C]]」のメッセージングベースがあるが、これはあくまで俗称なので正確な定義ではない|date=2021-04}}。「[[Swift (プログラミング言語)|Swift]]」ではプロトコル指向が標榜されている。{{独自研究範囲|現行のOOP言語は上述のスタイルよりも、[[静的型付け]]と[[動的型付け]]による分類の方が一般的になっており、クラスベースといった概念はそれほど認識されなくなっている|date=2021-04}}。静的型付けOOPの{{要出典範囲|代表格|date=2021-04}}は「[[C++]]」「[[Java]]」「[[C Sharp|C#]]」であり、動的型付けOOPの代表格は「[[Python]]」「[[JavaScript]]」「[[Ruby]]」である。{{要出典範囲|近年では[[手続き型プログラミング]]への回帰性や、[[関数型プログラミング]]との融合性による分類も考慮されるようになっている|date=2021-04}}。{{要出典範囲|それでもクラスベースと呼ばれるスタイル概念はOOPの基礎であり根幹であるという事実|date=2021-04}}に変わりはなく、本節でもクラスベースOOPを基準にして説明する。{{独自研究範囲|クラスベースは後述の[[抽象データ型]]をルーツにした標準的OOP思想であり|date=2021-04}}、その名の通り[[クラス (コンピュータ)|クラス]]と[[インスタンス]]の仕組みを中心にしたプログラミングスタイルである。 |
|||
OOPでは[[クラスベース]]と呼ばれるスタイルが標準にされている。他に[[プロトタイプベース]]と呼ばれる後発のスタイルもあるが、こちらは少数派である。クラスベースのOOP言語は、[[Smalltalk]]様式と[[C++]]様式で二分されており、C++の方がずっと多数派である。C++様式は、[[静的型付け]]と[[動的型付け]]の分類で大別されている。それらの主な特徴を箇条書きするとこうなる。 |
|||
* [[クラスベース]] - クラスのインスタンス化でオブジェクトを構築する。 |
|||
** [[Smalltalk]]様式 - クラスとインスタンスの[[相対性]]を付与してオブジェクトを体系化している。動的型付け中心。[[メッセージパッシング]]による多態性。 |
|||
** [[C++]]様式 - クラス(型)とインスタンス(型付け値)に分離してオブジェクトを体系化している。{{仮リンク|動的ディスパッチ|en|Dynamic dispatch}}による多態性。 |
|||
*** [[静的型付け]] - 実行時のクラス構成が基本的に固定されている。 |
|||
*** [[動的型付け]] - 実行時のクラス構成の変更が前提にされている。 |
|||
* [[プロトタイプベース]] - オブジェクトのクローンでオブジェクトを構築する。クラスとインスタンスの[[相対性]]をオブジェクトから撤廃して、プロトタイプでオブジェクトを体系化している。動的型付け中心。[[動的束縛|動的バインディング]]による多態性。 |
|||
本稿では最も標準的なクラスベースのC++様式の静的型付けを基準にして説明する。 |
|||
=== クラスとインスタンス === |
=== クラスとインスタンス === |
||
OOPの要点である[[クラス (コンピュータ)|クラス]]は、[[データ構造]]とそれを扱うための操作・振る舞いをひとまとめにした一種の[[モジュール|プログラムモジュール]]機能として定義されており、その実装は[[Simula|Simula67]]由来の[[継承 (プログラミング)|継承]]と動的ディスパッチを加えた[[抽象データ型]]のスーパーセットにされていることが多い。クラスのデータ構造は[[レコード型]]や[[構造体]]に似た書式で定義されることが多く、データ構造の要素は言語ごとに[[フィールド (計算機科学)|フィールド]]、[[プロパティ (プログラミング)|プロパティ]]、[[属性]]、メンバ変数などと呼ばれている。クラスに定義される操作・振る舞いは[[メソッド (計算機科学)|メソッド]]やメンバ[[関数 (プログラミング)|関数]]などと呼ばれる。メソッドとは、特定の対象に所属させることを前提にして[[This (プログラミング)|This参照]]を多相シンボルにしたディスパッチコードとしての[[関数 (プログラミング)|関数]]/[[手続き]]を意味している。 |
|||
{{main|クラス (コンピュータ)}} |
|||
[[クラス (コンピュータ)|クラス]]は、データの構造(形式)とそれに関連する操作・振る舞いをまとめたものという面がある。 |
|||
OOP言語の[[クラス (コンピュータ)|クラス]]と、そうでない言語での[[モジュール]]の違いを知ることは、OOPを理解する上でも重要になる。どちらも[[プロシージャ|手続き]]と[[データ]]の複合体であるが、クラスの第一の特徴はそれに[[継承 (プログラミング)|継承]]が備えられていることであり、次に[[This (プログラミング)|This参照]]の機構と、[[継承 (プログラミング)|継承]]構造上の内包的な{{仮リンク|動的ディスパッチ|en|Dynamic dispatch}}である。その次になる{{仮リンク|情報隠蔽|en|information hiding}}と、定義と実装に分離しての[[抽象化 (計算機科学)|抽象化]]の機構は、OOP以前の{{仮リンク|モジュラルプログラミング|en|Modular programming}}からのものである。振る舞い抽象を担っている[[インタフェース (抽象型)|インターフェース]]の概念もそちらが先例であったが、データ抽象を担っている[[カプセル化]](情報隠蔽とThis参照の融合)の採用はOOPが最初である。[[継承 (プログラミング)|継承]]構造上の内包的な動的ディスパッチによる[[派生型|サブタイピング]](=[[ポリモーフィズム]])はOOP発祥であるが、それを純粋化すると前述の[[インタフェース (抽象型)|インターフェース]]になる。OOP以前の[[構造化分析設計技法|構造化開発]]のモジュールには情報隠蔽はあるが、[[継承 (プログラミング)|継承]]はなく、[[This (プログラミング)|This参照]]・[[カプセル化]]・[[派生型|サブタイピング]]・[[インタフェース (抽象型)|インターフェース]]といった抽象化機構も持たない。なお、{{仮リンク|振る舞いサブタイピング|en|Behavioral subtyping}}は[[多重ディスパッチ]]中心OOPでは軽視されており、カプセル化は[[プロトタイプベース]]OOPでは軽視されている。従ってOOPを特徴付けている本来の要素とは即ち[[継承 (プログラミング)|継承]]であり、その真偽はともかくとして、継承はプログラムの再利用性と保守性を高めるためのメカニズムと定義されている。 |
|||
これだけの説明だと[[C言語]]<!--や[[Visual Basic]]系 非OOP?-->などの非OOP言語で使用される[[モジュール]]と同じようだが、 |
|||
クラスには [[カプセル化]]、[[継承 (プログラミング)|継承]]、[[ポリモーフィズム]](後述)といった考え方や言語機能が導入され、抽象化(abstraction)やコード再利用に役立つ。 |
|||
C++様式のクラスベースは[[オブジェクト (プログラミング)|オブジェクト]]を、[[型理論]]に沿った[[型システム|型]](type)と[[型付け|型付け値]](term)に分離している。その型の一種であるユーザー定義型(user defined type)がクラスにされており、その型付け値が[[インスタンス]]になっている。ユーザー定義型とは[[構造体]]と同じものであり、OOPでは[[手続き]](のポインタ)も構造体メンバにされている。ユーザー定義型は[[オブジェクト型]]とも呼ばれるが、このオブジェクト型と後節の「[[オブジェクト (プログラミング)|オブジェクト]]」の意味上の繋がりはない。[[クラス (コンピュータ)|クラス]]はインスタンスのひな型であり、[[インスタンス]]はクラスを実体化したものである。実体化=インスタンス化とは対象クラスの全変数内容を決定し、[[ヒープ領域]]の基底アドレス([[This (プログラミング)|This]])を定めた上でそこにメモリ展開するという作業を指している。コンパイル時にその構成が確定されるユーザー定義型は[[静的型付け]]であり、実行時にもその構成を変更できるユーザー定義型は[[動的型付け]]である。 |
|||
クラスは[[データ型|型]]としての役割りももつ。 |
|||
=== オブジェクトとは === |
|||
クラスの記述において、 |
|||
[[クラスベース]]言語での「[[オブジェクト (プログラミング)|オブジェクト]]」は、一般的には[[インスタンス]]を指す用語として説明されているが、このような重複語になった背景にはOOPの原点である[[Smalltalk]]からの事情がある。Smalltalkは、全てのプログラム要素は[[オブジェクト (プログラミング)|オブジェクト]]であり、全てのオブジェクトはクラスのインスタンス化であり、クラスもまたオブジェクトであると定義していた。そこでは[[クラス (コンピュータ)|クラス]]もまた[[メタクラス]](型)の[[インスタンス]]化(型付け値)になり、そのメタクラスもまた他のメタクラスのインスタンス化になっていたので、[[クラス (コンピュータ)|クラス]]と[[インスタンス]]は即ちオブジェクトの性質([[相対性]])を指すための言葉になっていた。これが[[C++]]様式では簡素化されて、クラスとインスタンスは[[型システム|型]]と[[型付け|型付け値]]の役割に固定され、メタクラスはクラス構成の動的変更機能に固定されたので、上述のインスタンス化の[[相互再帰]]が失われたオブジェクトは、インスタンスと同じ意味の言葉として残されることになった。またSmalltalkは、オブジェクトは他のオブジェクトと相互作用(interaction)するとも定義しており、この相互作用とは各種演算と同義である。Smalltalkのクラスは型付け値でもあるのでオブジェクトであるが、C++様式のクラスはただの型なので演算対象に出来ないことからオブジェクトではなくなっている。 |
|||
データの構造は名前を付けた複数のデータ要素をまとめた[[レコード型]]([[構造体]]型)かそれに似た形で定義されることが多く、この要素は(言語によって) [[フィールド (計算機科学)|フィールド]]、データメンバ、[[プロパティ (プログラミング)|プロパティ]]、[[属性]]、メンバ変数 などと呼ばれる。 |
|||
また、クラスに定義される操作・振る舞いは、(言語によって) [[メソッド (計算機科学)|メソッド]]、メンバ[[関数 (プログラミング)|関数]]、メンバ[[プロシージャ|手続き]] などと呼ばれる。 |
|||
Smalltalk方言の[[Self]]を原点とする[[プロトタイプベース]]は、オブジェクトからクラスとインスタンスの[[相対性]]を無くしたスタイルである。数値・文字列・配列・関数・シンボル・[[構造体]]([[オブジェクト型]])といった[[プリミティブ型|基本的な型]]は備えられているが、これはオブジェクト種類の区別に特化されたものなので、[[型理論]]に沿ったクラスベースの[[型システム|それ]]とは厳密には異なっている。クラス性質を除去したオブジェクトは事実上のインスタンスに一元化されており、その全てが相互作用(interaction)する。オブジェクトの表現はスロット(シンボルとコンテンツのペアデータ)の[[可変長配列]]でなされており、オブジェクトの識別は専ら[[ダックタイピング]]によってなされる。クラス概念が無いのでサブクラス化とインスタンス化は成立せず、代わりにクローン(複製)によってオブジェクトの継承がなされており、クローンはインスタンス化の代替になる。複製元オブジェクトは、複製先オブジェクトのプロトタイプと呼ばれる。 |
|||
{{独自研究範囲|クラスはデータとメソッドの構成を定義した型であるので、それを計算対象や代入対象になる値として扱うには[[インスタンス]]に実体化(量化)する必要がある。その用法でのクラスはユーザー定義型と呼ばれる。クラスはインスタンスのひな型であり、インスタンスはクラスを量化したものである。ここでの量化とは、そのクラスに属する変数の値を全て決定してメモリに展開する行為を指す。言語によっては後述の[[仮想関数テーブル]]もセットで展開する。インスタンスは別名としてオブジェクトとも呼ばれる。OOPの主役である'''オブジェクト'''の意味と用法は実は曖昧なのが現状であり言語ごとにも違いがある。|date=2021-04}} |
|||
=== オブジェクト指向の三大要素 === |
=== オブジェクト指向の三大要素 === |
||
[[クラスベース]]OOPの中心である[[クラス (コンピュータ)|クラス]]の実装様式を規定している以下の三項目は、日本では三大要素または三大原則などと呼ばれているが、各要素の軽視重視と導入方法は言語別に様々である。 |
|||
|date=2021-04}} |
|||
==== カプセル化 ==== |
|||
互いに関連するデータとメソッドをまとめてクラスとし、必要なデータとメソッドのみを外部公開し、それ以外をクラス内に隠蔽する機能を[[カプセル化]]と呼ぶ。外部公開されたデータとメソッドはクラス外からの直接アクセスが可能である。内部隠蔽されたデータとメソッドはクラス外からアクセスされないことが保証されこれは{{仮リンク|情報隠蔽|en|information hiding}}と呼ばれる。同クラス所属のメソッドを通してのデータの閲覧と変更はそのデータの抽象化を意味することになりこれは{{仮リンク|Data Abstraction|en|Abstraction (computer science)|label=データ抽象}}と呼ばれる。この二つがカプセル化の要点である。データ抽象を実装するための仕組みでもある[[This (プログラミング)|this参照]]については後節で述べられる。データ閲覧用メソッドはゲッター、データ変更用メソッドはセッターと呼ばれる。データとメソッドの外部公開範囲を、{{独自研究範囲|無制限・任意クラスグループ・派生クラスグループの三段階に分けて定義する機能|date=2021-04}}は[[アクセスコントロール]]と呼ばれる。 |
|||
==== |
==== [[カプセル化]] ==== |
||
データ構造とそれを扱うためのメソッド群を情報隠蔽の概念と合わせてモジュール化(パッケージ化)するという手法が[[カプセル化]]と呼ばれる。カプセル化されたメソッドは、[[This (プログラミング)|This値]]が暗黙の先頭引数として常に渡されるように実装される。This値とはクラスのデータ構造をメモリ展開するための[[ヒープ領域]]の基底アドレスを指しており、インスタンス化時に確定されたThis値によってメソッドは専用のデータ構造にアクセスできる。専用メソッドを通してのデータ構造の閲覧と変更は、[[抽象データ型]]の考え方に沿ったデータ構造の抽象化を意味することになり、これは{{仮リンク|Data Abstraction|en|Abstraction (computer science)|label=データ抽象}}と呼ばれる。データ閲覧用メソッドはゲッター/アクセッサと呼ばれ、データ変更用メソッドはセッター/ミューテイタと呼ばれる。 |
|||
{{see also|[[継承 (プログラミング)|継承]]|is-a関係|[[サブクラス (計算機科学)|サブクラス]]}} |
|||
{{日本語表現|date=2021年5月|section=1}} |
|||
既存クラスのデータ/メソッド構成に任意のデータ/メソッド構成を付け足して、既存構成+新規構成の新しいクラスを定義する機能を[[継承 (プログラミング)|継承]]と呼ぶ。その差分プログラミング目的の継承よりも、既存構成に抽象メソッドを置いて新規構成にその実体メソッドを置くという{{疑問点範囲|date=2021年5月|オーバーライド目的}}の継承の方が要点にされている。新規構成ではなく実装内容を付け足していくための継承である。 |
|||
{{仮リンク|情報隠蔽|en|information hiding}}とはそのクラスのデータ構造の各要素および各メソッドを必要に応じて内部隠蔽するという概念である。内部隠蔽されたデータ要素とメソッドはそのクラス外部からのアクセスが禁止される。[[抽象データ型]]本来の形式ではデータ構造のみが隠蔽対象になるので、これはデータ隠蔽とも呼ばれる。隠蔽指定外のデータ要素とメソッドは外部公開されて、そのクラス外部からもアクセス可能になる。外部公開の範囲を指定する機能は[[アクセスコントロール]]と呼ばれており、これが内部隠蔽の仕組みを担っている。クラスの[[レキシカルスコープ]]を基準にした段階的なアクセス許可範囲は可視性と呼ばれる。可視性は無制限・任意クラスグループ限定・派生クラスグループ限定・自クラス限定(内部隠蔽)の四段階が[[統一モデリング言語|UML]]では標準にされている。 |
|||
既存クラスは基底クラス、親クラス、スーパークラスなどと呼ばれ、新しいクラスは派生クラス、子クラス、サブクラスなどと呼ばれる。継承できるクラスが一つに限られている単一継承を採用している言語と、継承できるクラスの数に制限がない多重継承を採用している言語がある。 |
|||
==== [[継承 (プログラミング)|継承]] ==== |
|||
<!-- 概念的に インターフェース継承(型継承、[[is-a関係]]、下位分類)と 実装の継承 の2つの側面があるといったことを書いた方がよさそう。 記述によっては両方の継承関係が同時に生じる。 --> |
|||
既存クラスのデータ/メソッド構成に、任意のデータ/メソッド構成を付け足して、既存構成+新規構成の新しいクラスを定義するという手法が[[継承 (プログラミング)|継承]]と呼ばれる。また、各クラスの共通構成パートを括りだして特有構成パートと分離することでオブジェクトを分類体系化し、同時にその共通構成パートの記号化によってソースコード内の重複記述を削減する機能とも解釈される。これは差分プログラミング目的の継承であり、1990年代までは多用された。2000年代になると[[SOLID]]原則の重視に伴なって、既存構成に抽象メソッドを置いて新規構成にその実体メソッドを置くという[[オーバーライド|メソッドオーバーライド]]を用いるための{{仮リンク|振る舞いサブタイピング|en|Behavioral subtyping}}目的の継承が要点にされるようになっている。データ/メソッドの新規構成ではなく、メソッドの実装内容を付け足していくための継承である。そこでは特にデータ構造を付け足しての階層的なデータの分散配置は倦厭されている。 |
|||
抽象メソッドを持つクラスは抽象クラスと呼ばれる。 |
|||
抽象メソッドのみで構成される純粋抽象クラスの継承は、インターフェースの継承(または実装)などと呼ばれて抽象化目的の継承になる。 |
|||
既存クラスはスーパークラス・親クラス・基底クラスなどと呼ばれ、新しいクラスはサブクラス・子クラス・派生クラスなどと呼ばれる。親と子は差分プログラミング重視で用いられ、基底と派生はサブタイピング重視で用いられる。継承できるクラスが一つに限られている単一継承を採用している言語と、継承できるクラスの数に制限がない多重継承を採用している言語がある。<!-- 概念的に インターフェース継承(型継承、[[is-a関係]]、下位分類)と 実装の継承 の2つの側面があるといったことを書いた方がよさそう。 記述によっては両方の継承関係が同時に生じる。 -->抽象メソッドを持つクラスは抽象クラスと呼ばれる。基底クラス側で返り値の型と引数リストのみが定義されて実行コードブロックが未定義のままの抽象メソッドは、その派生クラス側の実体メソッドで[[オーバーライド]]される。オーバーライドは遅延結合による多相な[[複雑系]][[アルゴリズム]]を表現するオープン[[再帰]](open recursion)のメカニズムにもなっている。 |
|||
==== ポリモーフィズム ==== |
|||
異なる種類のクラスに同一の操作インターフェースを持たせる機能を[[ポリモーフィズム]](多態性)と呼ぶ。{{独自研究範囲|これはクラスの継承関係を利用して、コンパイル時のメソッド名から呼び出されるプロセス内容を実行時に決定するという仕組みを指す|date=2021-04}}。{{独自研究範囲|その実装は{{仮リンク|仮想関数(OOP)|en|Virtual function|label=仮想関数}}と呼ばれており、クラスベースOOPのポリモーフィズムはイコール仮想関数となっている。仮想関数はスーパークラスの抽象メソッドの呼び出しを、それを[[オーバーライド]]したサブクラスの実体メソッドの呼び出しにつなげる機能である。抽象メソッドとオーバーライド機能については後節で述べる。ポリモーフィスムの要点は、同じメソッド名からその実行時に対応した異なる処理内容を呼び出せるようにすることである。|date=2021-04}} |
|||
抽象メソッドのみで構成される純粋抽象クラスは[[インタフェース (抽象型)|インターフェース]]と呼ばれ、その継承は実装(implementaion)と呼ばれる。実装継承という俗称もある。実装はサブタイピングによる[[Is-a]]関係を完全順守させるためのメカニズムであり、継承とは別個の概念として扱われている。{{仮リンク|モジュラルプログラミング|en|Modular programming}}が先例になる実装は、[[継承 (プログラミング)|継承]]から[[派生型|派生]](subtyping)への先祖返りと言えるものである。 |
|||
=== インターフェース === |
|||
インターフェースという言葉は一般的な意味でも使われるが、 |
|||
ここで言う[[インタフェース (抽象型)|インターフェース]]は、 |
|||
[[抽象型]](抽象クラス)の一種で、抽象メソッドのみで構成されているものである。 |
|||
言語によってはクラスとは区別される(Javaなど)。 |
|||
インターフェースはカプセル化を更に突き詰めた仕組みであり、データ抽象とメソッド抽象と情報隠蔽を合わせて実現する{{独自研究範囲|最もOOPらしい機能と言える|date=2021-04}}。 |
|||
ゲッター、セッター、プロセスになる各抽象メソッドの実装内容は利用者側から隠されて実行時のその都度に決定される。 |
|||
=== |
==== [[ポリモーフィズム]] ==== |
||
異なる種類のクラスに共通の操作インターフェースを持たせてオブジェクトの振る舞いを抽象化するという手法が[[ポリモーフィズム]](多態性)と呼ばれる。OOPで語られる多態性はもっぱら継承構造を利用した{{仮リンク|サブタイプ多相|en|Subtyping}}を対象にしているが、{{仮リンク|アドホック多相|en|Ad hoc polymorphism}}もサポート的に用いられており、{{仮リンク|パラメトリック多相|en|Parametric polymorphism}}はコンポジションで用いられている。そのサブタイプ多相の設計としてはコンパイル時に確定されたメソッド名から呼び出されるプロセス内容が実行時に決定されるという仕組みを指しており、一つのメソッド名からその実行時状態に合わせた個別のメソッド処理が呼び出されるようにするという演繹的意味と、各クラスの同種機能メソッドを一つの共通メソッドにまとめて実行時状態に合わせたメソッド処理が呼び出されるようにするという帰納的意味がある。 |
|||
コンポジション(合成)とデリゲーション(委譲)は、{{疑問点範囲|date=2021年5月|継承の原型的仕組みであり、別の言い方をすると合成+委譲を最適化した機能が継承である}}。実装継承は[[is-a]]構造の委譲、合成は[[has-a]]構造の委譲と読み替える事ができる。合成とは、クラスに特定処理の委譲先となる部品クラスを複数持たせた構造であり、合成クラスがデータ/メソッドを要求されて自身が未所持の場合は、対応可能な部品クラスを選択して委譲するという仕組みである。 |
|||
その実装としては[[オーバーライド|メソッドオーバーライド]]機能を活用した仮想関数と、実行時[[パターンマッチング]]機能を活用した総称関数の二つが挙げられる。{{仮リンク|仮想メソッド(OOP)|en|Virtual function|label=仮想関数}}はスーパークラスの抽象メソッドの呼び出しを、それを[[オーバーライド]]したサブクラスの実体メソッドの呼び出しにつなげるという動的ディスパッチ機能である。{{仮リンク|総称関数|en|generic function}}はオブジェクトの実行時[[パターンマッチング]]を使用する独立関数であり、その引数にされた各オブジェクトの型(=クラス)の組み合わせに従って実行コードブロックを選択決定するという[[多重ディスパッチ]]機能である。 |
|||
その要求判別と選択過程を自動化したのが(実装)継承であり、部品クラスを親クラスに置き換えて暗黙の委譲先にしたものである。しかしその暗黙委譲は実際に参照されるデータ/メソッドの把握を困難にするという欠点も明らかになったので、合成の価値が再認識されるようになった。既存構成に新規構成を付け足していく差分プログラミング目的では、継承よりも合成を用いる方がよいと考えられている{{要出典|date=2021年5月}}。 |
|||
=== コンポジションとデリゲーションとジェネリクス === |
|||
{{仮リンク|オブジェクトコンポジション|en|Object composition|label=コンポジション (合成)}}と[[委譲|デリゲーション]](委譲)は、[[継承 (プログラミング)|継承]]を帰納的に分解した仕組みと言えるものであり、または合成と委譲による連携を演繹的に最適化した仕組みが継承であるとも言える。OOPにおける合成と委譲は、OOP原点の[[Smalltalk]]が[[Simula|Simula67]]発の継承機能をオブジェクトの再帰構成という観点から再解釈したものがデザイン上のルーツになっている。継承は例外も多いが[[Is-a]]の委譲であるのに対して、合成は[[Has-a]]の委譲と読み替えることができる。 |
|||
合成とは、特定処理の委譲先になる部品クラスの1個以上を持たせたクラス構造であり、そのクラスがとある処理を要求されてそれに対応できるデータ/メソッドを持っていない場合は、それに対応できる部品クラスを選択して処理を委譲するという仕組みである。その要求判別と選択過程を自動サーチ化したものが継承であり、部品クラスを基底クラスに置き換えて暗黙の委譲先にしたものである。しかしその自動サーチは、クラス階層に分散配置されているデータ/メソッドのどれが実際にアクセスされるのかという把握を困難にしたので、ここで差分プログラミング用途の継承の欠点が取り沙汰されるようになり、2000年代になるとクラスの機能拡張と分類体系化では、継承と合成の使い分けが重視されるようになった。 |
|||
[[ジェネリックプログラミング|ジェネリクス]](総称化)は、オブジェクトの[[データ構造]]を汎用化して、それに汎用[[アルゴリズム]]の数々を適用できるようにした技術である。様々なデータ要素を内包するコンポジション(合成)オブジェクトは[[コンテナ (データ型)|コンテナ]](List/Set/Mapなど)と呼ばれ、その要素の型を型変数化することで汎用的な[[データ構造]]を表現し、その要素の型はコンテナのインスタンス化時に与えられる型パラメータによって決定される。ジェネリクスは[[データ構造]]と[[アルゴリズム]]の柔軟な[[ソフトウェアコンポーネント|コンポーネント化]]を促進させた。また、[[圏論]]の[[圏 (圏論)|圏]]に見立てられた[[コンテナ (データ型)|コンテナ]]および[[対象 (圏論)|対象]]に見立てられたデータ要素の入れ子構造と、[[射 (圏論)|射]]に見立てられた[[派生型|サブタイピング]]の融合は、[[共変性と反変性 (計算機科学)|共変性と反変性]]の手法に発展した。 |
|||
=== 動的ディスパッチとメッセージパッシング === |
=== 動的ディスパッチとメッセージパッシング === |
||
動的ディスパッチはポリモーフィズムの |
{{仮リンク|動的ディスパッチ|en|Dynamic dispatch}}は、OOPの[[ポリモーフィズム]]の基礎的仕組みであり、例えば継承構造上での[[This (プログラミング)|This]]によるシングルディスパッチを最適化した機能が[[仮想関数]]になっている。動的ディスパッチは、コンパイル時に確定されたメソッド名から呼び出されるメソッド内容(実行コードブロック)が実行時に決定される仕組み全般を指している総称用語である。メソッドに与えられた各引数の型の組み合わせに従って、実行コードブロックが選択分岐される仕組みのシングルディスパッチと[[多重ディスパッチ]]を包括している。先頭引数の型[[パターンマッチング]]固定で実行コードブロックが選択決定(ディスパッチ)されるのはシングルになり、そうでないならば多重になる。 |
||
メッセージパッシングでは |
メッセージパッシングでは前述の引数の型だけでなく、メソッド名も実行時に解釈される要素にされており、そこでただのシンボルとして扱われるメソッド名はセレクタと呼ばれている。セレクタはメッセージ式を基本文とするSmalltalk様式のOOPで用いられる。<code>object selector: param</code>ような書式でオブジェクトの共通窓口となるメッセージレシーバーに、セレクタと引数値で構成されたメッセージが送られる。また、<code>object.call(method_name, param)</code>のような書式でオブジェクトの共通窓口関数をコールするのもメッセージパッシングと呼ばれる。これは[[Remote Procedure Call|遠隔手続き呼出し]]や[[Object Request Broker|オブジェクト要求ブローカー]]で用いられており、分散オブジェクトの標準的なインターフェース機構になっている。メソッド名(関数名)も実行時に解釈されるという特徴を指してメッセージパッシングと呼ぶ。メッセージレシーバー内では、渡されたセレクタの文字列照合の条件分岐によって実行コードブロックが選択されることが多く、OOP原点のSmalltalkは、引数と[[This (プログラミング)|This]]が与えられるその実行コードブロックをメソッドと呼んでいた。 |
||
=== |
=== インターフェース === |
||
[[インタフェース (抽象型)|インターフェース]]は、[[カプセル化]]を更に突き詰めた概念であり、[[継承 (プログラミング)|継承]]の[[派生型|サブタイピング]]用法を更に突き詰めた概念である実装(implementation)を体現している仕組みである。インターフェースは、[[オブジェクト (プログラミング)|オブジェクト]]の情報隠蔽とデータ抽象とメソッド抽象を合わせて表現する。[[クラス (コンピュータ)|クラス]]から見たインターフェースは、自身のサービスのモデリングであり、この作法は実現(realization)と呼ばれる。OOPの[[インタフェース (抽象型)|インターフェース]]は、基本的には抽象メソッドのみで構成される[[抽象型]]と定義されている。[[ソフトウェアコンポーネント]]間の相互通信媒体として用いられることが多く、[[SOLID|SOLID原則]]の重視に伴ないクラス間の[[結合度|結合]]にも多用される。 |
|||
{{see|プロトタイプベース}} |
|||
[[クラスベース]]のクラスと実体化とインスタンスは、[[プロトタイプベース]]ではプロトタイプと複製とオブジェクトに置き換わる。プロトタイプとオブジェクトは双方とも、プロパティとメソッドを自由に付け替え可能にされており{{独自研究範囲|これは[[動的束縛|動的バインディング]]とも呼ばれ、そのプロパティとメソッドの構成による型は[[ダックタイピング]]で判別される。この特徴は同時に[[ポリモーフィズム]]になる|date=2021-04}}。 |
|||
<!-- その用法は[[関数オブジェクト]]と変数オブジェクト(値オブジェクト)に大別され、前者のプロパティは[[二階述語論理]]、後者のメソッドは[[高階述語論理]]の表現体になり、それ自体が[[メタデータ|メタ]]視点から抽象化されたオブジェクトには[[カプセル化]]という概念は必要でなくなる。 --> |
|||
各言語での導入様式としては、抽象メソッドのみで構成される純粋抽象クラスが基本形にされており、インスタンス化はできない継承専用クラスになる。多重継承が前提にされている。インターフェースの抽象メソッドのメソッド内容(実行コードブロック)は、それを実装したクラスの同名メソッドで記述されることになる。インターフェースの各抽象メソッドはセッター・ゲッター・プロセスなどとして動作し、その実装内容は利用者側からは隠されて実行時のその都度に決定されることになる。 |
|||
{{独自研究範囲|[[継承 (プログラミング)|継承]]では委譲先になる部品オブジェクトの自由な合成が重視されており、その部品は[[トレイト]]と呼ばれる事が多くその合成は多重継承と同義になる。前述のコンポジションは[[has-a]]構造であるが、トレイトは[[is-a]]構造の合成である。トレイトの実装はもっぱら構造的型付けで判別される。|date=2021-04}} |
|||
=== 多重ディスパッチとミックスインとトレイト === |
|||
{{独自研究範囲|[[プロトタイプベース]]は動的な[[関数型プログラミング]]に似た性質になっているが、オブジェクトの柔軟な用法に対しての一定の枠組みが必要であるとも考えられて静的な[[クラス (コンピュータ)|クラス]]定義が積極的に導入されるようになり、現状では関数型とOOPのハイブリッドのようなパラダイムに落ち着いている。|date=2021-04}} |
|||
[[多重ディスパッチ]]は、オブジェクトの{{仮リンク|振る舞いサブタイピング|en|Behavioral subtyping}}を、オブジェクトの実行時[[パターンマッチング]]に置き換えた[[ポリモーフィズム]]・アプローチである。サブタイピングが同名アルゴリズムを個々の派生クラスに分散記述するのに対して、多重ディスパッチは個々の派生クラスの同名アルゴリズムを独立関数内で一括記述する。その独立関数が多重ディスパッチではメソッドと呼ばれるが、正確にはディスパッチ先になる個々の実行コードブロックがメソッドであり、その多重定義で総称メソッド({{仮リンク|総称関数|en|generic function}})と呼ばれる。総称メソッドは1個以上のオブジェクトを引数にし、その各引数オブジェクトの型(クラス)の組み合わせに従って、実行コードブロックを選択決定(ディスパッチ)する。その型[[パターンマッチング]]では、クラスに付加された[[アノテーション]]識別([[マーカーインタフェース|マーカーインターフェース]])の方が重視されることもあり、これがアドホック多相とサブタイプ多相の双方で継承を解釈できる[[ミックスイン]]の原点になっている。 |
|||
[[ミックスイン]](Mix-in)は、[[スーパークラス (計算機科学)|上位クラス]]からの機能取得と[[サブクラス (計算機科学)|下位クラス]]への機能注入を主体にした[[継承 (プログラミング)|継承]]アプローチであり、そこでは上位クラスと下位クラスの[[Is-a関係]]が軽視されていることも特徴である。これは[[サブタイプ|サブタイピング]]特化の実装継承とは反対に、差分プログラミング用法に特化させた継承と言えるものである。ただしこの差分プログラミングはあくまで[[メソッド (計算機科学)|メソッド]]限定である。こちらも[[多重継承]]が前提であり、インスタンス化はできない継承専用クラス(Mix-inクラス)を扱っている。Mix-inクラスの代表的な実装形態は[[トレイト]]である。トレイトはメソッドの集合体であり、そのメソッド集合を一つの機能にした分類体系化が可能であり、そのメソッド間で状態を共有するためのデータメンバも所有できることがある。トレイトは機能注入用のメソッド実体と、[[オーバーライド]]用の[[関数プロトタイプ|メソッド定義]]の双方を持てるが、[[インタフェース (抽象型)|インターフェース]]とは異なり前者のメソッド実体の方が中心である。Mix-inの設計は各言語で多様化しており、Mix-inクラスは各言語によって[[トレイト]]・モジュール・ロール・プロトコル・[[型クラス]]などの様々な形態で導入されている。クラスをそのままMix-in化できる言語もあり、[[関数オブジェクト]]をMix-in化できる言語もあり、メソッド実装も可能にしたインターフェースをMix-in化できる言語もある。[[トレイト]]のMix-inは{{仮リンク|構造的型付け|en|Structural type system}}で扱われることが多く、従来の[[クラス (コンピュータ)|クラス]]の継承と[[インタフェース (抽象型)|インターフェース]]の実装が{{仮リンク|記名的型付け|en|Nominal type system}}で扱われることと対比されている。 |
|||
=== メッセージング === |
|||
{{独自研究|date=2021-04}} |
|||
メッセージングはオブジェクト指向の父である{{要出典範囲|[[アラン・ケイ]]が最重視していた源流思想である|date=2021-04}}。これは当時の[[LISP]]風プログラミングの変化球と言えるものであり、[[関数型プログラミング]]と対比させると分かりやすくなる。関数([[写像]])を値に適用するという関数型の基本形に対して、メッセージングでは写像をただのシンボル(=セレクタ)にしており、写像を融合させた値(=オブジェクト)にセレクタを送ると、その値専用の写像がセレクタ別に呼び出されるようになっている。写像から計算式を取り除いてただの象徴記号にしたものがセレクタであり、値と写像を融合したものがオブジェクトである。セレクタには引数としてのオブジェクトを続かせられる。セレクタ用法はたらい回し的なデリゲーションに適しているようである。メッセージングベースのOOPは恐らく永遠の途上段階にあるので、このセレクタ構文を用いているか否かが現在に到るまでの類別基準になっている。 |
|||
== 歴史 == |
== 歴史 == |
||
1954年に初の[[高水準言語]]・[[FORTRAN]]が登場すると、開発効率の劇的な向上と共にソフトウェア要求度も自然と高まりを見せてプログラム規模の急速な拡大が始まった。それに対応するために肥大化したメインルーチンを[[サブルーチン]]に分割する手法と、[[スパゲティプログラム|スパゲティ化]]した[[Goto文|goto命令]]を[[制御構造|制御構 |
1954年に初の[[高水準言語]]・[[FORTRAN]]が登場すると、開発効率の劇的な向上と共にソフトウェア要求度も自然と高まりを見せてプログラム規模の急速な拡大が始まった。それに対応するために肥大化したメインルーチンを[[サブルーチン]]に分割する手法と、[[スパゲティプログラム|スパゲティ化]]した[[Goto文|goto命令]]を[[制御構造|制御フロー構文]]に置き換える手法が編み出され、これらは1960年に公開された言語「[[ALGOL|ALGOL60]]」で形式化された。当時のALGOLは[[アルゴリズム]]記述の一つの模範形と見なされたが、それと並行して北欧を中心にした計算機科学者たちはより大局的な観点によるプログラム開発技法の研究を進めていた。 |
||
=== Simulaの開発(1962 - 72) === |
=== Simulaの開発(1962 - 72) === |
||
1962年、ノルウェー計算センターで[[モンテカルロ法]]シミュレーションを運用していた計算機科学者[[クリステン・ニゴール]]は、[[ALGOL|ALGOL60]]を土台にしてProcessと呼ばれる[[コルーチン]]機構を加えたプログラミング言語「[[Simula]]」を |
1962年、ノルウェー計算センターで[[モンテカルロ法]]シミュレーションを運用していた計算機科学者[[クリステン・ニゴール]]は、[[ALGOL|ALGOL60]]を土台にしてProcessと呼ばれる[[コルーチン]]機構を加えたプログラミング言語「[[Simula]]」を制作し、続けてその拡張にも取り組んだ。ニゴールの同僚で、1963年にSimulaを[[メインフレーム|汎用機]][[UNIVAC I|UNIVAC]]系統上で運用できるように実装した計算機科学者[[オルヨハン・ダール]]は、Processにローカル変数構造を共有する手続き(サブルーチン)を加えてパッケージ化する言語仕様を考案し、これは任意の変数と手続きをまとめる[[モジュール|プログラムモジュール]]と同類の機能になった。程なくしてALGOL60コンパイラに準拠していての限界を悟ったニゴールとダールは、1965年からSimulaを一から再設計するように方針転換した。その過程で彼らは、計算機科学者[[アントニー・ホーア]]が考案して1962年のSIMSCRIPT([[FORTRAN]]用のスクリプト)に実装していたRecord Classを参考にしている。Record Classはソースコード水準の抽象記号を、各[[メインフレーム|汎用機]]に準拠した[[マシンコード]]水準の実装符号に落とし込む段階的データ構造のプログラム概念であった。これをモデルにした[[継承 (プログラミング)|継承]]と、その継承構造を利用した仮想手続き(仮想関数)の仕組みも考案され、上述のパッケージ化されたProcess(モジュール)に継承と仮想手続きの両機能を加えたものを「[[クラス (コンピュータ)|クラス]]」と定義し、クラスをメモリに展開したものを「[[オブジェクト (プログラミング)|オブジェクト]]」と定義する言語仕様がまとまり、1967年に「[[Simula|Simula67]]」が初公開された。オブジェクトという用語は、[[MIT]]の計算機科学者[[アイバン・サザランド]]が1963年に開発した[[Sketchpad]]の設計上にあったObjectが先例であった。[[Sketchpad]]は[[CAD]]と[[GUI]]の原点として知られており、後述の[[Smalltalk]]のモチーフの一つにもなっている。Simula67コンパイラはまず汎用機[[UNIVAC I|UNIVAC]]上で運用され、翌年から汎用機[[バロース B5000|バロースB5500]]などでも稼働されて北欧、ドイツ、ソ連の各研究機関へと広まり、1972年には[[IBMメインフレーム|IBM汎用機]][[System/360]]などにも導入されて北米全土にも広まった。その主な用途は離散事象および物理シミュレーションであった。 |
||
=== 構造化プログラミングの提唱(1969 - 75) === |
=== 構造化プログラミングの提唱(1969 - 75) === |
||
1960年代半ばになるとプログラム規模の際限なき拡大に伴なうソフトウェア開発の難航が頻発するようになり、いわゆる[[ソフトウェア危機]]問題が取り沙汰されるようになった。その解決に取り組んだ計算機科学者[[エドガー・ダイクストラ]]は、1969年のNATOソフトウェア工学会議で「[[構造化プログラミング]]」という論文を発表し[[トップダウン設計とボトムアップ設計|トップダウン設計]]、段階的な[[抽象化 (計算機科学)|抽象化]]、階層的な[[モジュール化]]、共同詳細化(抽象データ構造と抽象ステートメントを連携させて具象化する概念)といった技法を提唱した。その論旨は[[正当性 (計算機科学)|プログラム正当性]]検証技術の確立であり、[[数学的証明|数学証明]]に倣った視点でソースコードを適切に分割して抽象化することが勧められていた。しかしこの構造化プログラミングは後に曲解されて、[[制御構造|制御フロー構文]](順次・分岐・反復)を勧める論旨で世間に広まることになり、ダイクストラ本来の論旨であったプログラムモジュールを抽象化して扱おうとする考え方は当時の世間には伝わらなかった。共同詳細化は抽象データ構造を専用ステートメントを通して扱おうとする概念であり、Simula67の手続きを通してクラス内の変数にアクセスする仕組みに似ていた。段階的な抽象化と階層的なモジュール化は、SIMSCRIPTの段階的データ構造とSimura67の継承による階層的クラス構造が先例になっていた。[[エドガー・ダイクストラ|ダイクストラ]]、[[アントニー・ホーア|ホーア]]、[[オルヨハン・ダール|ダール]]の三者は1972年に『構造化プログラミング』と題した共著を上梓しており、その階層的プログラム構造という章の中でダールは、Simulaの設計理念を更に明らかにした。 |
|||
1974年に[[MIT]]の計算機科学者[[バーバラ・リスコフ]]は「[[抽象データ型]]」という |
1974年に[[MIT]]の計算機科学者[[バーバラ・リスコフ]]は「[[抽象データ型]]」という概念を提唱し、上述のモジュールの共同詳細化をその振る舞いによって[[セマンティクス|意味内容]]が決定される抽象データという考え方でより明解に形式化した。1975年に計算機科学者[[ニクラウス・ヴィルト]]は、モジュール機能を主題にした言語[[Modula-2|Modula]]と共に{{仮リンク|モジュラルプログラミング|en|Modular programming}}を提唱した。このパラダイムでは、[[モジュール]]を仕様定義とコード/データ実装に分離しての、前者による抽象化と後者の情報隠蔽が備えられて、これは[[インタフェース (抽象型)|インターフェース]]の実装という概念の先例になっている。また、1970年代後半から[[IBM]]社を中心にした研究者たちが([[エドワード・ヨードン]]など)[[サブルーチン]]モジュールと[[データ構造]]を連携させる[[構造化分析設計技法|構造化]]のパラダイムを発表し、こちらではモジュールの抽象指向は倦厭されて具象的な[[段階的詳細化法|段階的詳細化]]が重んじられた。当時は具象指向の方に軍配が上がり、[[構造化プログラミング|構造化開発]]は1980年代までのソフトウェア開発の主流になっている。このようにいささか奇妙ではあるが、Simulaのクラスとオブジェクトというプログラム概念は、[[モジュール|プログラムモジュール]]の登場からモジュラルや[[構造化分析設計技法|構造化開発]]へといった進化の流れとは関係なく、しかもその前段階において生まれていた。 |
||
=== Smalltalkとオブジェクト指向の誕生(1972 - 81) === |
=== Smalltalkとオブジェクト指向の誕生(1972 - 81) === |
||
Simula発のProcessとクラスの示した可能性は、[[パロアルト研究所]]の計算機科学者[[アラン・ケイ]]による「メッセージング」という考え方のヒントになった。ケイはプログラム内のあらゆる要素をオブジェクトとして扱い、オブジェクトはメッセージの送受信でコミュニケーションするという独特のプログラム理論を提唱した。それには従来の関数呼び出しをセレクタの実行時解釈に置き換えて積極的な[[委譲]]を推進するメッセージ式と、プログラムコードとしても解釈できるデータ列を送信してそれを任意のタイミングで評価(eval)することで新たなデータを導出できるなどのアイディアが盛り込まれていた。これらの遅延結合パラダイムは非同期通信や単方向通信への可能性をも開いており、この発想の背景には[[LISP]]の影響があった。メッセージを駆使するオブジェクトの構築には、Simula発のそれに[[プラトン]]の[[イデア論]]を重ね合わせた[[クラス (コンピュータ)|クラス]]と[[インスタンス]]の仕組みが導入された。オブジェクトとメッセージングの構想に基づいて開発された「[[Smalltalk]]」はプログラミング言語と[[GUI]]フレームワークを併せたものとなり、1972年に[[データゼネラルNova]]上での1000行程度のBASICを使った試作(概念実証)を経て、翌1973年に新開発された[[Alto|ゼロックスAlto]]上で本格稼働された。Smalltalkの設計を説明するためにケイが考案した「[[オブジェクト指向]]」という用語はここで初めて発信された。またケイのメッセージング構想は[[MIT]]の計算機科学者[[カール・ヒューイット]]に能動的な[[プロセス代数]]を意識させて<ref name="アクター">“Our research has concentrated on the development of a rigorous model of computation based on relationship among computational events. The development of this model has been greatly influenced by Seymour Papert's “little people” model of computation, a seminar given by Alan Key at M.I.T. on an early version of Smalltalk, and the work of Church, Fischer, Landin, on formalisms based on the lambda calculus.”[https://dl.acm.org/doi/abs/10.1145/512927.512942|Actor Induction and Meta-evaluation]</ref>、1973年発表の[[アクターモデル]]のヒントにもなっている。しかし委譲の多用とデータ列が常にコード候補としても扱われる処理系は、当時のコンピュータには負荷が大きく実用的な速度を得られないという問題にすぐ直面した。Smalltalk-74(新たに開発された[[BitBLT]]を使った高速描画版Smalltalk-72)からSmalltalk-76の過程で、やむなくメッセージは(多くの場合)関数の動的コールに、メソッドはパターンマッチ処理から単なる関数へ置き換えられるなど構想時の柔軟さが失われるほど最適化された。また、ケイの留保した継承機構<ref name="継承">“I didn't like the way Simula I or Simula 67 did inheritance (though I thought Nygaard and Dahl were just tremendous thinkers and designers). So I decided to leave out inheritance as a built-in feature until I understood it better. ”[http://www.purl.org/stefan_ram/pub/doc_kay_oop_en|Dr. Alan Kay on the Meaning of “Object-Oriented Programming”]</ref>も導入されてオブジェクトは抽象データ型の性格も有するようになった。 |
|||
1980年のSmalltalk-80は、元々はメッセージを重視していたケイを自嘲させるほど同期的で双方向的で手続き的なオブジェクト指向へと変貌していた。それでも動的ディスパッチと[[委譲]]でオブジェクトを連携させるスタイルは画期的であり、1994年に発表される[[デザインパターン (ソフトウェア)|デザインパターン]]の模範にもされている。1981年に当時の著名なマイコン専門誌『[[Byte (magazine)|BYTE]]』が、Smalltalkとその理念であるオブジェクト指向を紹介して世間の注目を集める契機になったが、ケイの思惑に反して技術的関心を集めたのはクラスの仕組みの方であった。オブジェクト指向は知名度を得るのと同時に、Simula発の[[クラス (コンピュータ)|クラス]]([[継承 (プログラミング)|継承]]と動的ディスパッチ)および[[抽象データ型]](データ抽象とデータ隠蔽)にマウントされて解釈されるようになり、それらのコンセプトがケイの構想とは無関係であったことから、オブジェクト指向の定義はケイの手を離れて独り歩きするようになった。 |
|||
Simula発のProcessとクラスの示した可能性は、[[パロアルト研究所]]の計算機科学者[[アラン・ケイ]]によるオブジェクト重視と「メッセージング」という考え方のヒントになった。ケイはプログラム内のあらゆる要素をオブジェクトとして扱い、オブジェクトはメッセージの送受信でコミュニケーションするという独特のプログラム理論を提唱した。それには関数適用風の書式を用いたオブジェクト同士の多種多様な[[委譲|デリゲーション]]と、プログラムコードとしても解釈できるデータ列を送信してそれを評価(''eval'')することで新たなデータを導出できるなどのアイディアが盛り込まれていた。オブジェクトが送るか受け取ったメッセージは任意のタイミングで評価できるので非同期通信や単方向通信をも可能にしていた。この発想の背景には[[LISP]]の影響があった。オブジェクトとメッセージングの構想に基づいて開発された「[[Smalltalk]]」はプログラミング言語と[[GUI]]運用環境を併せたものとなり、1972年に[[データゼネラルNova]]上での1000行程度のBASICを使った試作(概念実証)を経て、翌1973年に新開発された[[Alto|ゼロックスAlto]]上で本格稼働された。Smalltalkの設計を説明するためにケイが考案した「[[オブジェクト指向]]」という用語はここで初めて発信された。またケイのメッセージング構想は[[MIT]]の計算機科学者[[カール・ヒューイット]]に能動的な[[プロセス代数]]を意識させて<ref name="アクター">“Our research has concentrated on the development of a rigorous model of computation based on relationship among computational events. The development of this model has been greatly influenced by Seymour Papert's “little people” model of computation, a seminar given by Alan Key at M.I.T. on an early version of Smalltalk, and the work of Church, Fischer, Landin, on formalisms based on the lambda calculus.”[https://dl.acm.org/doi/abs/10.1145/512927.512942|Actor Induction and Meta-evaluation]</ref>、1973年発表の[[アクターモデル]]のヒントにもなっている。しかしデリゲーションの多用とデータ列が常にコード候補として扱われる処理系は、当時のコンピュータには負荷が大きく実用的な速度を得られないという問題にすぐ直面した。Smalltalk-74(新たに開発された[[BitBLT]]を使った高速描画版Smalltalk-72)からSmalltalk-76の過程で、やむなくメッセージは(多くの場合)関数の動的コールに、メソッドはパターンマッチ処理から単なる関数へ置き換えられるなど構想時の柔軟さが失われるほど最適化された。また、ケイの留保した継承機構<ref name="継承">“I didn't like the way Simula I or Simula 67 did inheritance (though I thought Nygaard and Dahl were just tremendous thinkers and designers). So I decided to leave out inheritance as a built-in feature until I understood it better. ”[http://www.purl.org/stefan_ram/pub/doc_kay_oop_en|Dr. Alan Kay on the Meaning of “Object-Oriented Programming”]</ref>も導入されてオブジェクトは抽象データ型の性格も有するようになった。 |
|||
1980年のSmalltalk-80は、元々はメッセージを重視していたケイを自嘲させるほど同期的で双方向的で手続き的なオブジェクト指向へと変貌していた。それでも動的ディスパッチと[[委譲]]でオブジェクトを連携させるスタイルは画期的であり、1994年に発表される[[デザインパターン (ソフトウェア)|デザインパターン]]の模範にもされている。1981年に当時の著名なマイコン専門誌『[[Byte (magazine)|BYTE]]』がSmalltalkとケイ提唱のオブジェクト指向を紹介して世間の注目を集める契機になったが、ケイの思惑に反して技術的関心を集めたのはクラス機構の方であった。オブジェクト指向は知名度を得るのと同時に、Simula発の[[クラス (コンピュータ)|クラス]]とそれを理論面から形式化した[[抽象データ型]]を中心に解釈されるようになり、それらの考案者がケイの構想とは無関係であったことから、オブジェクト指向の定義はケイの手を離れて独り歩きするようになった。 |
|||
=== C++の開発と普及(1979 - 88) === |
=== C++の開発と普及(1979 - 88) === |
||
[[Simula]]を研究対象にしていた[[ベル研究所|AT&Tベル研究所]]の計算機科学者[[ビャーネ・ストロヴストルップ]]は、1979年からクラス付きC言語の |
[[Simula]]を研究対象にしていた[[ベル研究所|AT&Tベル研究所]]の計算機科学者[[ビャーネ・ストロヴストルップ]]は、1979年からクラス付きC言語の制作に取り組み、1983年に「[[C++]]」を公開した。C++で実装された[[クラス (コンピュータ)|クラス]]は、Simula由来の[[継承 (プログラミング)|継承]]と仮想関数に加えて、段階的な[[レキシカルスコープ]]の概念をクラス構造に応用した[[アクセスコントロール]]を備えていた。C++で確立されたアクセスコントロールは情報隠蔽によるデータ抽象を提示したが、コードスタイル上ほとんどザル化されており、その理由からストロヴストルップ自身もC++は正しくない(''not just'')オブジェクト指向言語であると明言している。1986年にソフトウェア技術者[[バートランド・メイヤー]]が制作した「[[Eiffel]]」の方は、正しいオブジェクト指向を標榜してクラスのデータ抽象を遵守させるコードスタイルが導入されていた。クラスメンバ(フィーチャー)は属性/手続き/関数の三種構成で、手続きで属性を変更して関数で属性を閲覧するという形式に限定されており、これは[[抽象データ型]]に忠実な実装であった。アクセスコントロールはC++のとは異なるクラス指名方式にされ、仮想関数機能は延期手続き/関数として実装された。 |
||
1986年から[[Association for Computing Machinery|ACM]]が[[OOPSLA |
1986年から[[Association for Computing Machinery|ACM]]が[[OOPSLA]]を年度開催するようになり、オブジェクト指向は従来の[[構造化プログラミング|構造化開発]]に代わる技術として明確に意識され始めた。OOPSLAのプログラミング言語セクションでは、[[抽象データ型]]を基礎にした[[クラス (コンピュータ)|クラス]]・パラダイムが主要テーマにされ、それを標準化するための数々のトピックが議題に上げられている。[[モジュール|モジュール分割]]、[[抽象化 (計算機科学)|抽象化]]、[[再利用|再利用性]]、[[継承 (プログラミング)|階層構造]]、複合構成、情報隠蔽、実行時多態、[[動的束縛]]、[[総称型]]、[[永続性]]、[[並行性]]、[[ガベージコレクション|自動メモリ管理]]といったものがそうであり、参画した識者たちによる寄稿、出版、講演を通して世間にも広められた。そうした潮流の中で[[ビャーネ・ストロヴストルップ|ストロヴストルップ]]はデータ抽象の重要性を訴え、[[バーバラ・リスコフ|リスコフ]]は[[上位概念、下位概念、同位概念および同一概念|基底と派生]]に分けたデータ抽象の階層構造の連結関係([[リスコフの置換原則|置換原則]])について提言した。[[契約による設計]]と[[開放/閉鎖原則|開放閉鎖原則]]を提唱する[[バートランド・メイヤー|メイヤー]]が1988年に刊行した『オブジェクト指向ソフトウェア構築』によるEiffel式のクラス理論は高く評価され、Eiffelを現行の模範形とする声も多く上がった。ただしこれは学術寄りの意見でもあったようで、世間のプログラマの間では厳格なEiffelよりも、柔軟で融通の利くC++の人気の方が高まっていた。他方で[[アラン・ケイ]]のメッセージ・メタファに忠実であろうとする動きもあり、1984年に制作された「[[Objective-C]]」はC言語をSmalltalk方向に拡張してメッセージ式を平易化した言語であった。1987年に[[パロアルト研究所]]で誕生した「[[Self]]」は、Smalltalkの[[クラスベース]]設計をより柔軟に平易化した[[プロトタイプベース]]を編み出している。これらのメッセージレシーバーは、静的メソッド選択優先の動的ディスパッチ機構という方式でほぼ形式化された。メッセージレシーバーの仕組みは[[遠隔手続き呼出し]]や[[Object Request Broker|オブジェクト要求ブローカー]]の実装に適していたので、[[分散システム]]とオブジェクト指向の親和性を認識させることになった。 |
||
=== コンポーネントとネットワーク(1989 - 97) === |
=== コンポーネントとネットワーク(1989 - 97) === |
||
ネットワーク技術の発展に連れて、データとメソッドの複合体であるオブジェクトの概念は、[[分散システム]]構築のための基礎要素としての適性を特に見出される事になり、[[IBM|IBM社]]、[[Apple|Apple社]]、[[サン・マイクロシステムズ|サン社]]などが1989年に共同設立した[[Object Management Group|OMG]]は、企業システムネットワーク向け分散オブジェクトプログラミング規格となる[[CORBA]]を1991年に発表した。その前年に[[マイクロソフト|マイクロソフト社]]は[[ウェブアプリケーション]]向けの分散オブジェクトプログラミング技術となる[[OLE]]を発表し、1993年には[[Component Object Model|COM]]と称する[[ソフトウェアコンポーネント]]仕様へと整備した。この[[Component Object Model|COM]]の利用を眼目にしてリリースされた「[[Microsoft Visual C++|Visual C++]]」「[[Visual Basic]]」は[[World Wide Web|ウェブ]]時代の新しいプログラミング様式を普及させる先駆になった。この頃にデータ抽象、データ隠蔽、[[アクセスコントロール]]および[[インタフェース (抽象型)|インターフェース]]によるプログラムの抽象化は、総じて[[カプセル化]]の概念でまとめられるようになった。クラスの[[継承 (プログラミング)|継承]]が最もオブジェクト指向らしい機能と見なされていたのが当時の特徴であった。継承構造を利用した振る舞いサブタイピング及び動的ディスパッチは[[多態性]]という用語に包括された。こうしていわゆるオブジェクト指向の三大要素がやや漠然と確立されている。1996年にサン社がリリースした「[[Java]]」は三大要素が強く意識された[[クラスベース]]であり、その中の分散オブジェクト技術は[[JavaBeans|Beans]]と呼ばれた。類似の技術としてApple社も[[MacOS]]上で[[Objective-C]]などから扱える[[Cocoa]]を開発している。また、1994年から96年にかけて「[[Python]]」「[[Ruby]]」「[[JavaScript]]」といったオブジェクト指向スクリプト言語がリリースされ、従来の[[静的型付け]]に対する[[動的型付け]]と、[[クラスベース]]に対する新しい[[プロトタイプベース]]の認知度を高めている。 |
|||
抽象化を旨とするオブジェクト指向ではそのプログラミング自体の抽象化も積極的に推進されている。80年代後半から立ち上げられた[[オブジェクト指向分析設計|オブジェクト指向分析]](OOA)や[[オブジェクト指向設計]](OOD)の各手法から導き出される[[コンセプトモデル|概念モデル]]を、多角的に[[フローチャート|チャート化]]ないし[[ダイアグラム|ダイアグラム化]]するための数々のオブジェクト指向開発方法論が[[OOPSLA]]界隈の識者たちから発表されるようになり、そこで用いられる[[形式言語]]は{{仮リンク|オブジェクトモデリング言語|en|Object-modeling language}}と呼ばれた。これはプログラミングのためのプロトタイプ(ひな型)として重視され、オブジェクト指向では[[モデリング言語]]と[[プログラミング言語]]が並んでソフトウェア開発の両輪になった。1994年にモデリング言語の代表的な活用例である[[ギャング・オブ・フォー (情報工学)|GOF]][[デザインパターン (ソフトウェア)|デザインパターン]]が初回発表され、後のオブジェクト指向学習では非常に重視されるようになった。1995年にモデリング言語の標準化を企図した[[統一モデリング言語|UML]]がOOPSLAで初回発表され、1997年に[[Object Management Group|OMG]]の標準モデリング言語として採用された。 |
|||
ネットワーク技術の発展に連れて、データとメソッドの複合体であるオブジェクトの概念は、[[分散システム]]構築のための基礎要素としての適性を特に見出される事になり、[[IBM|IBM社]]、[[Apple]]、[[サン・マイクロシステムズ|サン社]]などが1989年に共同設立した[[Object Management Group|OMG]]は、企業システムネットワーク向け分散オブジェクトプログラミングの標準規格となる[[CORBA]]を1991年に公開した。その前年に[[マイクロソフト|マイクロソフト社]]は[[ウェブアプリケーション]]向けの分散オブジェクト技術となる[[OLE]]を発表し、1993年には[[Component Object Model|COM]]と称する[[ソフトウェアコンポーネント]]仕様へと整備した。この[[Component Object Model|COM]]の利用を眼目にしてリリースされた「[[Microsoft Visual C++|Visual C++]]」「[[Visual Basic]]」は[[World Wide Web|ウェブ]]時代の新しいプログラミング様式を普及させる先駆になった。この頃に[[抽象データ型]]のメソッドを通したデータ抽象、データ隠蔽、[[アクセスコントロール]]および分散オブジェクトの[[インタフェース (情報技術)|インターフェース]]機構によるプログラムの抽象化といった概念は、[[カプセル化]]という用語にまとめられるようになった。クラスの[[継承 (プログラミング)|継承]]が最もオブジェクト指向らしい機能と見なされていたのが当時の特徴であった。継承構造を利用した振る舞いサプタイピングは[[多態性]]という用語に包括された。こうしていわゆるオブジェクト指向の三大要素がやや漠然と確立されている。1996年にサン社がリリースした「[[Java]]」は三大要素が強く意識された[[クラスベース]]であり、その中の分散オブジェクト技術は[[JavaBeans|Beans]]と呼ばれた。類似の技術としてアップル社も[[MacOS]]上で[[Objective-C]]などから扱える[[Cocoa]]を開発している。また、1994年から96年にかけて「[[Python]]」「[[Ruby]]」「[[JavaScript]]」といったオブジェクト指向スクリプト言語がリリースされ、従来の[[クラスベース]]に対する新しい[[プロトタイプベース]]を定着させている{{疑問点|date=2021年5月|title=これだと Python と Ruby もプロトタイプベースであるかのようにも見える。}}。1994年の[[ギャング・オブ・フォー (情報工学)|GOF]][[デザインパターン (ソフトウェア)|デザインパターン]]の発表と、1997年に[[Object Management Group|OMG]]が標準[[モデリング言語]]として採用した[[統一モデリング言語|UML]]は、オブジェクト指向プログラミングの標準化を促進させた。 |
|||
== |
== オブジェクト指向言語一覧 == |
||
ここではクラスベースの記述は省略している。 |
|||
<div style="{{column-count|2}}"> |
|||
* [[Simula|Simula 67]] 1967年 |
|||
* [[ |
* [[Simula|Simula67]] 1967年 静的 |
||
* [[Smalltalk]] 1972年 動的・メッセージ式・純粋系 |
|||
* [[C++]] 1983年 |
|||
* [[ |
* [[C++]] 1983年 静的 |
||
* [[Objective-C]] 1984年 静的と動的・メッセージ式 |
|||
* [[Object Pascal]] 1986年 |
|||
* [[ |
* [[Object Pascal]] 1986年 静的 |
||
* [[ |
* [[Eiffel]] 1986年 静的・純粋系 |
||
* [[Self]] 1987年 動的・プロトタイプベース・メッセージ式・純粋系 |
|||
* [[Common Lisp]]([[CLOS]]) 1988年(ANSI規格化は1994年) |
|||
* [[Modula-3]] 1988年 静的 |
|||
* [[Common Lisp]]([[CLOS]]) 1988年(ANSI規格化は1994年) 動的・多重ディスパッチ・[[The Art of the Metaobject Protocol|メタオブジェクト]] |
|||
<!-- :[[クラスベース]]のオブジェクト指向。メソッド記述の関数呼び出し形式への統合、[[多重ディスパッチ]]、クラスの動的な再定義等を特徴とする。--> |
<!-- :[[クラスベース]]のオブジェクト指向。メソッド記述の関数呼び出し形式への統合、[[多重ディスパッチ]]、クラスの動的な再定義等を特徴とする。--> |
||
* |
*[[Oberon-2]] 1991年 静的 |
||
*[[Dylan]] 1992年 動的・多重ディスパッチ |
|||
* [[Java]] 1995年 |
|||
*[[Lua]] 1993年 動的・プロトタイプベース |
|||
* [[Delphi]] 1995年 |
|||
* [[ |
* [[Python]] 1994年(ver1.0) 動的 |
||
* [[ |
* [[Delphi]] 1995年 静的 |
||
*[[Ada]] 1995年からOOP化 静的 |
|||
* [[C Sharp|C#]] 2000年 |
|||
* |
*[[Perl]] 1995年からOOP化 動的 |
||
* [[ |
* [[Ruby]] 1995年 動的・純粋系 |
||
* |
*[[Java]] 1996年(ver1.0) 静的 |
||
* [[JavaScript]] 1996年(first released) 動的・プロトタイプベース |
|||
* [[Swift (プログラミング言語)|Swift]] 2014年 |
|||
*[[OCaml]] 1996年 静的・関数型 |
|||
*[[Squeak]] 1996年 動的・メッセージ式 |
|||
*[[ECMAScript]] 1997年 動的・プロトタイプベース |
|||
*[[C Sharp|C#]] 2000年 静的と動的 |
|||
*[[Visual Basic .NET|Visual Basic.NET]] 2001年 静的 |
|||
*[[D言語]] 2001年 静的 |
|||
*[[Io (プログラミング言語)|Io]] 2002年 動的・プロトタイプベース・メッセージ式・純粋系 |
|||
*[[COBOL]] 2002年からOOP化 静的 |
|||
*[[FORTRAN]] 2003年からOOP化 静的 |
|||
*[[R言語]] 2003年からOOP化 動的・関数型・多重ディスパッチ |
|||
* [[Scala]] 2003年 静的・関数型 |
|||
*[[Groovy]] 2003年 動的 |
|||
*[[PHP (プログラミング言語)|PHP]] 2004年からOOP化 静的と動的 |
|||
*[[F Sharp|F#]] 2005年 静的・関数型 |
|||
*[[MATLAB]] 2008年からOOP化 動的・関数型 |
|||
*[[Go (プログラミング言語)|Go]] 2009年 静的 |
|||
*[[Rust (プログラミング言語)|Rust]] 2010年 静的・関数型 |
|||
* [[Kotlin]] 2011年 静的 |
|||
*[[Ceylon]] 2011年 静的 |
|||
*[[Dart]] 2011年 静的・関数型 |
|||
*[[Elixir (プログラミング言語)|Elixir]] 2011年 動的・関数型 |
|||
*[[Julia (プログラミング言語)|Julia]] 2012年 動的・関数型・多重ディスパッチ |
|||
* [[TypeScript]] 2012年 静的と動的・関数型 |
|||
*[[Swift (プログラミング言語)|Swift]] 2014年 静的・プロトコル指向 |
|||
*[[Raku]] 2015年 静的と動的 |
|||
</div> |
|||
== |
== 用語一覧 == |
||
ここでは日本語ページ化されている項目のみ列挙している。 |
|||
{{Wikibooks|オブジェクト指向|オブジェクト指向}} |
|||
<div style="{{column-count|3}}"> |
|||
* [[メッセージ (コンピュータ)]] |
|||
* [[クラスベース]] |
|||
* [[メソッド (計算機科学)]] |
|||
*[[プロトタイプベース]] |
|||
* [[フィールド (計算機科学)]] |
|||
*[[メッセージ (コンピュータ)|メッセージ]] |
|||
*[[クラス (コンピュータ)|クラス]] |
|||
*[[インスタンス]] |
|||
*[[フィールド (計算機科学)|フィールド]] |
|||
*[[プロパティ (プログラミング)|プロパティ]] |
|||
*[[メソッド (計算機科学)|メソッド]] |
|||
* [[インスタンス変数]] |
* [[インスタンス変数]] |
||
* [[クラス変数]] |
* [[クラス変数]] |
||
* |
*[[コンストラクタ]] |
||
* |
*[[デストラクタ]] |
||
* |
*[[カプセル化]] |
||
* [[プロトタイプベース]] |
|||
* [[カプセル化]] |
|||
* [[継承 (プログラミング)|継承]] |
* [[継承 (プログラミング)|継承]] |
||
*[[ポリモーフィズム|多態性]] |
|||
* [[Is-a|Is-a関係]] |
|||
*[[This (プログラミング)|This]] |
|||
* [[Has-a|Has-a関係]] |
|||
* [[ |
* [[Is-a]] |
||
* [[ |
* [[Has-a]] |
||
* |
*[[派生型|サブタイピング]] |
||
*[[ダックタイピング]] |
|||
* [[委譲]] |
|||
* |
*[[ミックスイン]] |
||
*[[スーパークラス (計算機科学)|スーパークラス]] |
|||
* [[プログラミング言語]] |
|||
*[[サブクラス (計算機科学)|サブクラス]] |
|||
* [[デザインパターン (ソフトウェア)|GOFデザインパターン]] |
|||
*[[抽象クラス]] |
|||
* [[オブジェクト指向モデリング]] |
|||
*[[メタクラス]] |
|||
* [[オブジェクト指向分析設計]] |
|||
* |
*[[オーバーライド]] |
||
*[[仮想関数テーブル]] |
|||
* [[オブジェクトデータベース]] |
|||
*[[菱形継承問題]] |
|||
* [[トップダウン設計とボトムアップ設計]] |
|||
*[[仮想継承]] |
|||
* [[オブジェクト関係マッピング]] |
|||
*[[インタフェース (抽象型)|インターフェース]] |
|||
*[[トレイト]] |
|||
* [[抽象型]] |
|||
*[[オブジェクト型]] |
|||
*[[型クラス]] |
|||
*[[委譲]] |
|||
*[[多重ディスパッチ]] |
|||
*[[ダブルディスパッチ]] |
|||
*[[多重定義|オーバーロード]] |
|||
*[[アノテーション]] |
|||
*[[コンテナ (データ型)|コンテナ]] |
|||
*[[ボックス化]] |
|||
*[[ジェネリクス]] |
|||
*[[共変性と反変性 (計算機科学)|共変性と反変性]] |
|||
*[[モジュール]] |
|||
*[[ソフトウェアコンポーネント]] |
|||
*[[リフレクション (計算機科学)|リフレクション]] |
|||
*[[モンキーパッチ]] |
|||
*[[メッセージ転送]] |
|||
*[[型システム]] |
|||
*[[オブジェクト指向分析設計]] |
|||
*[[オブジェクト指向モデリング]] |
|||
*[[統一モデリング言語|UML]] |
|||
*[[契約による設計]] |
|||
*[[デザインパターン (ソフトウェア)|GOFデザインパターン]] |
|||
*[[GRASP]] |
|||
* [[SOLID]] |
|||
</div> |
|||
== 脚注 == |
== 脚注 == |
2021年10月9日 (土) 11:17時点における版
プログラミング・パラダイム |
---|
オブジェクト指向プログラミング(オブジェクトしこうプログラミング、英: object-oriented programming、略語:OOP)とはプログラミング技法の一つであり、任意の関連性を持つデータ(変数またはプロパティ)とコード(関数またはメソッド)をひとつにまとめてオブジェクトとし、それぞれ異なる性質と役割を持たせたオブジェクトの様々な定義と、それらオブジェクトを相互作用させる様々なプロセスの設定を通して、コンピュータプログラムを構築することを主眼にしている。
オブジェクト指向という言葉自体は、計算機科学者アラン・ケイによって作り出されている。Simula言語などにインスパイアされたケイが1967年頃に口にしたと伝えられるこの造語は[1]、彼が1972年から開発を始めた言語Smalltalkの設計を説明する過程で明確な用語として発信され、1981年頃から知名度を得るようになった。80年代半ばになるとオブジェクト指向の解釈は、元々のアラン・ケイによるSmalltalkの様式と、1983年に計算機科学者ビャーネ・ストロヴストルップが公開したC++の様式に二分された。前者ではメッセージングという概念が基礎にされ、後者ではSimula67由来の諸機能を加えた抽象データ型のスーパーセットが基礎にされていた。現在では、C++とそのモデルになったSimula67の様式がオブジェクト指向の標準になっている。
特徴
この記事には独自研究が含まれているおそれがあります。 |
OOPではクラスベースと呼ばれるスタイルが標準にされている。他にプロトタイプベースと呼ばれる後発のスタイルもあるが、こちらは少数派である。クラスベースのOOP言語は、Smalltalk様式とC++様式で二分されており、C++の方がずっと多数派である。C++様式は、静的型付けと動的型付けの分類で大別されている。それらの主な特徴を箇条書きするとこうなる。
- クラスベース - クラスのインスタンス化でオブジェクトを構築する。
- プロトタイプベース - オブジェクトのクローンでオブジェクトを構築する。クラスとインスタンスの相対性をオブジェクトから撤廃して、プロトタイプでオブジェクトを体系化している。動的型付け中心。動的バインディングによる多態性。
本稿では最も標準的なクラスベースのC++様式の静的型付けを基準にして説明する。
クラスとインスタンス
OOPの要点であるクラスは、データ構造とそれを扱うための操作・振る舞いをひとまとめにした一種のプログラムモジュール機能として定義されており、その実装はSimula67由来の継承と動的ディスパッチを加えた抽象データ型のスーパーセットにされていることが多い。クラスのデータ構造はレコード型や構造体に似た書式で定義されることが多く、データ構造の要素は言語ごとにフィールド、プロパティ、属性、メンバ変数などと呼ばれている。クラスに定義される操作・振る舞いはメソッドやメンバ関数などと呼ばれる。メソッドとは、特定の対象に所属させることを前提にしてThis参照を多相シンボルにしたディスパッチコードとしての関数/手続きを意味している。
OOP言語のクラスと、そうでない言語でのモジュールの違いを知ることは、OOPを理解する上でも重要になる。どちらも手続きとデータの複合体であるが、クラスの第一の特徴はそれに継承が備えられていることであり、次にThis参照の機構と、継承構造上の内包的な動的ディスパッチである。その次になる情報隠蔽と、定義と実装に分離しての抽象化の機構は、OOP以前のモジュラルプログラミングからのものである。振る舞い抽象を担っているインターフェースの概念もそちらが先例であったが、データ抽象を担っているカプセル化(情報隠蔽とThis参照の融合)の採用はOOPが最初である。継承構造上の内包的な動的ディスパッチによるサブタイピング(=ポリモーフィズム)はOOP発祥であるが、それを純粋化すると前述のインターフェースになる。OOP以前の構造化開発のモジュールには情報隠蔽はあるが、継承はなく、This参照・カプセル化・サブタイピング・インターフェースといった抽象化機構も持たない。なお、振る舞いサブタイピングは多重ディスパッチ中心OOPでは軽視されており、カプセル化はプロトタイプベースOOPでは軽視されている。従ってOOPを特徴付けている本来の要素とは即ち継承であり、その真偽はともかくとして、継承はプログラムの再利用性と保守性を高めるためのメカニズムと定義されている。
C++様式のクラスベースはオブジェクトを、型理論に沿った型(type)と型付け値(term)に分離している。その型の一種であるユーザー定義型(user defined type)がクラスにされており、その型付け値がインスタンスになっている。ユーザー定義型とは構造体と同じものであり、OOPでは手続き(のポインタ)も構造体メンバにされている。ユーザー定義型はオブジェクト型とも呼ばれるが、このオブジェクト型と後節の「オブジェクト」の意味上の繋がりはない。クラスはインスタンスのひな型であり、インスタンスはクラスを実体化したものである。実体化=インスタンス化とは対象クラスの全変数内容を決定し、ヒープ領域の基底アドレス(This)を定めた上でそこにメモリ展開するという作業を指している。コンパイル時にその構成が確定されるユーザー定義型は静的型付けであり、実行時にもその構成を変更できるユーザー定義型は動的型付けである。
オブジェクトとは
クラスベース言語での「オブジェクト」は、一般的にはインスタンスを指す用語として説明されているが、このような重複語になった背景にはOOPの原点であるSmalltalkからの事情がある。Smalltalkは、全てのプログラム要素はオブジェクトであり、全てのオブジェクトはクラスのインスタンス化であり、クラスもまたオブジェクトであると定義していた。そこではクラスもまたメタクラス(型)のインスタンス化(型付け値)になり、そのメタクラスもまた他のメタクラスのインスタンス化になっていたので、クラスとインスタンスは即ちオブジェクトの性質(相対性)を指すための言葉になっていた。これがC++様式では簡素化されて、クラスとインスタンスは型と型付け値の役割に固定され、メタクラスはクラス構成の動的変更機能に固定されたので、上述のインスタンス化の相互再帰が失われたオブジェクトは、インスタンスと同じ意味の言葉として残されることになった。またSmalltalkは、オブジェクトは他のオブジェクトと相互作用(interaction)するとも定義しており、この相互作用とは各種演算と同義である。Smalltalkのクラスは型付け値でもあるのでオブジェクトであるが、C++様式のクラスはただの型なので演算対象に出来ないことからオブジェクトではなくなっている。
Smalltalk方言のSelfを原点とするプロトタイプベースは、オブジェクトからクラスとインスタンスの相対性を無くしたスタイルである。数値・文字列・配列・関数・シンボル・構造体(オブジェクト型)といった基本的な型は備えられているが、これはオブジェクト種類の区別に特化されたものなので、型理論に沿ったクラスベースのそれとは厳密には異なっている。クラス性質を除去したオブジェクトは事実上のインスタンスに一元化されており、その全てが相互作用(interaction)する。オブジェクトの表現はスロット(シンボルとコンテンツのペアデータ)の可変長配列でなされており、オブジェクトの識別は専らダックタイピングによってなされる。クラス概念が無いのでサブクラス化とインスタンス化は成立せず、代わりにクローン(複製)によってオブジェクトの継承がなされており、クローンはインスタンス化の代替になる。複製元オブジェクトは、複製先オブジェクトのプロトタイプと呼ばれる。
オブジェクト指向の三大要素
クラスベースOOPの中心であるクラスの実装様式を規定している以下の三項目は、日本では三大要素または三大原則などと呼ばれているが、各要素の軽視重視と導入方法は言語別に様々である。
データ構造とそれを扱うためのメソッド群を情報隠蔽の概念と合わせてモジュール化(パッケージ化)するという手法がカプセル化と呼ばれる。カプセル化されたメソッドは、This値が暗黙の先頭引数として常に渡されるように実装される。This値とはクラスのデータ構造をメモリ展開するためのヒープ領域の基底アドレスを指しており、インスタンス化時に確定されたThis値によってメソッドは専用のデータ構造にアクセスできる。専用メソッドを通してのデータ構造の閲覧と変更は、抽象データ型の考え方に沿ったデータ構造の抽象化を意味することになり、これはデータ抽象と呼ばれる。データ閲覧用メソッドはゲッター/アクセッサと呼ばれ、データ変更用メソッドはセッター/ミューテイタと呼ばれる。
情報隠蔽とはそのクラスのデータ構造の各要素および各メソッドを必要に応じて内部隠蔽するという概念である。内部隠蔽されたデータ要素とメソッドはそのクラス外部からのアクセスが禁止される。抽象データ型本来の形式ではデータ構造のみが隠蔽対象になるので、これはデータ隠蔽とも呼ばれる。隠蔽指定外のデータ要素とメソッドは外部公開されて、そのクラス外部からもアクセス可能になる。外部公開の範囲を指定する機能はアクセスコントロールと呼ばれており、これが内部隠蔽の仕組みを担っている。クラスのレキシカルスコープを基準にした段階的なアクセス許可範囲は可視性と呼ばれる。可視性は無制限・任意クラスグループ限定・派生クラスグループ限定・自クラス限定(内部隠蔽)の四段階がUMLでは標準にされている。
既存クラスのデータ/メソッド構成に、任意のデータ/メソッド構成を付け足して、既存構成+新規構成の新しいクラスを定義するという手法が継承と呼ばれる。また、各クラスの共通構成パートを括りだして特有構成パートと分離することでオブジェクトを分類体系化し、同時にその共通構成パートの記号化によってソースコード内の重複記述を削減する機能とも解釈される。これは差分プログラミング目的の継承であり、1990年代までは多用された。2000年代になるとSOLID原則の重視に伴なって、既存構成に抽象メソッドを置いて新規構成にその実体メソッドを置くというメソッドオーバーライドを用いるための振る舞いサブタイピング目的の継承が要点にされるようになっている。データ/メソッドの新規構成ではなく、メソッドの実装内容を付け足していくための継承である。そこでは特にデータ構造を付け足しての階層的なデータの分散配置は倦厭されている。
既存クラスはスーパークラス・親クラス・基底クラスなどと呼ばれ、新しいクラスはサブクラス・子クラス・派生クラスなどと呼ばれる。親と子は差分プログラミング重視で用いられ、基底と派生はサブタイピング重視で用いられる。継承できるクラスが一つに限られている単一継承を採用している言語と、継承できるクラスの数に制限がない多重継承を採用している言語がある。抽象メソッドを持つクラスは抽象クラスと呼ばれる。基底クラス側で返り値の型と引数リストのみが定義されて実行コードブロックが未定義のままの抽象メソッドは、その派生クラス側の実体メソッドでオーバーライドされる。オーバーライドは遅延結合による多相な複雑系アルゴリズムを表現するオープン再帰(open recursion)のメカニズムにもなっている。
抽象メソッドのみで構成される純粋抽象クラスはインターフェースと呼ばれ、その継承は実装(implementaion)と呼ばれる。実装継承という俗称もある。実装はサブタイピングによるIs-a関係を完全順守させるためのメカニズムであり、継承とは別個の概念として扱われている。モジュラルプログラミングが先例になる実装は、継承から派生(subtyping)への先祖返りと言えるものである。
異なる種類のクラスに共通の操作インターフェースを持たせてオブジェクトの振る舞いを抽象化するという手法がポリモーフィズム(多態性)と呼ばれる。OOPで語られる多態性はもっぱら継承構造を利用したサブタイプ多相を対象にしているが、アドホック多相もサポート的に用いられており、パラメトリック多相はコンポジションで用いられている。そのサブタイプ多相の設計としてはコンパイル時に確定されたメソッド名から呼び出されるプロセス内容が実行時に決定されるという仕組みを指しており、一つのメソッド名からその実行時状態に合わせた個別のメソッド処理が呼び出されるようにするという演繹的意味と、各クラスの同種機能メソッドを一つの共通メソッドにまとめて実行時状態に合わせたメソッド処理が呼び出されるようにするという帰納的意味がある。
その実装としてはメソッドオーバーライド機能を活用した仮想関数と、実行時パターンマッチング機能を活用した総称関数の二つが挙げられる。仮想関数はスーパークラスの抽象メソッドの呼び出しを、それをオーバーライドしたサブクラスの実体メソッドの呼び出しにつなげるという動的ディスパッチ機能である。総称関数はオブジェクトの実行時パターンマッチングを使用する独立関数であり、その引数にされた各オブジェクトの型(=クラス)の組み合わせに従って実行コードブロックを選択決定するという多重ディスパッチ機能である。
コンポジションとデリゲーションとジェネリクス
コンポジション (合成)とデリゲーション(委譲)は、継承を帰納的に分解した仕組みと言えるものであり、または合成と委譲による連携を演繹的に最適化した仕組みが継承であるとも言える。OOPにおける合成と委譲は、OOP原点のSmalltalkがSimula67発の継承機能をオブジェクトの再帰構成という観点から再解釈したものがデザイン上のルーツになっている。継承は例外も多いがIs-aの委譲であるのに対して、合成はHas-aの委譲と読み替えることができる。
合成とは、特定処理の委譲先になる部品クラスの1個以上を持たせたクラス構造であり、そのクラスがとある処理を要求されてそれに対応できるデータ/メソッドを持っていない場合は、それに対応できる部品クラスを選択して処理を委譲するという仕組みである。その要求判別と選択過程を自動サーチ化したものが継承であり、部品クラスを基底クラスに置き換えて暗黙の委譲先にしたものである。しかしその自動サーチは、クラス階層に分散配置されているデータ/メソッドのどれが実際にアクセスされるのかという把握を困難にしたので、ここで差分プログラミング用途の継承の欠点が取り沙汰されるようになり、2000年代になるとクラスの機能拡張と分類体系化では、継承と合成の使い分けが重視されるようになった。
ジェネリクス(総称化)は、オブジェクトのデータ構造を汎用化して、それに汎用アルゴリズムの数々を適用できるようにした技術である。様々なデータ要素を内包するコンポジション(合成)オブジェクトはコンテナ(List/Set/Mapなど)と呼ばれ、その要素の型を型変数化することで汎用的なデータ構造を表現し、その要素の型はコンテナのインスタンス化時に与えられる型パラメータによって決定される。ジェネリクスはデータ構造とアルゴリズムの柔軟なコンポーネント化を促進させた。また、圏論の圏に見立てられたコンテナおよび対象に見立てられたデータ要素の入れ子構造と、射に見立てられたサブタイピングの融合は、共変性と反変性の手法に発展した。
動的ディスパッチとメッセージパッシング
動的ディスパッチは、OOPのポリモーフィズムの基礎的仕組みであり、例えば継承構造上でのThisによるシングルディスパッチを最適化した機能が仮想関数になっている。動的ディスパッチは、コンパイル時に確定されたメソッド名から呼び出されるメソッド内容(実行コードブロック)が実行時に決定される仕組み全般を指している総称用語である。メソッドに与えられた各引数の型の組み合わせに従って、実行コードブロックが選択分岐される仕組みのシングルディスパッチと多重ディスパッチを包括している。先頭引数の型パターンマッチング固定で実行コードブロックが選択決定(ディスパッチ)されるのはシングルになり、そうでないならば多重になる。
メッセージパッシングでは前述の引数の型だけでなく、メソッド名も実行時に解釈される要素にされており、そこでただのシンボルとして扱われるメソッド名はセレクタと呼ばれている。セレクタはメッセージ式を基本文とするSmalltalk様式のOOPで用いられる。object selector: param
ような書式でオブジェクトの共通窓口となるメッセージレシーバーに、セレクタと引数値で構成されたメッセージが送られる。また、object.call(method_name, param)
のような書式でオブジェクトの共通窓口関数をコールするのもメッセージパッシングと呼ばれる。これは遠隔手続き呼出しやオブジェクト要求ブローカーで用いられており、分散オブジェクトの標準的なインターフェース機構になっている。メソッド名(関数名)も実行時に解釈されるという特徴を指してメッセージパッシングと呼ぶ。メッセージレシーバー内では、渡されたセレクタの文字列照合の条件分岐によって実行コードブロックが選択されることが多く、OOP原点のSmalltalkは、引数とThisが与えられるその実行コードブロックをメソッドと呼んでいた。
インターフェース
インターフェースは、カプセル化を更に突き詰めた概念であり、継承のサブタイピング用法を更に突き詰めた概念である実装(implementation)を体現している仕組みである。インターフェースは、オブジェクトの情報隠蔽とデータ抽象とメソッド抽象を合わせて表現する。クラスから見たインターフェースは、自身のサービスのモデリングであり、この作法は実現(realization)と呼ばれる。OOPのインターフェースは、基本的には抽象メソッドのみで構成される抽象型と定義されている。ソフトウェアコンポーネント間の相互通信媒体として用いられることが多く、SOLID原則の重視に伴ないクラス間の結合にも多用される。
各言語での導入様式としては、抽象メソッドのみで構成される純粋抽象クラスが基本形にされており、インスタンス化はできない継承専用クラスになる。多重継承が前提にされている。インターフェースの抽象メソッドのメソッド内容(実行コードブロック)は、それを実装したクラスの同名メソッドで記述されることになる。インターフェースの各抽象メソッドはセッター・ゲッター・プロセスなどとして動作し、その実装内容は利用者側からは隠されて実行時のその都度に決定されることになる。
多重ディスパッチとミックスインとトレイト
多重ディスパッチは、オブジェクトの振る舞いサブタイピングを、オブジェクトの実行時パターンマッチングに置き換えたポリモーフィズム・アプローチである。サブタイピングが同名アルゴリズムを個々の派生クラスに分散記述するのに対して、多重ディスパッチは個々の派生クラスの同名アルゴリズムを独立関数内で一括記述する。その独立関数が多重ディスパッチではメソッドと呼ばれるが、正確にはディスパッチ先になる個々の実行コードブロックがメソッドであり、その多重定義で総称メソッド(総称関数)と呼ばれる。総称メソッドは1個以上のオブジェクトを引数にし、その各引数オブジェクトの型(クラス)の組み合わせに従って、実行コードブロックを選択決定(ディスパッチ)する。その型パターンマッチングでは、クラスに付加されたアノテーション識別(マーカーインターフェース)の方が重視されることもあり、これがアドホック多相とサブタイプ多相の双方で継承を解釈できるミックスインの原点になっている。
ミックスイン(Mix-in)は、上位クラスからの機能取得と下位クラスへの機能注入を主体にした継承アプローチであり、そこでは上位クラスと下位クラスのIs-a関係が軽視されていることも特徴である。これはサブタイピング特化の実装継承とは反対に、差分プログラミング用法に特化させた継承と言えるものである。ただしこの差分プログラミングはあくまでメソッド限定である。こちらも多重継承が前提であり、インスタンス化はできない継承専用クラス(Mix-inクラス)を扱っている。Mix-inクラスの代表的な実装形態はトレイトである。トレイトはメソッドの集合体であり、そのメソッド集合を一つの機能にした分類体系化が可能であり、そのメソッド間で状態を共有するためのデータメンバも所有できることがある。トレイトは機能注入用のメソッド実体と、オーバーライド用のメソッド定義の双方を持てるが、インターフェースとは異なり前者のメソッド実体の方が中心である。Mix-inの設計は各言語で多様化しており、Mix-inクラスは各言語によってトレイト・モジュール・ロール・プロトコル・型クラスなどの様々な形態で導入されている。クラスをそのままMix-in化できる言語もあり、関数オブジェクトをMix-in化できる言語もあり、メソッド実装も可能にしたインターフェースをMix-in化できる言語もある。トレイトのMix-inは構造的型付けで扱われることが多く、従来のクラスの継承とインターフェースの実装が記名的型付けで扱われることと対比されている。
歴史
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年に開発したSketchpadの設計上にあったObjectが先例であった。SketchpadはCADとGUIの原点として知られており、後述のSmalltalkのモチーフの一つにもなっている。Simula67コンパイラはまず汎用機UNIVAC上で運用され、翌年から汎用機バロースB5500などでも稼働されて北欧、ドイツ、ソ連の各研究機関へと広まり、1972年にはIBM汎用機System/360などにも導入されて北米全土にも広まった。その主な用途は離散事象および物理シミュレーションであった。
構造化プログラミングの提唱(1969 - 75)
1960年代半ばになるとプログラム規模の際限なき拡大に伴なうソフトウェア開発の難航が頻発するようになり、いわゆるソフトウェア危機問題が取り沙汰されるようになった。その解決に取り組んだ計算機科学者エドガー・ダイクストラは、1969年のNATOソフトウェア工学会議で「構造化プログラミング」という論文を発表しトップダウン設計、段階的な抽象化、階層的なモジュール化、共同詳細化(抽象データ構造と抽象ステートメントを連携させて具象化する概念)といった技法を提唱した。その論旨はプログラム正当性検証技術の確立であり、数学証明に倣った視点でソースコードを適切に分割して抽象化することが勧められていた。しかしこの構造化プログラミングは後に曲解されて、制御フロー構文(順次・分岐・反復)を勧める論旨で世間に広まることになり、ダイクストラ本来の論旨であったプログラムモジュールを抽象化して扱おうとする考え方は当時の世間には伝わらなかった。共同詳細化は抽象データ構造を専用ステートメントを通して扱おうとする概念であり、Simula67の手続きを通してクラス内の変数にアクセスする仕組みに似ていた。段階的な抽象化と階層的なモジュール化は、SIMSCRIPTの段階的データ構造とSimura67の継承による階層的クラス構造が先例になっていた。ダイクストラ、ホーア、ダールの三者は1972年に『構造化プログラミング』と題した共著を上梓しており、その階層的プログラム構造という章の中でダールは、Simulaの設計理念を更に明らかにした。
1974年にMITの計算機科学者バーバラ・リスコフは「抽象データ型」という概念を提唱し、上述のモジュールの共同詳細化をその振る舞いによって意味内容が決定される抽象データという考え方でより明解に形式化した。1975年に計算機科学者ニクラウス・ヴィルトは、モジュール機能を主題にした言語Modulaと共にモジュラルプログラミングを提唱した。このパラダイムでは、モジュールを仕様定義とコード/データ実装に分離しての、前者による抽象化と後者の情報隠蔽が備えられて、これはインターフェースの実装という概念の先例になっている。また、1970年代後半からIBM社を中心にした研究者たちが(エドワード・ヨードンなど)サブルーチンモジュールとデータ構造を連携させる構造化のパラダイムを発表し、こちらではモジュールの抽象指向は倦厭されて具象的な段階的詳細化が重んじられた。当時は具象指向の方に軍配が上がり、構造化開発は1980年代までのソフトウェア開発の主流になっている。このようにいささか奇妙ではあるが、Simulaのクラスとオブジェクトというプログラム概念は、プログラムモジュールの登場からモジュラルや構造化開発へといった進化の流れとは関係なく、しかもその前段階において生まれていた。
Smalltalkとオブジェクト指向の誕生(1972 - 81)
Simula発のProcessとクラスの示した可能性は、パロアルト研究所の計算機科学者アラン・ケイによる「メッセージング」という考え方のヒントになった。ケイはプログラム内のあらゆる要素をオブジェクトとして扱い、オブジェクトはメッセージの送受信でコミュニケーションするという独特のプログラム理論を提唱した。それには従来の関数呼び出しをセレクタの実行時解釈に置き換えて積極的な委譲を推進するメッセージ式と、プログラムコードとしても解釈できるデータ列を送信してそれを任意のタイミングで評価(eval)することで新たなデータを導出できるなどのアイディアが盛り込まれていた。これらの遅延結合パラダイムは非同期通信や単方向通信への可能性をも開いており、この発想の背景にはLISPの影響があった。メッセージを駆使するオブジェクトの構築には、Simula発のそれにプラトンのイデア論を重ね合わせたクラスとインスタンスの仕組みが導入された。オブジェクトとメッセージングの構想に基づいて開発された「Smalltalk」はプログラミング言語とGUIフレームワークを併せたものとなり、1972年にデータゼネラルNova上での1000行程度のBASICを使った試作(概念実証)を経て、翌1973年に新開発されたゼロックスAlto上で本格稼働された。Smalltalkの設計を説明するためにケイが考案した「オブジェクト指向」という用語はここで初めて発信された。またケイのメッセージング構想はMITの計算機科学者カール・ヒューイットに能動的なプロセス代数を意識させて[2]、1973年発表のアクターモデルのヒントにもなっている。しかし委譲の多用とデータ列が常にコード候補としても扱われる処理系は、当時のコンピュータには負荷が大きく実用的な速度を得られないという問題にすぐ直面した。Smalltalk-74(新たに開発されたBitBLTを使った高速描画版Smalltalk-72)からSmalltalk-76の過程で、やむなくメッセージは(多くの場合)関数の動的コールに、メソッドはパターンマッチ処理から単なる関数へ置き換えられるなど構想時の柔軟さが失われるほど最適化された。また、ケイの留保した継承機構[3]も導入されてオブジェクトは抽象データ型の性格も有するようになった。
1980年のSmalltalk-80は、元々はメッセージを重視していたケイを自嘲させるほど同期的で双方向的で手続き的なオブジェクト指向へと変貌していた。それでも動的ディスパッチと委譲でオブジェクトを連携させるスタイルは画期的であり、1994年に発表されるデザインパターンの模範にもされている。1981年に当時の著名なマイコン専門誌『BYTE』が、Smalltalkとその理念であるオブジェクト指向を紹介して世間の注目を集める契機になったが、ケイの思惑に反して技術的関心を集めたのはクラスの仕組みの方であった。オブジェクト指向は知名度を得るのと同時に、Simula発のクラス(継承と動的ディスパッチ)および抽象データ型(データ抽象とデータ隠蔽)にマウントされて解釈されるようになり、それらのコンセプトがケイの構想とは無関係であったことから、オブジェクト指向の定義はケイの手を離れて独り歩きするようになった。
C++の開発と普及(1979 - 88)
Simulaを研究対象にしていたAT&Tベル研究所の計算機科学者ビャーネ・ストロヴストルップは、1979年からクラス付きC言語の制作に取り組み、1983年に「C++」を公開した。C++で実装されたクラスは、Simula由来の継承と仮想関数に加えて、段階的なレキシカルスコープの概念をクラス構造に応用したアクセスコントロールを備えていた。C++で確立されたアクセスコントロールは情報隠蔽によるデータ抽象を提示したが、コードスタイル上ほとんどザル化されており、その理由からストロヴストルップ自身もC++は正しくない(not just)オブジェクト指向言語であると明言している。1986年にソフトウェア技術者バートランド・メイヤーが制作した「Eiffel」の方は、正しいオブジェクト指向を標榜してクラスのデータ抽象を遵守させるコードスタイルが導入されていた。クラスメンバ(フィーチャー)は属性/手続き/関数の三種構成で、手続きで属性を変更して関数で属性を閲覧するという形式に限定されており、これは抽象データ型に忠実な実装であった。アクセスコントロールはC++のとは異なるクラス指名方式にされ、仮想関数機能は延期手続き/関数として実装された。
1986年からACMがOOPSLAを年度開催するようになり、オブジェクト指向は従来の構造化開発に代わる技術として明確に意識され始めた。OOPSLAのプログラミング言語セクションでは、抽象データ型を基礎にしたクラス・パラダイムが主要テーマにされ、それを標準化するための数々のトピックが議題に上げられている。モジュール分割、抽象化、再利用性、階層構造、複合構成、情報隠蔽、実行時多態、動的束縛、総称型、永続性、並行性、自動メモリ管理といったものがそうであり、参画した識者たちによる寄稿、出版、講演を通して世間にも広められた。そうした潮流の中でストロヴストルップはデータ抽象の重要性を訴え、リスコフは基底と派生に分けたデータ抽象の階層構造の連結関係(置換原則)について提言した。契約による設計と開放閉鎖原則を提唱するメイヤーが1988年に刊行した『オブジェクト指向ソフトウェア構築』によるEiffel式のクラス理論は高く評価され、Eiffelを現行の模範形とする声も多く上がった。ただしこれは学術寄りの意見でもあったようで、世間のプログラマの間では厳格なEiffelよりも、柔軟で融通の利くC++の人気の方が高まっていた。他方でアラン・ケイのメッセージ・メタファに忠実であろうとする動きもあり、1984年に制作された「Objective-C」はC言語をSmalltalk方向に拡張してメッセージ式を平易化した言語であった。1987年にパロアルト研究所で誕生した「Self」は、Smalltalkのクラスベース設計をより柔軟に平易化したプロトタイプベースを編み出している。これらのメッセージレシーバーは、静的メソッド選択優先の動的ディスパッチ機構という方式でほぼ形式化された。メッセージレシーバーの仕組みは遠隔手続き呼出しやオブジェクト要求ブローカーの実装に適していたので、分散システムとオブジェクト指向の親和性を認識させることになった。
コンポーネントとネットワーク(1989 - 97)
ネットワーク技術の発展に連れて、データとメソッドの複合体であるオブジェクトの概念は、分散システム構築のための基礎要素としての適性を特に見出される事になり、IBM社、Apple社、サン社などが1989年に共同設立したOMGは、企業システムネットワーク向け分散オブジェクトプログラミング規格となるCORBAを1991年に発表した。その前年にマイクロソフト社はウェブアプリケーション向けの分散オブジェクトプログラミング技術となるOLEを発表し、1993年にはCOMと称するソフトウェアコンポーネント仕様へと整備した。このCOMの利用を眼目にしてリリースされた「Visual C++」「Visual Basic」はウェブ時代の新しいプログラミング様式を普及させる先駆になった。この頃にデータ抽象、データ隠蔽、アクセスコントロールおよびインターフェースによるプログラムの抽象化は、総じてカプセル化の概念でまとめられるようになった。クラスの継承が最もオブジェクト指向らしい機能と見なされていたのが当時の特徴であった。継承構造を利用した振る舞いサブタイピング及び動的ディスパッチは多態性という用語に包括された。こうしていわゆるオブジェクト指向の三大要素がやや漠然と確立されている。1996年にサン社がリリースした「Java」は三大要素が強く意識されたクラスベースであり、その中の分散オブジェクト技術はBeansと呼ばれた。類似の技術としてApple社もMacOS上でObjective-Cなどから扱えるCocoaを開発している。また、1994年から96年にかけて「Python」「Ruby」「JavaScript」といったオブジェクト指向スクリプト言語がリリースされ、従来の静的型付けに対する動的型付けと、クラスベースに対する新しいプロトタイプベースの認知度を高めている。
抽象化を旨とするオブジェクト指向ではそのプログラミング自体の抽象化も積極的に推進されている。80年代後半から立ち上げられたオブジェクト指向分析(OOA)やオブジェクト指向設計(OOD)の各手法から導き出される概念モデルを、多角的にチャート化ないしダイアグラム化するための数々のオブジェクト指向開発方法論がOOPSLA界隈の識者たちから発表されるようになり、そこで用いられる形式言語はオブジェクトモデリング言語と呼ばれた。これはプログラミングのためのプロトタイプ(ひな型)として重視され、オブジェクト指向ではモデリング言語とプログラミング言語が並んでソフトウェア開発の両輪になった。1994年にモデリング言語の代表的な活用例であるGOFデザインパターンが初回発表され、後のオブジェクト指向学習では非常に重視されるようになった。1995年にモデリング言語の標準化を企図したUMLがOOPSLAで初回発表され、1997年にOMGの標準モデリング言語として採用された。
オブジェクト指向言語一覧
ここではクラスベースの記述は省略している。
- Simula67 1967年 静的
- Smalltalk 1972年 動的・メッセージ式・純粋系
- C++ 1983年 静的
- Objective-C 1984年 静的と動的・メッセージ式
- Object Pascal 1986年 静的
- Eiffel 1986年 静的・純粋系
- Self 1987年 動的・プロトタイプベース・メッセージ式・純粋系
- Modula-3 1988年 静的
- Common Lisp(CLOS) 1988年(ANSI規格化は1994年) 動的・多重ディスパッチ・メタオブジェクト
- Oberon-2 1991年 静的
- Dylan 1992年 動的・多重ディスパッチ
- Lua 1993年 動的・プロトタイプベース
- Python 1994年(ver1.0) 動的
- Delphi 1995年 静的
- Ada 1995年からOOP化 静的
- Perl 1995年からOOP化 動的
- Ruby 1995年 動的・純粋系
- Java 1996年(ver1.0) 静的
- JavaScript 1996年(first released) 動的・プロトタイプベース
- OCaml 1996年 静的・関数型
- Squeak 1996年 動的・メッセージ式
- ECMAScript 1997年 動的・プロトタイプベース
- C# 2000年 静的と動的
- Visual Basic.NET 2001年 静的
- D言語 2001年 静的
- Io 2002年 動的・プロトタイプベース・メッセージ式・純粋系
- COBOL 2002年からOOP化 静的
- FORTRAN 2003年からOOP化 静的
- R言語 2003年からOOP化 動的・関数型・多重ディスパッチ
- Scala 2003年 静的・関数型
- Groovy 2003年 動的
- PHP 2004年からOOP化 静的と動的
- F# 2005年 静的・関数型
- MATLAB 2008年からOOP化 動的・関数型
- Go 2009年 静的
- Rust 2010年 静的・関数型
- Kotlin 2011年 静的
- Ceylon 2011年 静的
- Dart 2011年 静的・関数型
- Elixir 2011年 動的・関数型
- Julia 2012年 動的・関数型・多重ディスパッチ
- TypeScript 2012年 静的と動的・関数型
- Swift 2014年 静的・プロトコル指向
- Raku 2015年 静的と動的
用語一覧
ここでは日本語ページ化されている項目のみ列挙している。
- クラスベース
- プロトタイプベース
- メッセージ
- クラス
- インスタンス
- フィールド
- プロパティ
- メソッド
- インスタンス変数
- クラス変数
- コンストラクタ
- デストラクタ
- カプセル化
- 継承
- 多態性
- This
- Is-a
- Has-a
- サブタイピング
- ダックタイピング
- ミックスイン
- スーパークラス
- サブクラス
- 抽象クラス
- メタクラス
- オーバーライド
- 仮想関数テーブル
- 菱形継承問題
- 仮想継承
- インターフェース
- トレイト
- 抽象型
- オブジェクト型
- 型クラス
- 委譲
- 多重ディスパッチ
- ダブルディスパッチ
- オーバーロード
- アノテーション
- コンテナ
- ボックス化
- ジェネリクス
- 共変性と反変性
- モジュール
- ソフトウェアコンポーネント
- リフレクション
- モンキーパッチ
- メッセージ転送
- 型システム
- オブジェクト指向分析設計
- オブジェクト指向モデリング
- UML
- 契約による設計
- GOFデザインパターン
- GRASP
- SOLID
脚注
- ^ “At Utah sometime after Nov 66 when, 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. It was probably in 1967 when someone asked me what I was doing, and I said: "It's object-oriented programming".”Alan Kay on the Meaning of “Object-Oriented Programming”
- ^ “Our research has concentrated on the development of a rigorous model of computation based on relationship among computational events. The development of this model has been greatly influenced by Seymour Papert's “little people” model of computation, a seminar given by Alan Key at M.I.T. on an early version of Smalltalk, and the work of Church, Fischer, Landin, on formalisms based on the lambda calculus.”Induction and Meta-evaluation
- ^ “I didn't like the way Simula I or Simula 67 did inheritance (though I thought Nygaard and Dahl were just tremendous thinkers and designers). So I decided to leave out inheritance as a built-in feature until I understood it better. ”Alan Kay on the Meaning of “Object-Oriented Programming”