オブジェクト指向プログラミング

提供: miniwiki
2018/8/19/ (日) 20:02時点におけるAdmin (トーク | 投稿記録)による版 (1版 をインポートしました)
(差分) ← 古い版 | 最新版 (差分) | 新しい版 → (差分)
移動先:案内検索


オブジェクト指向プログラミング(オブジェクトしこうプログラミング、: object-oriented programming, OOP)は、コンピュータ・プログラミングパラダイム[1]のひとつで、オブジェクト指向の概念や手法を取り入れたものである。プログラムを、データとその振舞が結び付けられたオブジェクトの集まりとして構成する、などといった特徴がある。このパラダイムを指向しているプログラミング言語オブジェクト指向プログラミング言語である。

特徴

オブジェクト指向プログラミングで用いられる代表的な概念および技法として、以下のものが挙げられる。

上記を含むオブジェクト指向プログラミングにおいて有用な機能(の一部)を言語仕様として備えたプログラミング言語を、オブジェクト指向プログラミング言語 (OOPL; object-oriented programming language) という。

ただし、上述の特徴はオブジェクト指向言語に固有の概念というわけではなく、非オブジェクト指向言語の中にもこの性質を備えるものもある。

背景

オブジェクト指向プログラミングという考え方が生まれた背景には、計算機の性能向上によって従来より大規模なソフトウェアが書かれるようになってきたということが挙げられる。大規模なソフトウェアが書かれコードも複雑化してゆくにつれ、ソフトウェア開発コストが上昇し、1960年代には「ソフトウェア危機 (software crisis)」といったようなことも危惧されるようになってきた。そこでソフトウェアの再利用部品化といったようなことを意識した仕組みの開発や、ソフトウェア開発工程の体系化(ソフトウェア工学 (software engineering) の誕生)などが行われるようになった。

このような流れの中で、プログラムを構成するコードとデータのうちコードについては手続き関数といった仕組みを基礎に整理され、その構成単位をブラックボックスとすることで再利用性を向上し、部品化を推進する仕組みが提唱され構造化プログラミング (structured programming) として1967年エドガー・ダイクストラ (Edsger Wybe Dijkstra) らによってまとめあげられた(プログラミング言語の例としてはPascal 1971年)。なお、それに続けて「しかしデータについては相変わらず主記憶上の記憶場所に置かれている限られた種類の基本データ型の値という比較的低レベルの抽象化から抜け出せなかった。これはコードはそれ自身で意味的なまとまりを持つがデータはそれを処理するコードと組み合わせないと十分に意味が表現できないという性質があるためであった。」といったように、ほぼ間違いなく説明されている[2]

そこでデータを構造化し、ブラックボックス化するために考え出されたのが、データ形式の定義とそれを処理する手続きや関数をまとめて一個の構成単位とするという考え方でモジュール (module) と呼ばれる概念である(プログラミング言語の例としてはModula-2 1979年)。しかし定義とプログラム内の実体が一対一に対応する手続きや関数とは異なり、データはその形式の定義に対して値となる実体(インスタンスと呼ばれる)が複数存在し、各々様々な寿命を持つのが通例であるため、そのような複数の実体をうまく管理する枠組みも必要であることがわかってきた。そこで単なるモジュールではなく、それらのインスタンスを整理して管理する仕組み(例えばクラスとその継承など)まで考慮して生まれたのがオブジェクトという概念である(プログラミング言語の例としてはSimula 1962年)

Simulaのオブジェクトとクラスというアイデアは異なる二つの概念に継承される。一つはシステム全てをオブジェクトの集合と捉え、オブジェクトの相互作用をメッセージに喩えた「オブジェクト指向」である。オブジェクト間の相互作用をメッセージの送受と捉えることで、オブジェクトは受信したメッセージに見合った手続き単位(≒関数)を自身で起動すると考える。結果オブジェクトは自身の持つ手続きのカプセル化を行うことができ、メッセージが同じでもレシーバオブジェクトによって行われる手続きは異なる――多相性(ポリモーフィズム)を実現した(このメッセージを受け実行される手続き単位は、メッセージで依頼されたことを行うための「手法」の意味でメソッドと呼ばれる)。この思想に基づき作られたのがSmalltalk1972年)であり、オブジェクト指向という言葉はこのとき作られた。

