クラスとは、オブジェクト指向プログラミングにおいて、生成されるオブジェクトの特徴を取り決めたものを指す。
オブジェクトはクラスを元に生成される。これをインスタンス化と呼ぶ。クラスの持つそれぞれのデータ構造、振る舞いには、外部からのアクセス範囲を定義することができる。クラスは要素の継承を行うことが出来る。これを汎化と呼ぶ。
継承されたクラスは継承元のクラスから見て、子クラス(または、サブクラス)と呼ばれる。また、継承元のクラスは親クラス(またはスーパークラス)と呼ばれる。子クラスは、親クラスが持っている要素をすべて受け継いでいる。これが継承である。
以下ではダイヤモンド継承の問題を取り上げ、その解決策としての観点からインターフェースと抽象クラス(プログラミング言語によっては呼称が異なることあり)について述べる。クラス・メソッド・継承・リスコフの置換原則などの用語についてはオブジェクト指向の記事の「詳細」以下の部分を読むなどして理解していることを前提とする。
リスコフの置換原則の最も単純な実装は、継承した元のクラスの動作をそのまま使用することである。
それだとメソッドの追加くらいしかできず親クラスとの差別化が難しいので、実際のプログラミング言語では継承した元クラスのメソッドを矛盾のない範囲で変更することができる(オーバーライド)。
だが変更できるというだけで、継承した元のクラスの性質を引き継げる範囲内で変更するべきということには変わりない。
前述の通りサブクラス(子クラス)はスーパークラス(親クラス)の性質を継承する。
今回はオブジェクト指向の記事にあった動物園の例は忘れて、以下のような性質(クラス)を考えよう。
- 回避する人: 前方に障害物があると回避して進む。
クラピカ理論によれば、人は左右に分かれ道があると、左を選ぶらしいので、以下のようにサブクラスを作成する。
なお、説明のための例示なので、必ず決まった方向に回避するものとする。異論は認めない。
生物学の分類だと一つ生物が複数の種に同時に属するということはないのだが、上記のような分類だと複数の分類(クラス)に所属することもあり得る。ここで、以下のようなクラスを考える。
さて、このクラスに属する人は前方に障害物があると左右どちらに回避して進むのだろうか。
このように複数のクラスを継承した結果、リスコフの置換原則と矛盾する結果が生じることがある。
継承関係を図式化するとダイヤモンド型になることからダイヤモンド継承と呼び、オブジェクト指向の特色である継承の大きな問題点とされている。
↙ | 回避する人 | ↘ |
本能のままに、回避する人 | 天の邪鬼な、回避する人 | |
↘ | 天の邪鬼かつ本能のままに、回避する人 | ↙ |
意味論的に、「天の邪鬼」と「本能のまま」という性質は両立しないという反論はありえるが、プログラミング言語処理系がそのような識別を行うことはない。
言語によって対応が異なるが、おおまかな対処方法は以下のようなものがある。
メソッドを継承する際のクラスの優先順位を決めれば、継承するメソッドを一つに特定することは可能である。
ただ、継承関係が網の目のように複雑になった時に優先順位を決められるのか、そして、その規則で決定した優先順位は意味論的にも適切なのかという問題が残る。
もう一つの解決法は、具体的なことを決めてしまうことで矛盾が確定的になるのだから、具体的なことは決めずにあいまいなままにしておこうという、いささか乱暴な考えである。以下のように曖昧な定義となる。
そして、「天の邪鬼かつ本能のままに、回避する人」が左右どちらに回避するかは、このクラスの用途に応じて継承する側が責任を持って改めて設計し直すのである。
例えば、かなりいい加減に書くが、
のような感じである。
勘の良い人は気づいたかも知れない。そう、実装責任の丸投げである。
誰かが責任を負わなければならない以上、抽象的な段階よりは、具体的な段階で実装したほうが実際の要件と乖離してしまう危険が少ないので、丸投げだから無責任ということはなく、むしろ適切と言っていいくらいである。
さて、このように具体的な内容を決めない抽象的な動作定義をインターフェースという用語で呼ぶ。インターフェースはダイヤモンド継承問題を生じないので、複数継承することが可能である。
この呼称はクラスをプログラム部品と見た時に、クラスの機能(メソッド・プロパティ)に「インターフェース」を介してアクセスすることから来ている。実際にプログラミングする段階になると、クラスに「インターフェース」を通じてアクセスしていることが実感できるはずなので、用語に納得できない人はコードを書いてみるしかない。
解決方法はもう一つある。
実装を全く継承しないのでは、オブジェクト指向の特長を生かせない。
もう一度よく考えてみよう。二つ以上のクラスを継承するから矛盾するのであって、一つだけなら矛盾を生じない。そこでクラスの継承は一つまでで、残りの性質はインターフェースで継承するという方式を採用したのが単一継承である。
この時、実装の一部はインターフェースの様に未定にしておいて、継承可能な部分だけ実装するという、クラスとインターフェースの中間的なものも考えることができる。これが抽象クラスである。
抽象クラスも、実装を持つ部分がダイヤモンド継承問題に抵触するので、単一継承しか出来ない。
インターフェースと抽象クラスは実装に未定の部分があるのでインスタンス化できないという制約がある。
掲示板
掲示板に書き込みがありません。
急上昇ワード改
最終更新:2024/06/05(水) 07:00
最終更新:2024/06/05(水) 07:00
ウォッチリストに追加しました!
すでにウォッチリストに
入っています。
追加に失敗しました。
ほめた!
ほめるを取消しました。
ほめるに失敗しました。
ほめるの取消しに失敗しました。