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

削除された内容 追加された内容
用語と解説
用語と解説
22行目:
 
=== 継承 ===
既存オブジェクトのデータ構成とメソッド構成を引き継いで、新しい派生オブジェクトを定義する仕組みが継承と呼ばれる。引き継ぐ際には新たなデータとメソッドを自由に追加できるので、派生オブジェクトの構成は既存要素+追加要素になる。既存の基底オブジェクトは親オブジェクト、その派生オブジェクトは子オブジェクトとも呼ばれる。クラスベースでは、親をスーパークラス、子をサブクラスと呼ぶ。一つのスーパークラスを継承するのは単一継承と呼ばれる。複数個のスーパークラスを継承してそれぞれの要素を引き継ぐのは多重継承と呼ばれる。[[統一モデリング言語|UML]]では汎化と特化の関係で表現されている。メソッドの抽象化に焦点を当てた継承の方は、[[統一モデ{{仮リング言語|実装継承|en|3=https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming)#:~:text=Implementation%20inheritance%20is%20the%20mechanism,class%20implementation%20with%20its%20own.}}などと呼ばれる。UML]]では実現と呼ば実装の関係で表現さるものになている。これ実装継承は特定のオブジェクトたちに共通した振る舞い側面を抜き出して抽象化する仕組みを指し、その抽象化オブジェクトは[[インタフェース (抽象型)|インターフェース]]、[[トレイト]]、{{仮リンク|プロトコル(OOP)|en|Protocol (object-oriented programming)|label=プロコトル}}などと呼ばれる。
 
=== 多態性 ===
46行目:
=== C++の開発(1979 - 88) ===
[[Simula]]を研究対象にしていた[[ベル研究所|AT&Tベル研究所]]の計算機科学者[[ビャーネ・ストロヴストルップ]]は、1979年からクラス付きC言語の開発に取り組み、1983年に「[[C++]]」を公開した。C++で実装された[[クラス (コンピュータ)|クラス]]は、Simula譲りの[[継承 (プログラミング)|継承]]と仮想関数に加えて、[[レキシカルスコープ]]の概念をクラス定義とその継承構造に応用したアクセスコントロールを備えていた。C++で確立されたアクセスコントロールはカプセル化の元になったがコードスタイル上ほとんどザル化されており、その理由からストロヴストルップ自身もC++は正しくない(''not just'')オブジェクト指向言語であると明言している。1986年にソフトウェア技術者[[バートランド・メイヤー]]が開発した「[[Eiffel]]」の方は、正しいオブジェクト指向を標榜してクラスのデータ抽象を遵守させるコードスタイルが導入されていた。クラスメンバ(フィーチャー)は属性、手続き、関数の三種構成で、手続きで属性を変更し関数で属性を参照するという形式に限定されており、これは抽象データ型の[[セマンティクス|振る舞い意味論]]に沿った実装であった。アクセスコントロールはC++のアクセス修飾子による段階的レキシカルスコープ定義に対して、自身のクライアントクラスを定義する書式になり、これはモジューラプログラミングの情報隠蔽論に沿った実装であった。C++の仮想関数は延期フィーチャーとして実装された。{{Quotation|''I made up the term ‘object-oriented’, and I can tell you I didn’t have C++ in mind.''
<br />(僕はオブジェクト指向という言葉を作った、それとC++に関心がなかったことも分かっている)|Alan Kay}}1986年から[[Association for Computing Machinery|ACM]]が[[OOPSLA|オブジェクト指向会議]](OOPSLA)を開催し、そのプログラミング言語セクションでは[[抽象データ型]]の流れを汲む[[クラス (コンピュータ)|クラス]]・パラダイムが主要テーマにされ、それを標準化するための数々のトピックが議題に上げられている。[[モジュール性]]、情報隠蔽、[[抽象化 (計算機科学)|抽象化]]、再利用性、階層構造、複合構成、実行時多態、動的束縛、[[ガベージコレクション|自動メモリ管理]]といったものがそうであり、参画した識者たちによる寄稿、出版、講演を通して世間にも広められた。[[契約による設計]]を重視提唱するメイヤーが1988年に刊行した『オブジェクト指向ソフトウェア構築』は名著とされ、Eiffelを現行の模範形とする声も多く上がった。ただしこれは学術寄りの意見でもあったようで、世間のプログラマの間では厳格なEiffelよりも柔軟で融通の利くC++の人気の方が高まっていた。また、Smalltalkが提唱するメッセージ・メタファも単にオブジェクト指向の発案元であるという理由から一目置かれており、クラスのメソッド呼び出しをオブジェクトにメッセージを送ることになぞらえる考えが広まった。これは実行時の選択メソッドをメッセージの発送先にする意味合いで動的(一重と多重)ディスパッチの呼称の由来になっている。他方でSmalltalkの仕様に忠実であろうとする動きもあり、1984年に計算機科学者ブラッド・コックスが開発した「[[Objective-C]]」はSmalltalkをモデルにしてそれを平易化した言語であった。そのメッセージレシーバーはメソッドリストにないセレクタを受け取った場合にのみ動的ディスパッチ機構に移るというスタイルで形式化された。程なくしてこのメッセージレシーバー実装の必要性には疑問符が付けられるようになったが、[[遠隔手続き呼出し]]のスタイルにはマッチしていたのでメッセージにある種の理想を抱く風潮はいつまでも残った。
 
=== プロトタイプベースの考案(1985 - 90) ===
68行目:
:[[Pascal]]にクラスベースのオブジェクト指向を追加したもの。当初はモジュールのデータ隠蔽的なカプセル化、単一継承、仮想関数による多態性という基本的なものだった。静的型付け重視である。[[多重定義|関数&演算子オーバーロード]](アドホック多相)と[[ジェネリックプログラミング|ジェネリクス]](パラメトリック多相)は当初採用に到らなかった。[[ニクラウス・ヴィルト|ヴィルト]]監修の[[アップル (企業)|アップル社]]による初回バージョンを基礎にして様々な企業団体による派生版が公開されており、その特徴と機能追加も様々である。
;[[Eiffel]] 1986年
:[[C++]]の柔軟性と融通性とは正反対のオブジェクト指向言語。[[クラスベース]]で[[静的型付け]]重視である。[[契約プログラミング|契約による設計]]に基づく[[表明|アサーション]]の挿入でクラスの状態および演算用の引数と返り値を細かくチェックできる。[[例外処理]]も備えられている。クラスメンバ(フィーチャー)はデータ、アクセッサ、ミューテイタの三種限定で[[多重定義|オーバーロード]]はできない。カプセル化の可視性は自身に依存するクラス(クライアント)を定義する形で決められる。多重継承可能であり、クラス間の繋がりを[[仮想継承]]機能、各種[[オーバーライド]]指定子、名前衝突を解決するリネーミング機能などで綿密に設定できる。仮想関数と[[ジェネリックプログラミング|ジェネリッククラス]]も導入されている。[[ガーベジコレクション]]機能が初めて導入されたオブジェクト指向言語でもある。
;[[Self]] 1987年
:[[メッセージパッシング|メッセージ]]構文ベースのオブジェクト指向言語。デフォルト配備のオブジェクトを複製して、そのスロットに任意のプロパティとメソッドを[[ダイナミックバインディング|動的バインディング]]できるという[[プロトタイプベース]]を初めて導入したオブジェクト指向言語でもある。ゆえに[[動的型付け]]重視である。当初はSmalltalkの派生言語として公開されており、それと同様に専用のランタイム環境上で実行され、GUI運用環境の構築も目標にしていた。Selfのランタイム環境は[[実行時コンパイラ]]機能を初めて実装したことで知られており画期的な処理速度を実現している。この技術は[[Java仮想マシン]]の土台になった。
134行目:
:(''abstract class'')は、クラスメンバの一部のメソッドだけが抽象化されているクラスを意味する。抽象化されたメソッドとは、メソッドシグネチャ(返り値+メソッド名+引数欄)だけが定義されてコード内容が省略されているメソッドを意味する。抽象クラスはインスタンス化できないので継承専用になる。抽象メソッドは、サブクラスの方でメソッドのコード内容が実装されてオーバーライドされる。
;[[インタフェース (抽象型)|インターフェース]]
:(''interface'')はプログラム概念と機能名の双方を指す用語である。言語によってはプロトコルと言われる。抽象メソッドと実メソッドをメンバにできる純粋抽象〜半抽象クラスを意味する。クラスの振る舞い側面を抜き出したもの抽象体であり、[[統一モデリング言語|UML]]では実現と言われる。クラスによるインターフェースの継承は実装と呼ばれる。多重実装可が普通である。ミックスインとの違いは、抽象階層に焦点が当てられている事であり、直下の実装オブジェクトを共通の振る舞い側面でまとめることがその役割である。インターフェースは自身の実装オブジェクトをグループ化できる。{{仮リンク|記名的型付け|en|Nominal type system|label=}}に準拠しているのでインターフェースの実装の明記が振る舞い側面の識別基準になる。インターフェースは抽象メソッド主体なので多重継承時のメンバ名の重複はあまり問題にならない。共通の実装メソッドに集約されるからである。インターフェースは非インスタンス対象である。
;[[ミックスイン]]
:(''mixin'')はインターフェースに似たプログラム概念を指す用語である。機能名は言語によって[[トレイト]]、プロトコル、構造型(''structural type'')と言われる。抽象メソッドと実メソッドとデータメンバをメンバにできる継承専用クラスを意味する。クラスを特徴付けるための構成パーツである。クラスによるトレイトの継承は実装と呼ばれる。多重実装可が普通である。インターフェースとの違いは、トレイトの実装階層に焦点が当てられている事であり、オブジェクトを所有メンバで特定してまとめることがその役割である。トレイトは自身の[[上位集合]]であるオブジェクトをグループ化できる。{{仮リンク|構造的型付け|en|Structural type system|label=}}に準拠しているので所属メンバ構成自体がトレイト等価性の識別基準になる。これはトレイト実装を明記していなくても、そのトレイトが内包する全メンバを所持していれば同じトレイトと見なされることを意味する。トレイトは合成や交差が可能である。トレイトは多重継承時のメンバ名重複の際にその参照の優先順位に注意する必要がある。トレイトは非インスタンス対象である。
159行目:
 
;[[モジュール]]
:(''module'')は1個以上のクラスをまとめたものである。パッケージと似ているが、オブジェクト指向下のモジュールはもっぱら動的ローディング(遅延バインディング)と情報隠蔽に焦点を当てたプログラム概念である。情報隠蔽は[[カプセル化]]と同様に、自身の内包クラスの外部公開(輸出)と内部隠蔽を定義できる。自身が参照する他モジュールは輸入するという形式で明確に宣言される。動的ローディング用途のモジュールでは内包する基底クラスの詳細を明らかにしつつも、その派生クラスの種類と詳細を明らかにしていないケースが多々あるので、その派生クラスを代入するための動的束縛型は特に不透明型(''opaque type'')と呼ばれる。不透明型はもっぱら型制約と併せて用いられる。
;[[モンキーパッチ]]
:(''monkey patch'')はモジュールやスクリプトファイルなどの動的ローディングを用いて、インタプリタ実行後またはコンパイル後のソースコード内容を変化させる手法である。ソースコードに特定のフィルター処理を記述しておき、その中で任意の箇所を動的ローディングされたモジュール内のクラスや関数や変数で置き換えさせる事で、その時の配置モジュールに合わせた処理内容の変化ができる。モジュールを外せばフィルター処理は無効になる。この置き換え(パッチ当て)は遅延バインディング相当である。ソースコードを変えなくてよいのが条件である。
179行目:
:(''message receiver'')はメッセージを受け取ることに特化されたメソッドである。メッセージレシーバーはインスタンスのデフォルトで呼び出される窓口レシーバーの形態と、指定メソッドが存在していない時に呼び出される補足レシーバーの形態がある。窓口レシーバーのメッセージはセレクタと引数のペアまたはそのどちらかだけという書式である。窓口レシーバーは極めて柔軟なプロセスを実現できるが、実装の煩雑さとオーバーヘッドが大きくなる。セレクタは識別子またはペア引数の注釈になる文字列である。セレクタはメソッドへの自動分岐が主な用途になるが、そのフィルター処理と取りこぼし処理の中でただのキーワードとしても自由に解釈できる。補足レシーバーのメッセージはメソッド名文字列と引数配列という書式になっており、いかなるメソッドシグネチャにも該当しなかった取りこぼしになる。このメソッド名文字列と引数配列を自由に解釈して柔軟な処理を行える。補足レシーバーの機能名はメソッドミッシングなどである。
;[[派生型|サブタイピング]]
:(''subtyping'')はクラス(型)のあらゆる派生関係および派生構造の実装形式とその働き方を包括したプログラム概念である。サブタイプ多相(''subtype polymorphism'')とも呼ばれる。継承、オーバーライド、コンポジション、ジェネリクス、共変バリアンス、引数バリアンスによるディスパッチ、不透明型といったものは全てサブタイピングの一側面である。オブジェクト指向でよく使われるものは振る舞いサブタイピング(''behavioral subtyping'')であり、これに当てはまるものは継承とメソッドのオーバーライドを組み合わせた仮想関数である。
 
;ジェネラライゼーション
:汎化(''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に依存しているとなる。
;[[委譲|デリゲーション]]
:委譲(''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デザインパターン
: 生成に関するパターン<gallery heights="40">
ファイル:Abstract Factory UML class diagram.svg|[[Abstract Factory パターン|Abstract factory]]
ファイル:Factory Method UML class diagram.svg|[[Factory Method パターン|Factory Method]]
ファイル:Builder UML class diagram.svg|[[Builder パターン|Builder]]
ファイル:Prototype UML.svg|[[Prototype パターン|Prototype]]
ファイル:Singleton UML class diagram.svg|[[Singleton パターン|Singleton]]
</gallery>
: 構造に関するパターン<gallery heights="40">
ファイル:Adapter pattern UML diagram.PNG|[[Adapter パターン|Adapter]]
ファイル:Bridge UML class diagram.svg|[[Bridge パターン|Bridge]]
ファイル:Composite UML class diagram (fixed).svg|[[Composite パターン|Composite]]
ファイル:Decorator UML class diagram.svg|[[Decorator パターン|Decorator]]
ファイル:Facade UML class diagram.svg|[[Facade パターン|Facade]]
ファイル:Flyweight UML class diagram.svg|[[Flyweight パターン|Flyweight]]
ファイル:UML DP Proxy.png|[[Proxy パターン|Proxy]]
</gallery>
: 振る舞いに関するパターン<gallery heights="40" perrow="11">
ファイル:Chain of responsibility UML diagram.png|[[Chain of Responsibility パターン|Chain of Responsibility]]
ファイル:Command Design Pattern Class Diagram.png|[[Command パターン|Command]]
ファイル:InterpreterUMLDiagramm.png|[[Interpreter パターン|Interpreter]]
ファイル:Iterator UML class diagram.svg|[[Iterator パターン|Iterator]]
ファイル:Mediator design pattern.png|[[Mediator パターン|Mediator]]
ファイル:Memento design pattern.png|[[Memento パターン|Memento]]
ファイル:Observer UML smal.png|[[Observer パターン|Observer]]
ファイル:State Design Pattern UML Class Diagram.svg|[[State パターン|State]]
ファイル:StrategyPattern.png|[[Strategy パターン|Strategy]]
ファイル:Template Method UML class diagram.svg|[[Template Method パターン|Template Method]]
ファイル:Visitor UML class diagram.svg|[[Visitor パターン|Visitor]]
</gallery>
 
== 脚注 ==