一方、Smalltalkとは別にSimulaの影響を受け作られたC++1979年)は抽象データ型のスーパーセットとしてのクラス、オブジェクトに注目し、オブジェクト指向をカプセル化、継承、多相性をサポートするものと再定義した(その際、実行時速度重視及びコンパイラ設計上の制約により、変数メタファである動的束縛の特徴は除外された)。これらは当初抽象データ型派生仮想関数と呼ばれ、オブジェクトのメンバ関数を実体ではなくポインタとすることで、継承関係にあるクラスのメンバ関数のオーバーライド(上書き)を可能にしたことで、多相性を実現した(この流儀ではメッセージメタファはオブジェクト指向に必須ではないものと定義し、オブジェクトの持つ手続きをメソッドとは呼ばずメンバ関数と呼ぶ)。この他、Smalltalkにある動的束縛の類似的な機能としてオーバーロード(多重定義)が実装されている。

Smalltalkはこの「全てをオブジェクトとその相互作用で表現する」というデザインに立ち設計されたため、全てをファイルと捉えるファイル指向オペレーティングシステムからの脱却と、プログラムをフロー制御された手続きと捉える手続き型言語からの脱却が行われた。そのためSmalltalkは自身がオブジェクト指向オペレーティングシステムでもあること、メッセージ・パッシングなどの特徴を持った。これは当時のプログラム言語としては特異的であり、ガベージコレクタを必要としインタプリタ形式で実行される処理の重さも手伝って先進的ではありながら普及しがたいものであると捉えられた。また、メッセージでの多相性は、変数へのオブジェクトの動的束縛が前提となるため、静的型チェックが必要な実行時性能重視のコンパイラ言語にとって、実装から除外すべき特徴となった。

C++の創始者ビャーネ・ストロヴストルップは、Smalltalkのような論理美の追求よりも、現用としての実用性を重視した。そのため、C++の再定義した「オブジェクト指向」はこれらの問題を全てクリアにし、既存言語の拡張としてオブジェクト指向機能を実装できることでブレイクスルーを迎え急速に普及する。これによりメッセージ送信という考え方はやや軽視されるようになり、オブジェクト指向とはC++の再定義したものと広く認知されるようになった。

1980年代後半に次々と生まれたオブジェクト指向分析・設計論は、Smalltalkを源流とするオブジェクト指向を基に組み立てられた。このときSmalltalkは健在であったが広く普及しているとは言えずC++実装する機会が多かったが、C++はSmalltalkとは思想的にやや異なることと、仕様の複雑さが問題とされた。このニーズを受けC++の提示した現実解と、Smalltalk的理想論を融合するものとして、構文面ではシンプル化しながらも強くC++の影響を受けつつ、一方で用語や思想面でSmalltalk色を濃くしたJava1991年)が作られた。バランス感覚に長けたJavaの登場によってオブジェクト指向開発に必要な要素が全てそろい、1990年代後半からオブジェクト指向は広く普及するようになった。

オブジェクト指向プログラミング言語一覧

オブジェクト指向プログラミングをサポートする機能を備える代表的なプログラミング言語オブジェクト指向プログラミング言語)としては以下のようなものが挙げられる:

