分類基準が複数存在する際の話。
自然科学における伝統的な分類では1つのレベルについて1つの基準が使われている:
- ex: 脊椎動物or無脊椎動物、葉が毎年新しいものと入れ替わるかどうか
- 単一継承の階層構造
- 利点: 非常に単純
- 問題: 自然は決して単一の基準で成り立っているのではない (例は本文参照)
ソフトウェアを作る際に、1つの基準では限定的するぎるような場合にはどうするか?
- これまでの章で(完全に習得すべく学んだ)多重継承、特に反復継承の技法が利用可能
人事管理システムにおけるクラスEMPLOYEEの例:
- 従業員を分類するための、二つの独立した基準がある:
- 契約の種類による分類: 正社員と臨時雇い、etc
- 仕事の種類による分類: 技術職、事務職、管理職、etc
単純だか好ましくない継承構造の例:
- 図24.16: 図乱雑な分類
- 大きく異なる概念が同じレベルのクラスによって表現されている
- この継承構造は満足なものとは言えない
今検討中の例で使われているこの分類のために、まだ継承を使うつもりなら、競合する分類基準を表すための中間的なレベルを作るべきである
中間的なレベルを導入した例:
- 図24.17 viewによる分類
- CONTRACT_EMPLOYEEとSPECIALTY_EMPLOYEEが中間的なレベルのクラスとして導入されている
- ビュー継承の例
- 「正社員の技術職」という従業員が存在し得る
- 反復継承が向いている
部分型継承とビュー継承:
- 部分型継承: ある特定のクラスのさまざまな後継者が互いに素であるインスタンスの部分集合を表す
- ビュー継承: 同じ親のインスタンスを分類するさまざまなやり方を表している
注意:
- ビュー継承に意味があるのは、親と後継者の両方が暫定クラス(一般なカテゴリを表すクラス)の場合だけ
ビュー継承は継承のより一般的な使い方から比較的かけ離れており、批判を受けやすい:
- 「読者の皆さんがご自身の目的のために使うかどうかはご自分で判断されるのが良い」
- 「いずれにしろ、ここで賛否両論を検討しなければならない」
ビュー継承は熟練者向け:
- ビュー継承(およびそれが必要とする反復継承)が 初心者向けのメカニズム ではないことは明らか
- もし、重大なオブジェクト指向開発プロジェクトの実戦経験が数ヶ月にも満たないのなら、ビュー継承には関わらない方が良い
代替案:
- 最優先する基準として分類基準のうちの1つを選び、継承の階層構造を考える際の唯一の指針として使う
- そのほかの基準を扱う場合は、特定の特性を使う
- EMPLOYEEの例 (図24.18)
- 仕事の種類による分類、を優先 (メインの継承構造に採用)
- 契約の種類による分類は WOKER_CONTRACT 型の特性
contract
を持たせることで解決
この方法はビュー継承の代わりに使うことが考えられる:
- 別の階層構造と新しい属性(この場合はcontract)ができ、対応する顧客関係が増えたことで、確かに構造は複雑になる
- とはいえ、そのような階層構造における抽象は分かりやすい
- ex: 「雇用契約」、「正社員の雇用契約」
- ビュー継承の方法でも抽象は明らかであるが、説明が少し難しくなる
- ex:「雇用契約という視点からみた従業員」、「職種という視点から見た従業員」
問題領域分析の初期の段階においてビュー継承について考えることは珍しいことではない:
- 応用領域に関する理解が浅いので、すべての候補が有力に見える
- 最終的には、それらの中の一つの基準が他より飛び抜けて優れていることが分かることがある
- そのような場合には、ビュー継承は捨てるべき (そして、24.10.3の代替手法等の、より複雑ではないものを選ぶべき)
ただし、次の三つの条件が満たされる場合は、ビュー継承が有効:
- いろいろな分類基準が同じように重要であるため、主たる基準として任意の基準を選ぶことができる
- たくさんの組み合わせを考える必要がある (ex: 正社員の管理職、一時雇いの技術者、正社員の技術者、etc)
- 検討中のクラス群が非常に重要であり、考え得る限り最善の継承構造を得るために多くの時間をかける価値がある
- それらのクラスが再利用の可能性の大きい 再利用可能なライブラリ に含まれる場合は、特にそうである
Eiffelのコンテナライブラリの例:
- 図24.19
- BOX, COLLECTION, TRAVERSABLE, はビュー
- 上の三つの条件を満たしている
- 「最初はビュー構造ではなかったけど、後になって...」
ビュー継承に関して検討してきたことの結論:
- お気楽な人向けではない
- 適用できる場合は、多くの基準が相互に作用する複雑な問題領域における主要な役割を果たす
- とにかく正しくできなければならない、再利用可能な部品の基本ライブラリの場合のように、それだけの努力をする価値があるならば
- オブジェクト指向に関する書籍/記事/ライブラリを見ているとき、その中に見られる継承構造はすでに設計済み
- なぜそのような構造にする必要があったかが必ず説明されているとは限らない
- 自分で継承構造の設計に取り掛かることになったら、どうすればよいか?
トップダウン設計について:
- 教育用の文献の多くは、最も汎用的なレベル(一番上の部分)から最も具体的なレベル(葉の部分)へと継承構造を設計すべきである、という印象を与える傾向がある
- 図形のように、既に良い構造があるなら、それについて 記述 することがしばしば最善の方法である
- しかし、構造を記述するための最善の方法が、構造を 作り出す 最善の方法とは限らない
- 現実の世界では、先に特殊なケースを見つけ、後からその特殊なケースが実はある汎用的な抽象の変形の1つだと分かる場合が多い
多くの場合、抽象は唯一無二ではない。 ある特定の概念を一般化するための最善の方法は、その概念とその概念の変種に対して、あなた、または、あなたの顧客が最もしそうなことが何かによって異なる。
二次元空間における点(POINTクラス)の例:
- 少なくとも四つの一般化が考えられる:
- 任意の次元の空間における点: POINT, POINT_3D
- 幾何学的図形: POINT, FIGURE, RECTANGLE, CIRCLE
- 多角形: POINT, QUADRANGLE, TRIANGLE, SEGMENT
- 二つの座標によって完全に決定されるオブジェクト: POINT, COMPLEX, VECTOR_2D
- どれが最善かを断定することは不可能 (ソフトウェアの対象領域依存)
したがって、最も役に立つ一般化の道筋をみつけたと確信できるまで待ったために、 抽象化が少し遅すぎると思うこともあるような慎重な過程を辿る方が、 試されていない抽象をあまりにも多く、あまりにも早く使うやり方よりも望ましい。
POINTは典型的な例である。 ある特定の抽象の集合について2つの競合する分類を提示されたとき、多くの場合、どちらがより良いかは合理的な論拠に基づいて判断できる。 しかし、考え得る中で特定の継承構造が最善のものであると判断できるケースはめったにない。
ソフトウェア以外の領域でも同様。
ソフトウェアの階層構造を設計するときの適切な過程は、演繹と帰納の組み合わせ、特殊化と一般化の組み合わせである。
帰納的な「ヨーヨー」アプローチも正常
経験を積み、直感が磨かれてゆくにつれて、(具体的な)演繹的な決定の割合が大きくなっていくことに気付くはずである。 しかし、帰納的な部分は常に残ることだろう。
二つの形式の帰納的な親の構築が一般的であり、有効である:
- 概念抽象:
- 図24.21
- より高いレベルの概念を後から認識すること
- 「ある役に立つ概念を表すクラスBを見つけたが、このクラスを作った人は、その概念が、実は、より一般的な概念Aの特殊なケースであることを認識していなかった」
- 要素抽出:
- 図24.22
- EとFという二つのクラスが実は同じいパン的な概念の変形を表すことが分かった場合
後からより適切な抽象に気づいた場合は、書き直し(親クラスの追加)を推奨。
図 24.23
概念抽象や要素抽出によって、継承構造を変更したとしても、各クラスの直接の顧客には影響を与えない。
概念抽象と要素抽出は成功するオブジェクト指向ソフトウェア構築の過程の特徴:
- 継続的改善の過程を表す典型
- 「私の経験では、オブジェクト指向の実戦において最も自慢できる側面の1つがこの点」
- 「最初から完全なものを作ることを期待されてはいないけれども、皆が満足するまで設計を継続して改善する機会が与えられていると分かるのだ」
オブジェクト指向をうまく適用している開発グループでは、このように、ソフトウェアの 抽象のレベル が定期的に高まり、 その結果が品質に反映されていることがプロジェクトのメンバに明らかに分かり、それが彼らに絶えず刺激を与え、 やる気を促す役割を果たす。
この章で扱ってきた継承が「何を意味するのか」や「どのように利用すればよいのか」についての、いくつかの結論。
- 継承の使い方の多様性に怖気付いてはならない
- 多重継承や共有性質継承を禁止しても、自分を傷つける以外の目的を果たさない
- あなたを助けるためのものなので、上手に使いなさい
- 継承はだいたいにおいて 供給者 の技術
- 敵(特に、ソフトウェア開発者にとって情け容赦のない敵である複雑さ)と戦うための武器の一つ
- (特にライブラリの場合は)__顧客__ソフトウェアにとっても重要
- しかし、継承の主な目的はそもそも「もの」作りを助けること
- 目的はソフトウェアの構築であって、哲学ではない
- 解決策が1つしかないとか、絶対的に最善の方法といったものがあることはほとんどない
- 「最善」とは、顧客アプリケーションの中にある特定のクラスの目的に最も適したという意味
- ソフトウェアで絶対的なカテゴリを考えなければならないのは、汎用ライブラリを作る仕事に携わるときだけ
- たいてい、目的はもっと穏やかなものであり、求められるのは、良い 階層構造の設計
- すなわり、特定の種類の顧客ソフトウェアの必要を満たす階層構造
- クラス構造を構築する際の難しさは継承そのものにではなく、抽象を見つけ出すことにある
- 一般的な法則:
- 妥当な抽象を選んでいるならば、継承構造は自ずとできてくるもの
- 抽象を見つけ出すときのガイドとなるのは、抽象データ型理論
- 一般的な法則:
本文参照
省略
省略
省略