継承

常々思うのだが、継承はなくても構わないような気がする。もちろん、ここで言う継承は実装の継承のことだ。インターフェースのような仕様の継承は静的型宣言OOPLでは必須だろう。
何故、継承が不要か。単純に言えば委譲の方がもっと肌理細かく実装を分割出来るからだ。そうすればクラス間の依存性を減らすことも出来るだろう。
ただし、委譲にも欠点がある。表現力が高まった代わりに記述量が増すことと間接操作が一つ加わる分だけメモリ消費が増えたり関数呼び出しが遅くなったりする可能性があることだ。前者は言語仕様のレベルでサポートしたり、マクロを使って記述を平易にしたり、クラスを操作するライブラリを使ったり、などの必要がある。後者は充分な最適化を行えば解消できるはずであり、言語仕様の問題というよりは実装の問題だと考えられる。
例えば、C++Javaは継承を重視した言語設計になっている。例えば、protectedのような継承のためのアクセス修飾子があるが、これに対応した委譲のためのアクセス修飾子はない。*1そのため、委譲されるメソッドはpublicに公開しておいてドキュメントのレベルで制限するということになりがちだが、これはカプセル化に反しているように思える。良い解決策はないだろうか。

      • -

書き終わって思ったのだが、コンパイル時にクラスを操作するメタな手段があると良いと思う。C++のtemplateでもある程度は出来るが、あまり変態的な記述になってしまっては元も子もない。この路線を追求して行くと結局とても賢いプリプロセッサを通すべきだという結論に達するような気もする。lispのマクロはとても賢いプリプロセッサと言えそうだが、強力すぎて可読性を損なっているという評判があるし。

      • -

さらに追記。
継承にするか委譲にするかの選択でis_a関係が成り立つならば継承にすべきという話がある。それはそれで正しいと思うのだけど、私が主張したいのは「実装 is_a 型」が本質ではないかということだ。C++Javaではクラスが型と実装を兼ねているからis_aが成り立っているだけにすぎないのではないか。
C++で基底クラスBaseと派生クラスSubがあるとする。これを型と実装を完全に分離して考えると型BaseIFに対して基底実装BaseImplと派生実装SubImplがあるということになる。「BaseImpl is_a BaseIF」や「SubImpl is_a BaseIF」は成り立つけれども「SubImpl is_a BaseImpl」は成り立たない。これならば実装を再利用する際の手法が継承であっても委譲であっても構わないだろう。

*1:あまり気にせず書いたが、委譲のためのアクセス修飾子というアイディアは新言語を作るのならありかもしれない。具体的には委譲元と委譲先のインターフェースが共通のときだけ呼び出せるだとか(緩く)。または委譲元と委譲先で委譲だと明記しなくてはならないとか(厳しく)。