Simula 1962年
オブジェクトクラスを導入した最初の言語。また、C++が再定義したオブジェクト指向プログラミング言語の要件を満たすことから、最初のオブジェクト指向プログラミング言語とされることもある。クラスベース。当初はシミュレーション記述用に開発されたものであったが、後に汎用化された。
Smalltalk 1972年
オブジェクト間の相互作用にメッセージという機構を導入した最初の言語。オブジェクト指向と言う言葉は、Smalltalkのプログラミングスタイルを説明するために作られた。動的な型付けにより高い柔軟性を持つ。
C++ 1979年
C言語のオブジェクト指向拡張。Simula言語風にクラスを定義でき、C言語の型システムを強化している。当初はC言語に対して上位互換であった。また、強い型付けによりC言語譲りの実行効率の高いコードが生成できる。
Objective-C 1983年
C言語のオブジェクト指向拡張。C言語のコードとSmalltalk型のオブジェクトシステムを混在させたもの。オブジェクトシステム自体はCで書かれたランタイム。
Eiffel 1986年
強い型付けを行う型システム表明など堅牢性を強く意識して設計されたオブジェクト指向プログラミング言語。C++にテンプレートが導入される前に総称型を導入していた。
Self 1987年
最初のインスタンスベースのオブジェクト指向プログラミング言語。
CLOS 1988年
関数型言語Common Lispのオブジェクト指向仕様。
Modula-3 1988年
モジュールを実現していたModula-2言語のオブジェクト指向拡張。
Python 1990年
最初のオブジェクト指向スクリプト言語
Sather 1990年
Eiffelを拡張したもの。実行効率面での工夫が見られる。
NewtonScript 1993年
PDA環境用に修正されたSelf言語。PDAの性質を生かすため永続オブジェクトをサポートしている。
Ruby 1993年
オブジェクト指向スクリプト言語。クラスのMix-inなどのユニークな機能を持つほか、正規表現に渡るまで全てがクラスとして実装されていることが特徴。
Perl 1994年
Perl 5.0にてオブジェクト指向に対応。既存のPerl言語に後付けで拡張されたため記法は特殊だが、機能面では他のオブジェクト指向言語に引けを取らない。
Adaのオブジェクト指向拡張 1995年
恐らく最も仕様が複雑なオブジェクト指向プログラミング言語。
Java 1995年
仮想マシンJava仮想マシン)上で動作することによる高い可搬性リフレクションスレッドの標準サポートと充実したライブラリ群で知られているオブジェクト指向プログラミング言語。
Object Pascal (Delphi) 1995年
Pascal言語のオブジェクト指向拡張。
JavaScript 1996年
ウェブページ上で実行することを主目的に開発されたスクリプト言語。プロトタイプベースの言語である。
OCaml 1996年
関数型言語MLのオブジェクト指向拡張。クラス型推論機構に組み込まれているという特徴がある。
PHP 2000年
動的にWebページを生成することを主目的としたスクリプト言語。バージョン4からオブジェクト指向に対応し、バージョン5でアクセス権やインターフェースなど、オブジェクト指向の強化が行われている。
C# 2000年
Javaとよく似た作りでJavaと同じく仮想マシン上で動作するが様々な部分で拡張されている。.NET Framework上で動く。
Visual Basic.NET 2002年
既存のVisual Basic(6.0以前)を大きく改訂してオブジェクト指向に対応。C#同様.NET Framework上で動く。
COBOL 2002年
2002年に制定された国際規格に、オブジェクト指向機能が採用された。1994年には、国際規格案を先取りした商用コンパイラ上で利用可能になった。オブジェクト指向構文を使わないCOBOLプログラムと混在できる。
Ceylon 2011年
Java をもとにつくられ、Java のもつ長所と短所を考慮しつつ、商用言語として再設計された言語。
Swift 2014年
Objective-Cのオブジェクトを引き継ぎながらも、ベースのC言語を取り除いて新しく再設計された言語。

また、以下のような分類もある。

ハイブリッド型オブジェクト指向プログラミング言語 (hybrid object-oriented language)
「ハイブリッド」という用語をどう定義するかによるが、たとえば「手続き型や関数型などの既存の他のパラダイムを主とした言語に、オブジェクト指向機能を拡張した」という言語を「ハイブリッドだ」、と言う者もいれば、「当初からオブジェクト指向を含む、複数のパラダイムを指向する言語として設計された」という言語を「ハイブリッドだ」と言う者もいる。例としては、以上のどちらの基準か、それとも別の定義によるものかは知らないが、OCaml、C++およびObjective-C、CLOS、Object Pascal、object-oriented COBOLなどを挙げる者がいる。
純粋なオブジェクト指向プログラミング言語 (pure object-oriented language)
「純粋」という用語をどう定義するかによるが、たとえばSmalltalkRubyのように「あらゆる対象がオブジェクトである」という言語(たとえば、Rubyではnilも、ヌルポインタのようなものではなく、NilClassのインスタンスというオブジェクトである。この場合対照例としては、通常の数値型などがオブジェクトでないJavaであろう)を「純粋だ」と言う者もいれば、「当初からオブジェクト指向プログラミング言語として設計された」言語を「純粋だ」と言う者もいる(こちらの定義ではJavaも含まれるだろうか)。例としては、以上のどちらの基準か、それとも別の定義によるものかは知らないが、SmalltalkEiffelSelfRubySwiftなどを挙げる者がいる。

オブジェクト指向プログラミング言語では、オブジェクトへの参照ポインタが多用される。そのため、オブジェクトのメモリへの割り当てに関して、自動ガベージコレクション機能を備えているものが多い。ただし、すべての言語が備えているわけではない。例えば、C++はガベージコレクションを備えていない。

オブジェクト指向の応用

プログラミング(ただし、ごく狭義の。普通は以下のようなものもプログラミングの工程に含まれる)にとどまらず、以下のようなより広い範囲にオブジェクト指向は応用されている。

また、当初はJavaないしC++におけるデザインパターンを表記・記述するための(図形を含む)言語として設計が始まった統一モデリング言語は、ツールベンダ等の意向により以上のような応用をサポートするためのものとして、仕様は大幅に膨れている。

オブジェクト指向プログラミング言語の仕組み

オブジェクト指向プログラミング言語は、相互にメッセージを送りあうオブジェクトの集まりとしてプログラムを構成することができる仕組みを持つ。 そのために、少なくともオブジェクトについての3つの仕組みと、オブジェクトの管理についての3つの仕組みが必要となる。

オブジェクトの仕組み
  • オブジェクトに蓄えられる情報、データを表現する仕組み。
  • 他のオブジェクトにメッセージを配送する仕組み。
  • 受け入れ可能な各種メッセージに対応して、処理する事柄を記述する仕組み(メソッド)。
オブジェクトを管理する仕組み
  • オブジェクト間の関係を整理分類して系統立てる仕組み。
  • 必要なオブジェクトを作成・準備する仕組み。
  • 不要なオブジェクトを安全に破棄する仕組み。

これらをどのように言語の要素として提供し、どのような機械語コードで実現するかによって様々なオブジェクト指向プログラミング言語のバリエーションが生まれる。以下、オブジェクト指向プログラミング言語が提供する様々な要素が上記の仕組みをどのように実現しているかについて概観する。

オブジェクトの概念と実際

オブジェクト (object) はオブジェクト指向プログラミングの中心となる概念であり、この概念を実際にどう実現するかはオブジェクト指向プログラミング言語により異なる。

以下、概念と実際がどう対応しているかについて説明する。

  • 概念的には各々のオブジェクトは、プログラムが表現する情報システムの中で能動的な役割を持った存在を表現している。
  • 概念的には メッセージを受け取り、その処理の過程で内部に蓄えたデータを書き換え、必要に応じて他のオブジェクトにメッセージを送るといった動作をしている。
  • 概念的には コードとデータが一つになっている。

オブジェクトという概念の実現のされ方

実際のプログラムでは、全てのオブジェクトが互いに全く異なった存在ではなくオブジェクトは種類に分けることが出来る。

例えば勤怠管理のシステムであれば、氏名や年齢、累積勤務時間などのデータは異なっても社員は皆、出勤し退勤するという処理(振る舞い)は同じだろう。このように複数の異なるオブジェクトが同じ種類のメッセージを受け取り共通の処理をするのが普通である。

このような場合、各オブジェクトがそれぞれメッセージ処理のコード(前述の「振る舞い」に当たる)を独自に備えていては無駄である。そこでオブジェクト指向プログラミング言語がオブジェクトを実現する際には多くの場合、内部的にはオブジェクトを2つの部分に分けている。

同一種類のオブジェクトの間で変わらない共通部分

一つは同一種類のオブジェクトに共有される部分、例えばメッセージ処理のコード(振る舞い)や定数(どのオブジェクトでも異ならないデータ)の類である。

同一種類のオブジェクトの間で変わる個々の部分

もう一つは同一種類のオブジェクトでもそれぞれ異なる部分、典型的には各オブジェクトが保持するデータ群である。

メッセージの処理のされ方

そしてあるオブジェクトOにメッセージを配送し適切なメッセージ処理コード(振る舞い)を呼び出す際には、まず対象となるオブジェクトOについて共通部分の格納場所を見つけて適切なコードを選び出し、次にそのコードに対して処理対象となるオブジェクトO固有のデータの所在を示すオブジェクトIDを渡すようになっている。

各オブジェクトの固有データを識別するオブジェクトIDを表現する方法も様々で、オブジェクトのIDとしては名前、番号なども用いられることがあるが、オブジェクトの固有データを記憶している主記憶上のアドレスがそのまま用いられることもある。アドレスを直接利用することは非常に実行効率の向上に寄与するが、プログラム間でのオブジェクトの受け渡し、セッション間(プログラムが終了して再度起動された時など)でのオブジェクトの受け渡しにはそのまま利用することができない。

また各オブジェクトの固有データから共通部分の格納場所を見つける方法もまた各言語により異なり、その言語の開発目的に応じて実に多種多様である。

JavaScriptの場合

例えばJavaScriptの場合、各オブジェクトは連想配列であり、名前で表現されたメッセージのIDからメッセージ処理コードである関数への参照を直接見つけ出す。各オブジェクトの固有データもその連想配列に格納されていて、メッセージを処理する関数には連想配列のアドレスが渡される。

Selfの場合

Selfのようなインスタンスベースのオブジェクト指向プログラミング言語では、プロトタイプとなるオブジェクトがメッセージを処理するコードも保持しており、オブジェクトがクローンされて作成されるときにそのプロトタイプのありかを示す情報もコピーされ、メッセージは受け取ったオブジェクトのIDを添えてプロトタイプに送られて処理される(Selfでは実行効率上の問題から後に内部的にクラスを作って利用するようになっている)。

クラスベースの言語の場合

最も普及しているクラスベースの言語では、共通部分はオブジェクトの種類を表現するクラスに保持され、各オブジェクトは固有データと共にそのクラスのIDを保持する。そしてオブジェクトに送られるメッセージはその送り先オブジェクトにあるクラスのIDからクラスを見つけ、その中からメッセージを処理するコードを見つけ出し、処理対象となっているオブジェクトのIDを付してそのコードを呼び出す仕組みになっている。

メッセージ

メッセージ (message) はオブジェクト間の通信でやりとりされる情報である。メッセージはメッセージ種別を示すIDとメッセージの種別に応じた追加の情報からなる定まった形式を持つ。追加の情報はそれ自身が何らかのオブジェクトやオブジェクトのIDである場合もある。メッセージの配送には大別して2つの方式がある:

同期式
オブジェクトがメッセージの送信を依頼すると相手が受信、処理して結果を返すまでそのオブジェクトは処理を中断して待つ。
非同期式
オブジェクトがメッセージの送信を依頼した後、相手の応答を待たずにオブジェクトは処理を続行する。処理結果は別のメッセージとして返される。

両者とも一長一短がありどちらがすぐれているとは言えない。また並列・並行処理が可能な環境では一方の仕組みがあれば、それを利用してもう一方も実現可能である。一般的な傾向としては、メッセージの伝送や処理に時間が掛かる場合は非同期式の方が効率は良く、そうでない場合には同期式の方が挙動が分かりやすく利用しやすい。

並列処理並行処理システムを記述する言語や分散システムを記述する言語ではOSなどが提供するメッセージ機能や自前の配送メカニズムを使って非同期式でメッセージが配送される場合もあるが、一般にオブジェクト指向プログラミング言語ではその多くが同一のプログラム内の通信であるので同期式のメッセージ配送が利用される。特にコンパイルされるタイプのオブジェクト指向プログラミング言語では、しばしば特別なメッセージ配送の仕組みを用意せず、特別な形式の関数の呼び出しでメッセージの配送を直接に表現する。即ち、各メソッドを内部的には関数として実現し、メッセージIDはメソッド名で表し、関数の第一引数としてオブジェクトIDを渡し(この第一引数は多くの言語で特別な記法で表される)、追加の引数としてメッセージの追加部分の情報を渡すのである。こうするとメッセージ送信は直接的なメソッドの関数呼び出しとして表せる。ただし、プログラムで継承の仕組みが利用されている場合はプログラムのテキストからだけでは呼び出すべきメソッドが決定できない場合があるので、実行時にメソッドを決定するためにメソッド・サーチ仮想関数テーブルといった仕組みが必要となる。

多くのプログラミング言語においてメッセージは、メソッド呼び出しの比喩でしかないことが多い。SmalltalkやObjective-Cの様な言語では、メッセージはメソッド呼び出しとは独立した機構として存在している。メッセージが機構として存在する言語では、メッセージをオブジェクトに送信した際、宛先のオブジェクトにメッセージで指定したメソッドが存在しない場合でもメッセージを処理することが出来る。これを利用し、メッセージの配送先を別のオブジェクトに指定したり、メッセージを一時保存したり、不要なメッセージを無視する等といったメッセージ処理が行われる。


クラス

クラス (class) は大多数のオブジェクト指向プログラミング言語で提供されている仕組みであり、上記の機能の殆ど全てに関わりがある。概念的にはクラスはオブジェクトの種類を表す。このためオブジェクトはクラスに属するという言い方をする。あるクラスに属するオブジェクトのことをそのクラスのインスタンス (instance) と呼ぶ。データ型の理論から見た場合クラスは型を定義する手段の一つである。クラスによってオブジェクトを記述する言語をクラスベース (class-based) のオブジェクト指向プログラミング言語と呼ぶ。

ハイブリッド型オブジェクト指向プログラミング言語では在来のレコード型(Cでは構造体)の構文を拡張してクラスの定義を行うようにしたものが多い。

多くのオブジェクト指向プログラミング言語ではクラスをデータメンバメソッドの集まりとして記述する。平たく言えばデータ・メンバの集まりはオブジェクトが保持するデータの形式を定め、各メソッドはそれぞれオブジェクトが処理する特定のメッセージの処理方法を定める。しばしばデータ・メンバとメソッドには個別にアクセス権が設定できるようになっていて、そのクラスに属するオブジェクトが内部的に利用するものと他のクラスに属するオブジェクトに公開するものを分類できるようになっている。多くの場合、公開されたメソッドの集まりは全体として処理可能なメッセージのカタログの機能、即ちインタフェースを提供する。各言語によって異なるが特定の名前のメソッドを定めて、オブジェクトの生成や初期化時の処理、廃棄時の処理などを記述できるようにすることも多い。

多くの言語でクラスは言語の要素として直接実現されているが、これは実行効率のためであり、そのように実現することが必須というわけではない。実際、各クラスをそれぞれオブジェクトとして提供する言語も存在する(例:Smalltalk)。このような言語ではある種のリフレクション (reflection) が可能となる。即ち必要があればプログラムで実行時にクラスの動作を変更することが可能である。これは非常に大きな柔軟性を提供するが、言語処理系による最適化が難しいため実行効率は低下することが多い。近年では柔軟性と効率性を両立させるために基本的に言語要素としてクラスを提供した上で、リフレクション機能が必要なプログラムに対しては必要に応じて各クラスに対応するクラス・オブジェクトをプログラムが獲得できるようにしている言語が現れてきている。(例:JavaのリフレクションAPI)

インスタンスベース

クラスは非常に多くのオブジェクト指向プログラミング言語で提供されている機能ではあるが、オブジェクト指向プログラミング言語に必須の機能というわけではない。実際にオブジェクトの管理や、データ・メンバやメソッドの記述、継承に際してクラスという仕組みに依存せずに、もしくはクラスという仕組み自体を持たずに別の手段でこれらを実現している言語も存在する。このような言語をインスタンスベース (instance-based)、オブジェクトベース (object-based) あるいはプロトタイプベース (prototype-based) のオブジェクト指向プログラミング言語と呼ぶ。インスタンスベースまたはそれに類するのオブジェクト指向プログラミング言語には以下のようなものがある:

なお、クラスベースの言語とインスタンス・ベースの言語との間には明確な境界線はない。たとえば、インスタンス・ベースの代表格ともいえる Self には、traits と呼ばれるクラスのような仕組みが追加されているし、JavaScript、NewtonScript に至っては traits 類似の仕組みを「クラス」と呼称している。また逆に、クラスベースの言語でもクローンを行うメソッドを備え、委譲の仕組みを記述すればある程度はインスタンス・ベースのスタイルでプログラムを記述できる。

インスタンス・ベースの言語ではオブジェクトの生成は既存のオブジェクト、特にプロトタイプprototype、原型)と呼ばれるオブジェクトからのクローンによって行われる。当然、一群のクローンはその親、ひいてはプロトタイプと同一の種類のオブジェクトと見なされる。メソッドはプロトタイプ・オブジェクトに属し、メッセージは委譲によってそのオブジェクトが覚えているコピー元へ向かってプロトタイプまで順にメッセージが中継されてから処理される。新しい種類のオブジェクトが必要な場合は適当なオブジェクトをクローンした後で必要なデータ・メンバやメソッドを追加あるいは削除し新たなプロトタイプとすることで行われる。追加されたのでないメソッドに対応するメッセージについてはコピー元のオブジェクトに処理を委譲する。

クラスベースの言語との関係について考えてみると、クローンはプロトタイプと同一の「クラス」に属すると見なし、データ・メンバやメソッドが追加・削除されてあらたなプロトタイプが作られると別の「クラス」が内部的に生成されると考えることができる。ここでデータ・メンバやメソッドの追加のみを許して削除を許さないよう制限すればクローンの「クラス」がその親の「クラス」を継承した場合と同等になる。このためメッセージが委譲の連鎖をたどって配送されるという効率上の問題を無視すれば理論上、インスタンス・ベースの言語の記述能力はクラス・ベースの言語を包含していると言える。ただ、インスタンス・ベースの言語でも実行効率上の問題からなんらかのクラスに似た仕組みを備えている場合が多い。

データメンバ

データメンバ (data member) は、他のオブジェクトに対する参照ポインタであるか、他のオブジェクトそのものである。参照かポインタである場合にはそのデータメンバの参照するのはデータメンバが記述されているクラスそのもののインスタンスに対する参照であっても良い。

一般にデータメンバはインスタンスデータメンバインスタンスフィールド)とクラスデータメンバ静的変数)の2種類に大別できる。効率上の観点から言語が提供する基本オブジェクトの定数を表すデータメンバは特別扱いされる。そのような定数を表すデータメンバを特に定数データメンバ (constant data member) と呼ぶ。データメンバはC++などの言語ではメンバ変数 (member variable)、Javaなどではフィールドと呼ばれることがあり、UMLでは属性と呼ばれる。

インスタンスデータメンバ

インスタンスデータメンバ(一般に単にデータメンバと言われる場合はこちら)はそのクラスのインスタンス各々に保持される。インスタンスデータメンバの集まりはそのクラスのインスタンスが保持するデータの形式を定める。インスタンスデータメンバは単にデータメンバと呼ばれることも多い。

Smalltalkではインスタンス変数 (instance variable) と呼ばれる。

クラスデータメンバ

クラスデータメンバはそのクラスオブジェクトとインスタンスオブジェクトの間で共有されるデータである。

Smalltalkではクラスデータメンバはクラス変数 (class variable) と呼ばれる。また、C++Javaでは歴史的事情によりクラスデータメンバは静的データメンバ (static data member)、静的変数 (static variable)、静的フィールド (static field) と呼ばれる。

ただし、Smalltalkのクラス変数はC++やJavaのクラス変数とは異なる。Smalltalkにおいて、C++やJavaのクラス変数と同等となる変数はプール辞書 (pool dictionary) と呼ばれる。

インスタンスオブジェクトとクラスオブジェクト

動的型付けを採用するオブジェクト指向言語の多くは、クラスより生成するインスタンスの他にメタクラスという機能を持ちクラス自体をオブジェクトとして扱うことが出来る。このためオブジェクトには、インスタンスオブジェクトとクラスオブジェクトという2種類のオブジェクトが存在する。Java等クラスオブジェクトを持たない言語の文化圏では、インスタンスオブジェクトとオブジェクトを混同して説明される事があるが、Objective-CやPython、Ruby等、インスタンスオブジェクトとクラスオブジェクトが別であるオブジェクト指向言語では区別して説明される。[3] [4] [5]元々はSmalltalkから始まった用語である。

メソッド

メソッド (method) は特定の種類のメッセージの処理方法を記述したものである。メソッドもインスタンス・メソッドクラス・メソッドの2種にできる。インスタンス・メソッドはそのクラスの各インスタンスオブジェクトを操作し、クラス・メソッドはクラスオブジェクトを操作する。メソッドとの集まりはそのクラスのオブジェクトが処理可能なメッセージのカタログの機能を果たす。

他言語と比較したC++のメソッド

C++ではメソッドはメンバ関数 (member function) や関数メンバ (function member) と呼ばれる。これはC++がグローバル関数との区別をつけることと、クラスを抽象データ型の拡張と位置づけ、非メッセージメタファな言語思想を持っている為である。これら言語ではメソッドをオブジェクト(=クラスやインスタンス)の持ち物として捉えず、クラスに定義された機能要素であると考える。メッセージメタファを否定するため、同時にメッセージを実行するメソッド(手法)ではありえない。

クラスメソッドについて

クラス・メソッドだが、オブジェクト指向の本義に立ち返れば、クラス・メソッドがあるということはクラスがメッセージをレシーブできるという事になる。

クラスがメソッドを持つことは便利だが、クラスをオブジェクトとすると実行効率に劣るため、双方の利点を享受できるこのような折中的仕様を取る言語は多い。

C++ではクラスはオブジェクトでは無いが、一方でクラスに属するメソッドは存在する。

Eiffelではクラスはオブジェクトでは無いためクラスのメソッドであるクラス・メソッドは存在しない。

Smalltalkではクラスもオブジェクトの一種であるため当然クラスはメソッドをもつ。

クラスメソッドの呼び方について

クラス・メソッドは、C++では静的メンバ関数 (static member function) と呼ばれる。これはクラスがオブジェクトでない言語にとってはクラス・メソッドより正確な表現であり適切である。("static" とはCのstatic変数に由来しauto変数の対語である。関数コールによりスタック上に生成される関数インスタンスに依存しない変数と、インスタンス生成有無にかかわらず実行できる関数の類似による。)

Javaではクラス・メソッドは静的メソッド (static method) とも呼ばれることもある。

特定の機能の割り当てについて

言語によっては特定の名前のインスタンス・メソッドやクラス・メソッドにオブジェクトの生成、初期化、複製、廃棄といった機能を固定的に割り当てている。

初期化と廃棄時に利用されるメソッド

初期化に利用されるメソッドをコンストラクタあるいは構築子 (constructor)、廃棄時に利用されるメソッドをデストラクタあるいは消滅子 (destructor) と呼んで特別に扱うことが多い。

構築子が初期化だけを担う場合はイニシャライザあるいは初期化子 (initializer) と呼ばれることもある。

Javaでは消滅子をファイナライザ (finalizer) と呼び、テンプレート:Javadoc:SEメソッドがその役割を果たす。ただし、Javaにおけるファイナライザは本当に必要でない限り使用するべきではなく、C++などのデストラクタとは違った意味を持つ。

データ型の理論においては保持されるデータが必ずその型で認められる正しい値の範囲に収まることを保証するため、生成されるオブジェクトのデータ・メンバが必ず適切な構築子によって初期化されるように求める。またオブジェクトが入出力機器やファイルや通信、プロセススレッドウィンドウウィジェットなどハードウェアオペレーティングシステム (OS) が提供する資源を管理するために利用される場合に、構築子や消滅子でそれらの資源の使用開始(オープン処理)や使用終了(クローズ処理)をそれぞれ管理し、通常のメソッドでそれらにまつわる各種サービスを提供するようにすることで、それらのリソースがあたかもプログラム中のオブジェクトであるかのように自然に取り扱うことができるようになる。

C++とJavaの場合、各クラスの名前がコンストラクタの名前として使用される。C++では一部のコンストラクタは型変換演算子として、また暗黙の型変換にも利用される。

アクセス権

オブジェクト指向プログラミングにおいて、オブジェクトは、カプセル化されておりブラックボックスである。したがって、処理するメッセージのカタログ、つまりインターフェースだけが利用者に公開され、内部の詳細は隠されるのが基本である。しかし、あるクラスのインスタンスの内部だけで利用されるメソッドまで公開してしまうと、利用者にとって煩雑である。また、定数データ・メンバのようなものは一々メソッドでアクセスするようにせず公開してしまっても、カプセル化の利点は失われず効率的でもある。そこで、オブジェクトを定義するプログラマが各データ・メンバやメソッドについて公開・非公開を設定できる機能を用意している言語は多い。

例えば、Javaでは、データ・メンバやメソッドの宣言にpublicと指定すれば、他オブジェクトから自由に利用でき(公開と呼ばれる)、privateと指定すればオブジェクト内だけで利用できるようになる(非公開と呼ばれる)。

しかし、ある機能を提供するのに、一個ではなく一群のクラスに属するオブジェクトでそれを記述するのが相応しい事例がある。そのような場合、関係する一群のオブジェクト間でだけデータ・メンバやメソッドを利用できれば便利である。それを可能にするための拡張がいくつか存在する。例えば、継承を利用しているときに、あるクラスが子孫にだけ利用を許可したいデータ・メンバやメソッドがある場合、Javaではprotectedを指定することでそれを実現できる(限定公開と呼ばれる)。また、ある一群の機能を実現するクラスのライブラリで、その実現に関連するクラスに属するオブジェクトだけがデータ・メンバやメソッド利用できるようにしたい場合も考えられる。また、Javaでは、ライブラリを構成するクラス群を表現するパッケージ (package) という仕組みがあり、特に指定がない場合は同一パッケージに属するクラスのオブジェクト間でのみデータ・メンバやメソッドを相互に利用可能である。その他にも、デザインパターンの一つであるFacade パターンでは、この仕組みがテクニックとして応用されている。また、C++ではフレンド宣言という仕組みがあり、あるクラスで外部非公開に指定されているデータ・メンバやメソッドについて、その利用を許可するクラスや関数のリストをクラス内に列挙することができる。

なお、public、private、protectedというキーワードは、多くのプログラミング言語で用いられているが、その示す意味は言語ごとに差異があるため、注意が必要である。

コンポジション

コンポジションは、複数のオブジェクトがある一つのオブジェクトの構成要素となっている巨大なオブジェクト群をいう。コンポジションのもとにあるオブジェクトは同一の生存期間を持ち、一つの巨大な仮想オブジェクトの構成部品として機能する。

脚注

  1. コンピュータ・プログラミングのパラダイムについては『新しいプログラミング・パラダイム』などを参照: http://www.kyoritsu-pub.co.jp/bookdetail/9784320024939
  2. が、そういったように書いている書籍の著者がもし、ダイクストラダールホーアの本『構造化プログラミング』を参考文献に挙げていた場合、その著者は目次の後半すらきちんとチェックしていない本を参考文献として挙げている蓋然性が高い。
  3. Objective-Cプログラミ ング言語[1]
  4. Classes ― Python v2.7.3 documentation[2]
  5. クラス/メソッドの定義 (Ruby manual) [3]

関連項目